//#####################################################################
// 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_SCALAR__
#define __ANALYTIC_SCALAR__

#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/SYMMETRIC_MATRIX.h>
#include <Core/Symbolics/PROGRAM.h>
#include <Core/Symbolics/PROGRAM_CONTEXT.h>
#include <Core/Vectors/VECTOR.h>

namespace PhysBAM{

template<class TV>
struct ANALYTIC_SCALAR
{
    typedef typename TV::SCALAR T;
    virtual ~ANALYTIC_SCALAR();
    virtual T f(const TV& X,T t) const=0;
    virtual TV dX(const TV& X,T t) const=0;
    virtual SYMMETRIC_MATRIX<T,TV::m> ddX(const TV& X,T t) const=0;
    virtual T L(const TV& X,T t) const;
    virtual T dt(const TV& X,T t) const=0;
    virtual void Test(const TV& X,T t) const;
};

template<class TV,class G>
struct ANALYTIC_SCALAR_CUSTOM:public ANALYTIC_SCALAR<TV>
{
    G g;
    typedef typename TV::SCALAR T;
    ANALYTIC_SCALAR_CUSTOM(G g): g(g) {}
    virtual ~ANALYTIC_SCALAR_CUSTOM(){}
    virtual T f(const TV& X,T t) const {return g(X,t);}
    virtual TV dX(const TV& X,T t) const
    {return AUTO_DIFF<T,TV>(g(AUTO_DIFF<TV,TV>::From_Var(X),t)).dx;}
    virtual SYMMETRIC_MATRIX<T,TV::m> ddX(const TV& X,T t) const
    {return AUTO_HESS<T,TV>(g(AUTO_HESS<TV,TV>::From_Var(X),t)).ddx;}
    virtual T dt(const TV& X,T t) const
    {
        typedef VECTOR<T,1> V1;typedef AUTO_DIFF<T,V1> AD;
        return AD(g(X,AD::From_Var(V1(t),0))).dx.x;
    }
};

template<class TV,class G>
ANALYTIC_SCALAR_CUSTOM<TV,G>* Make_Analytic_Scalar(G g)
{
    return new ANALYTIC_SCALAR_CUSTOM<TV,G>(g);
}

template<class TV>
struct ANALYTIC_SCALAR_PROGRAM:public ANALYTIC_SCALAR<TV>
{
    typedef typename TV::SCALAR T;
    ANALYTIC_PROGRAM_HELPER<T> h;

    ANALYTIC_SCALAR_PROGRAM(const std::string& str,const std::string& out_name);
    virtual ~ANALYTIC_SCALAR_PROGRAM();
    virtual T f(const TV& X,T t) const;
    virtual TV dX(const TV& X,T t) const;
    virtual SYMMETRIC_MATRIX<T,TV::m> ddX(const TV& X,T t) const;
    virtual T dt(const TV& X,T t) const;
};
}
#endif
