//#####################################################################
// Copyright 2022.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYREGHT.txt.
//#####################################################################

#include <Core/Symbolics/EXECUTABLE_PROGRAM.h>
#include <Core/Data_Structures/HASHTABLE.h>
#include <Core/Matrices/MATRIX.h>
#include <Core/Symbolics/CODE_BLOCK.h>
#include <Core/Symbolics/INSTRUCTION.h>
#include <Core/Symbolics/PROGRAM.h>
#include <Core/Symbolics/PROGRAM_COMPILER.h>
#include <Core/Symbolics/PROGRAM_CONTEXT.h>
#include <Core/Symbolics/PROGRAM_PARSE_NODE.h>
#include <Core/Symbolics/PROGRAM_SCALAR.h>
#include <Core/Vectors/VECTOR.h>
#include <map>
#include <string>
namespace PhysBAM{
//#####################################################################
// Constructor
//#####################################################################
template<class U> EXECUTABLE_PROGRAM<U>::
EXECUTABLE_PROGRAM()
    :prog(*new PROGRAM<T>),context(*new PROGRAM_CONTEXT<T>)
{
}
template<class U> EXECUTABLE_PROGRAM<U>::
EXECUTABLE_PROGRAM(const T_PROGRAM_SCALAR& ps)
    :prog(*new PROGRAM<T>),context(*new PROGRAM_CONTEXT<T>)
{
    Set(ps);
}
//#####################################################################
// Destructor
//#####################################################################
template<class U> EXECUTABLE_PROGRAM<U>::
~EXECUTABLE_PROGRAM()
{
    delete &prog;
    delete &context;
}
//#####################################################################
// Function Eval
//#####################################################################
template<class U> U EXECUTABLE_PROGRAM<U>::
Eval(const ARRAY<T,MEM_IN>& in)
{
    context.data_in=in;
    prog.Execute(context);
    if constexpr(is_same<T,U>::value)
        return context.data_out({});
    else if constexpr(is_same<VECTOR<T,U::m>,U>::value)
    {
        U u;
        for(int i=0;i<U::m;i++) u(i)=context.data_out(MEM_OUT(i));
        return u;
    }
    else if constexpr(is_same<MATRIX<T,U::m,U::n>,U>::value)
    {
        U u;
        for(int j=0;j<U::n;j++)
            for(int i=0;i<U::m;i++)
                u(i,j)=context.data_out(MEM_OUT(i+j*U::m));
        return u;
    }
    else PHYSBAM_FATAL_ERROR();
}
//#####################################################################
// Function Set
//#####################################################################
template<class U> void EXECUTABLE_PROGRAM<U>::
Set(const T_PROGRAM_SCALAR& ps)
{
    ARRAY_VIEW<const PROGRAM_SCALAR<T>> av;
    if constexpr(is_same<U,T>::value) av.Set(&ps,1);
    else if constexpr(is_same<VECTOR<T,U::m>,U>::value) av.Set(ps);
    else if constexpr(is_same<MATRIX<T,U::m,U::n>,U>::value) av.Set(&ps(0,0),U::m*U::n);
    else PHYSBAM_FATAL_ERROR();

    auto pc=*av(0).pc; // Note: this is intended to be a copy
    auto [first_out,num]=pc.Move_Outputs_To_Reg();
    for(int i=0;i<av.m;i++)
    {
        REG var=av(i).var;
        if(var.Is_Out())
            var=first_out+Value(var.Out());
        pc.Make_Output(var);
    }
    pc.Optimize();
    pc.Finalize(prog);
    context.Initialize(prog);
}
//#####################################################################
// Function Print
//#####################################################################
template<class U> void EXECUTABLE_PROGRAM<U>::
Print() const
{
    prog.Print();
}
template class EXECUTABLE_PROGRAM<float>;
template class EXECUTABLE_PROGRAM<double>;
template class EXECUTABLE_PROGRAM<VECTOR<float,1>>;
template class EXECUTABLE_PROGRAM<VECTOR<double,1>>;
template class EXECUTABLE_PROGRAM<VECTOR<float,2>>;
template class EXECUTABLE_PROGRAM<VECTOR<double,2>>;
template class EXECUTABLE_PROGRAM<VECTOR<float,3>>;
template class EXECUTABLE_PROGRAM<VECTOR<double,3>>;
template class EXECUTABLE_PROGRAM<MATRIX<float,1>>;
template class EXECUTABLE_PROGRAM<MATRIX<double,1>>;
template class EXECUTABLE_PROGRAM<MATRIX<float,2>>;
template class EXECUTABLE_PROGRAM<MATRIX<double,2>>;
template class EXECUTABLE_PROGRAM<MATRIX<float,3>>;
template class EXECUTABLE_PROGRAM<MATRIX<double,3>>;
}
