//#####################################################################
// Copyright 2022, Craig Schroeder.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
#include <Core/Symbolics/SYMBOLIC_NUMBER.h>
#include <Core/Data_Structures/HASHTABLE.h>
#include <Core/Log/LOG.h>
#include <Core/Math_Tools/cube.h>
#include <Core/Math_Tools/sqr.h>
#include <Core/Symbolics/SYMBOLIC.h>
#include <Core/Utilities/DEBUG_CAST.h>
#include <Core/Vectors/SCALAR_POLICY.h>
#include <cmath>
#include <complex>
#include <iosfwd>
#include <string>
namespace PhysBAM{

SYMBOLIC_NUMBER::SYMBOLIC_NUMBER(){s="0";}

void SYMBOLIC_NUMBER::Set(double x)
{
    if(floor(x)!=x && abs(x)<1000000 && floor(x*128)==x*128)
    {
        for(int d=2;d<=128;d+=d)
        {
            x+=x;
            if(floor(x)==x)
            {
                s=LOG::sprintf("(%P/%P)",(int)x,d);
                return;
            }
        }
    }
    if(x<0) s=LOG::sprintf("(%.16P)",x);
    else s=LOG::sprintf("%.16P",x);
}

void SYMBOLIC_NUMBER::Cache()
{
    static int next_id=0;
    static HASHTABLE<std::string,int> hash;
    auto pr=hash.Insert(s,next_id);
    if(pr.second)
    {
        next_id++;
        LOG::printf("X_%d:=%s:\n",*pr.first,s.c_str());
    }
    s=LOG::sprintf("X_%d",*pr.first);
}
    
SYMBOLIC_BASE* SYMBOLIC_NUMBER::Clone() const
{
    return new SYMBOLIC_NUMBER;
}

void SYMBOLIC_NUMBER::Print(double x,std::ostream& o) const
{
    o<<s;
}

void SYMBOLIC_NUMBER::Binary(double x,const SYMBOLIC& a,const SYMBOLIC& b,type_2 type)
{
    const auto& A=debug_cast<const SYMBOLIC_NUMBER*>(a.z)->s;
    const auto& B=debug_cast<const SYMBOLIC_NUMBER*>(b.z)->s;
    switch(type)
    {
        case type_2::add:
            if(A=="0") return Unary(x,b,type_1::copy);
            if(B=="0") return Unary(x,a,type_1::copy);
            s="("+A+"+"+B+")";
            break;
        case type_2::sub:
            if(A=="0") return Unary(x,b,type_1::neg);
            if(B=="0") return Unary(x,a,type_1::copy);
            s="("+A+"-"+B+")";
            break;
        case type_2::mul:
            if(B=="0") return Unary(x,b,type_1::copy);
            if(A=="0") return Unary(x,a,type_1::copy);
            if(B=="1") return Unary(x,a,type_1::copy);
            if(A=="1") return Unary(x,b,type_1::copy);
            s="("+A+"*"+B+")";
            break;
        case type_2::div:
            if(A=="0") return Unary(x,a,type_1::copy);
            if(B=="1") return Unary(x,a,type_1::copy);
            s="("+A+"/"+B+")";
            break;
        case type_2::hypot: s="hypot("+A+","+B+")"; break;
        case type_2::pow: s="pow("+A+","+B+")"; break;
        case type_2::atan2: s="atan2("+A+","+B+")"; break;
        case type_2::fmod: s="fmod("+A+","+B+")"; break;
        case type_2::cyl_neumann: s="cyl_neumann("+A+","+B+")"; break;
        case type_2::cyl_bessel_j: s="cyl_bessel_j("+A+","+B+")"; break;
    }
    Cache();
}

void SYMBOLIC_NUMBER::Unary(double x,const SYMBOLIC& a,type_1 type)
{
    const auto& A=debug_cast<const SYMBOLIC_NUMBER*>(a.z)->s;
    switch(type)
    {
        case type_1::copy: s=A; return;
        case type_1::pos: s=A; return;
        case type_1::neg: s="(-"+A+")"; break;
        case type_1::inv: s="(1/"+A+")"; break;
        case type_1::abs: s="abs("+A+")"; break;
        case type_1::sqrt: s="sqrt("+A+")"; break;
        case type_1::floor: s="floor("+A+")"; break;
        case type_1::ceil: s="ceil("+A+")"; break;
        case type_1::rint: s="rint("+A+")"; break;
        case type_1::round: s="round("+A+")"; break;
        case type_1::exp: s="exp("+A+")"; break;
        case type_1::log: s="log("+A+")"; break;
        case type_1::sin: s="sin("+A+")"; break;
        case type_1::cos: s="cos("+A+")"; break;
        case type_1::tan: s="tan("+A+")"; break;
        case type_1::asin: s="asin("+A+")"; break;
        case type_1::acos: s="acos("+A+")"; break;
        case type_1::atan: s="atan("+A+")"; break;
        case type_1::cbrt: s="cbrt("+A+")"; break;
        case type_1::expm1: s="expm1("+A+")"; break;
        case type_1::log1p: s="log1p("+A+")"; break;
        case type_1::sinh: s="sinh("+A+")"; break;
        case type_1::cosh: s="cosh("+A+")"; break;
        case type_1::tanh: s="tanh("+A+")"; break;
        case type_1::asinh: s="asinh("+A+")"; break;
        case type_1::acosh: s="acosh("+A+")"; break;
        case type_1::atanh: s="atanh("+A+")"; break;
        case type_1::modf_ret: s="modf_ret("+A+")"; break;
        case type_1::modf_arg: s="modf_arg("+A+")"; break;
    }
    Cache();
}
}
