//#####################################################################
// Copyright 2013.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
#ifndef __ANALYTIC_SYMMETRIC_MATRIX__
#define __ANALYTIC_SYMMETRIC_MATRIX__

#include <Core/Analytic_Tests/ANALYTIC_PROGRAM_HELPER.h>
#include <Core/Auto_Diff/AUTO_DIFF.h>
#include <Core/Matrices/SYMMETRIC_MATRIX.h>
#include <Core/Symbolics/PROGRAM.h>
#include <Core/Symbolics/PROGRAM_CONTEXT.h>
#include <Core/Tensors/SYMMETRIC_TENSOR.h>

namespace PhysBAM{

template<class TV,int m=TV::m>
struct ANALYTIC_SYMMETRIC_MATRIX
{
    typedef typename TV::SCALAR T;
    typedef SYMMETRIC_MATRIX<T,m> T_MATRIX;
    typedef SYMMETRIC_TENSOR<T,2,TV::m,m> T_TENSOR;
    virtual ~ANALYTIC_SYMMETRIC_MATRIX();
    virtual T_MATRIX f(const TV& X,T t) const=0;
    virtual T_TENSOR dX(const TV& X,T t) const=0;
    virtual T_MATRIX dt(const TV& X,T t) const=0;
    virtual void Test(const TV& X,T t) const;
};

template<class TV,int m,class G>
struct ANALYTIC_SYMMETRIC_MATRIX_CUSTOM:public ANALYTIC_SYMMETRIC_MATRIX<TV,m>
{
    typedef typename TV::SCALAR T;
    typedef SYMMETRIC_MATRIX<T,m> T_MATRIX;
    typedef SYMMETRIC_TENSOR<T,2,TV::m,m> T_TENSOR;
    G g;
    ANALYTIC_SYMMETRIC_MATRIX_CUSTOM(G g): g(g) {}
    virtual ~ANALYTIC_SYMMETRIC_MATRIX_CUSTOM(){}
    virtual T_MATRIX f(const TV& X,T t) const
    {
        T_MATRIX M;
        Fill_From(M,g(X,t));
        return M;
    }
    virtual T_MATRIX dt(const TV& X,T t) const
    {
        typedef VECTOR<T,1> V1;
        return AUTO_DIFF<T_MATRIX,V1>(g(X,AUTO_DIFF<T,V1>::From_Var(V1(t),0))).dx.x.x;
    }
    virtual T_TENSOR dX(const TV& X,T t) const
    {return AUTO_DIFF<T_MATRIX,TV>(g(AUTO_DIFF<TV,TV>::From_Var(X),t)).dx;}
};

template<class TV,int m,class G>
ANALYTIC_SYMMETRIC_MATRIX_CUSTOM<TV,m,G>* Make_Analytic_Symmetric_Matrix(G g)
{
    return new ANALYTIC_SYMMETRIC_MATRIX_CUSTOM<TV,m,G>(g);
}

template<class TV,int m=TV::m>
struct ANALYTIC_SYMMETRIC_MATRIX_PROGRAM:public ANALYTIC_SYMMETRIC_MATRIX<TV,m>
{
    typedef typename TV::SCALAR T;
    typedef SYMMETRIC_MATRIX<T,m> T_MATRIX;
    typedef SYMMETRIC_TENSOR<T,2,TV::m,m> T_TENSOR;
    ANALYTIC_PROGRAM_HELPER<T> h;

    ANALYTIC_SYMMETRIC_MATRIX_PROGRAM(const std::string& str,const std::string& out_name);
    virtual ~ANALYTIC_SYMMETRIC_MATRIX_PROGRAM();
    virtual T_MATRIX f(const TV& X,T t) const;
    virtual T_MATRIX dt(const TV& X,T t) const;
    virtual T_TENSOR dX(const TV& X,T t) const;
};
}
#endif
