#include <gtest/gtest.h>
#include <json.hpp>
//#define private public
#include "OptimizerEvolutionary.h"
#include <ModuleMath.h>
#include <ModuleSwitch.h>


using namespace mdd;
TEST(OptimizerEvolutionary, OptimizeSimpleFormula) {
    //optimize f(x)=a*b
    std::vector<std::string> inputs;
    std::shared_ptr<ModuleMath> f1 = std::make_shared<ModuleMath>(MathOperation::MULTIPLY);
    inputs = f1->getInputIDs();
    limits limit;
    limit.min = { -10 };
    limit.max = { 10 };
    limit.step = { 1 };

    f1->getInput(inputs[0])->setLimits() = limit;
    f1->getInput(inputs[1])->setLimits() = limit;

    f1->getInput(inputs[0])->setOptimizability(true);
    f1->getInput(inputs[1])->setOptimizability(true);

    OptimizerEvolutionary optimizer(f1, 3);
    optimizer.setEvaluation("out0");

    auto res = optimizer.update();
    //std::cout << res.dump() << std::endl;
    EXPECT_EQ(res[0][0]* res[1][0],-100);
}

TEST(OptimizerEvolutionary, OptimizeSimpleFormulaWithSimpleRestriction) {
    //optimize f(x)=a*b
    std::vector<std::string> inputs;
    std::shared_ptr<ModuleMath> f1 = std::make_shared<ModuleMath>(MathOperation::MULTIPLY);
    inputs = f1->getInputIDs();
    limits limit;
    limit.min = { -10 };
    limit.max = { 10 };
    limit.step = { 1 };
    limit.rule = "val[0] != -10";

    f1->getInput(inputs[0])->setLimits() = limit;
    f1->getInput(inputs[1])->setLimits() = limit;

    f1->getInput(inputs[0])->setOptimizability(true);
    f1->getInput(inputs[1])->setOptimizability(true);

    OptimizerEvolutionary optimizer(f1, 3);
    optimizer.setEvaluation("out0");

    auto res = optimizer.update();
    //std::cout << res.dump() << std::endl;
    EXPECT_EQ(res[0][0] * res[1][0], -90);
}

TEST(OptimizerEvolutionary, exprtk) {
    json a;
    a["value"] = { 1,2,3 };
    exprtk::symbol_table<double> symbol_table;
    auto v = a["value"].get<std::vector<double>>();
    //std::cout << v[0] << "|" << v[1] << "|" << v[2] << std::endl;
    symbol_table.add_vector("in" + std::to_string(0), v);
    symbol_table.add_constants();
    exprtk::expression<double> _func_expr;
    _func_expr.register_symbol_table(symbol_table);

    exprtk::parser<double> parser;
    //"in0[1]==in0[1]"=>1
    //"in0[1]!=in0[1]"=>0
    
    //std::cout << _func_expr.value() << std::endl;
    //std::cout << res.dump() << std::endl;
    parser.compile("in0[1]==in0[1]", _func_expr);
    EXPECT_EQ(_func_expr.value(), 1);
    parser.compile("in0[1]!=in0[1]", _func_expr);
    EXPECT_EQ(_func_expr.value(), 0);
}