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

#include <Core/Analytic_Tests/ANALYTIC_PROGRAM_HELPER.h>
#include <Core/Auto_Diff/AUTO_DIFF.h>
#include <Core/Auto_Diff/AUTO_HESS.h>
#include <Core/Matrices/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_VECTOR
{
    typedef typename TV::SCALAR T;
    virtual ~ANALYTIC_VECTOR();
    virtual VECTOR<T,m> f(const TV& X,T t) const=0;
    virtual TV dt(const TV& X,T t) const=0;
    virtual MATRIX<T,m,TV::m> dX(const TV& X,T t) const=0;
    virtual SYMMETRIC_TENSOR<T,0,m,TV::m> ddX(const TV& X,T t) const=0;
    virtual VECTOR<T,m> L(const TV& X,T t) const;
    virtual void Test(const TV& X,T t) const;
};

template<class TV,int m,class G>
struct ANALYTIC_VECTOR_CUSTOM:public ANALYTIC_VECTOR<TV,m>
{
    G g;
    typedef typename TV::SCALAR T;
    ANALYTIC_VECTOR_CUSTOM(G g): g(g) {}
    virtual ~ANALYTIC_VECTOR_CUSTOM(){}
    virtual VECTOR<T,m> f(const TV& X,T t) const {return g(X,t);}
    virtual TV dt(const TV& X,T t) const
    {
        typedef VECTOR<T,1> V1;
        return AUTO_DIFF<TV,V1>(g(X,AUTO_DIFF<T,V1>::From_Var(V1(t),0))).dx.Column(0);
    }
    virtual MATRIX<T,m,TV::m> dX(const TV& X,T t) const
    {return AUTO_DIFF<TV,TV>(g(AUTO_DIFF<TV,TV>::From_Var(X),t)).dx;}
    virtual SYMMETRIC_TENSOR<T,0,m,TV::m> ddX(const TV& X,T t) const
    {return AUTO_HESS<TV,TV>(g(AUTO_HESS<TV,TV>::From_Var(X),t)).ddx;}
};

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

template<class TV,int m=TV::m>
struct ANALYTIC_VECTOR_PROGRAM:public ANALYTIC_VECTOR<TV,m>
{
    typedef typename TV::SCALAR T;
    ANALYTIC_PROGRAM_HELPER<T> h;
    
    ANALYTIC_VECTOR_PROGRAM(const std::string& str,const std::string& out_name);
    virtual ~ANALYTIC_VECTOR_PROGRAM();
    virtual VECTOR<T,m> f(const TV& X,T t) const;
    virtual TV dt(const TV& X,T t) const;
    virtual MATRIX<T,m,TV::m> dX(const TV& X,T t) const;
    virtual SYMMETRIC_TENSOR<T,0,m,TV::m> ddX(const TV& X,T t) const;
};
}
#endif
