//
// Created by pgess on 26/03/2020.
//

#include "xreatemanager.h"
#include "pass/compilepass.h"
#include "llvmlayer.h"
#include "supplemental/basics.h"

#include "gtest/gtest.h"

using namespace xreate;

struct Pair {intmax_t x; intmax_t y;};
typedef Pair (*FnPair)();

struct Tuple3 {intmax_t x; intmax_t y; intmax_t z; };
typedef Tuple3 (*FnTuple3)();

//struct Tuple4 {Pair x; Pair y;};
struct Tuple4 {intmax_t a; intmax_t b; intmax_t c; intmax_t d;};
typedef Tuple4 (*FnTuple4)();

TEST(Problems, MinMax1){
  FILE* code = fopen("scripts/containers/minmax.xreate", "r");
  assert(code != nullptr);

  auto man = details::tier1::XreateManager::prepare(code);
  LLVMLayer* llvm = man->llvm;
  man->analyse();

  std::unique_ptr<CompilePass> compiler(new compilation::CompilePassCustomDecorators<>(man));
  compiler->prepare();
  llvm::Function* fnMinMax1Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax1"))->compile();
  llvm::Function* fnMinMax2Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax2"))->compile();
  llvm::Function* fnMinMax3Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax3"))->compile();
  llvm::Function* fnMinMax4Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax4"))->compile();
  llvm::Function* fnMinMax5Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax5"))->compile();
  llvm::Function* fnMinMax6Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax6"))->compile();
  llvm::Function* fnMinMax7Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax7"))->compile();
  llvm::Function* fnMinMax8Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax8"))->compile();
  llvm::Function* fnMinMax9Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax9"))->compile();
  llvm::Function* fnMinMax10Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax10"))->compile();
  llvm::Function* fnMinMax11Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax11"))->compile();
  llvm::Function* fnMinMax12Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax12"))->compile();
  llvm::Function* fnMinMax13Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax13"))->compile();
  llvm::Function* fnMinMax14Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax14"))->compile();
  llvm::Function* fnMinMax15Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax15"))->compile();
  llvm::Function* fnMinMax19Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax19"))->compile();
  llvm::Function* fnMinMax20Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax20"))->compile();
  llvm::Function* fnMinMax21Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax21"))->compile();
  llvm::Function* fnMinMax22Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax22"))->compile();
  llvm::Function* fnMinMax23Raw = compiler->getBruteFn(man->root->findFunction("fn-minmax23"))->compile();
  llvm->print();

  llvm->initJit();
  {
    FnPair fnMinxMax1 = (FnPair) llvm->getFunctionPointer(fnMinMax1Raw);
    Pair resultMinMax1 = fnMinxMax1();
    ASSERT_EQ(3, resultMinMax1.x);
    ASSERT_EQ(37, resultMinMax1.y);
  }

  {
    FnNoArgs fnMinMax2 = (FnNoArgs) llvm->getFunctionPointer(fnMinMax2Raw);
    intmax_t resultMinMax2 = fnMinMax2();
    ASSERT_EQ(10, resultMinMax2);
  }

  {
    FnNoArgs fnMinMax3 = (FnNoArgs) llvm->getFunctionPointer(fnMinMax3Raw);
    intmax_t resultMinMax3 = fnMinMax3();
    ASSERT_EQ(146, resultMinMax3);
  }

  {
    FnNoArgs fnMinMax4 = (FnNoArgs) llvm->getFunctionPointer(fnMinMax4Raw);
    intmax_t resultMinMax4 = fnMinMax4();
    ASSERT_EQ(6, resultMinMax4);
  }

  {
    FnPair fnMinMax5 = (FnPair) llvm->getFunctionPointer(fnMinMax5Raw);
    Pair resultMinMax5 = fnMinMax5();
    ASSERT_EQ(4, resultMinMax5.x);
    ASSERT_EQ(11, resultMinMax5.y);
  }

  {
    FnPair fnMinMax6 = (FnPair) llvm->getFunctionPointer(fnMinMax6Raw);
    Pair resultMinMax6 = fnMinMax6();
    ASSERT_EQ(2, resultMinMax6.x);
    ASSERT_EQ(8, resultMinMax6.y);
  }

  {
    FnPair fnMinMax7 = (FnPair) llvm->getFunctionPointer(fnMinMax7Raw);
    Pair resultMinMax7 = fnMinMax7();
    ASSERT_EQ(1, resultMinMax7.x);
    ASSERT_EQ(3, resultMinMax7.y);
  }

  {
    FnPair fnMinMax8 = (FnPair) llvm->getFunctionPointer(fnMinMax8Raw);
    Pair resultMinMax8 = fnMinMax8();
    ASSERT_EQ(2, resultMinMax8.x);
    ASSERT_EQ(3, resultMinMax8.y);
  }

  {
    FnPair fnMinMax9 = (FnPair) llvm->getFunctionPointer(fnMinMax9Raw);
    Pair resultMinMax9 = fnMinMax9();
    ASSERT_EQ(1, resultMinMax9.x);
    ASSERT_EQ(8, resultMinMax9.y);
  }

  {
    FnNoArgs fnMinMax10 = (FnNoArgs ) llvm->getFunctionPointer(fnMinMax10Raw);
    intmax_t resultMinMax10 = fnMinMax10();
    ASSERT_EQ(1, resultMinMax10);
  }

  {
    FnNoArgs fnMinMax11 = (FnNoArgs ) llvm->getFunctionPointer(fnMinMax11Raw);
    intmax_t resultMinMax11 = fnMinMax11();
    ASSERT_EQ(8, resultMinMax11);
  }

  {
    FnNoArgs fnMinMax12 = (FnNoArgs ) llvm->getFunctionPointer(fnMinMax12Raw);
    intmax_t resultMinMax12 = fnMinMax12();
    ASSERT_EQ(2, resultMinMax12);
  }

  {
    FnNoArgs fnMinMax13 = (FnNoArgs ) llvm->getFunctionPointer(fnMinMax13Raw);
    intmax_t resultMinMax13 = fnMinMax13();
    ASSERT_EQ(3, resultMinMax13);
  }

  {
    FnPair fnMinMax14 = (FnPair) llvm->getFunctionPointer(fnMinMax14Raw);
    Pair resultMinMax14 = fnMinMax14();
    ASSERT_EQ(2, resultMinMax14.x);
    ASSERT_EQ(3, resultMinMax14.y);
  }

  {
    FnPair fnMinMax15 = (FnPair) llvm->getFunctionPointer(fnMinMax15Raw);
    Pair resultMinMax15 = fnMinMax15();
    ASSERT_EQ(1, resultMinMax15.x);
    ASSERT_EQ(16, resultMinMax15.y);
  }

  {
    FnNoArgs fnMinMax19 = (FnNoArgs) llvm->getFunctionPointer(fnMinMax19Raw);
    intmax_t resultMinMax19 = fnMinMax19();
    ASSERT_EQ(3, resultMinMax19);
  }

  {
    FnNoArgs fnMinMax20 = (FnNoArgs) llvm->getFunctionPointer(fnMinMax20Raw);
    intmax_t resultMinMax20 = fnMinMax20();
    ASSERT_EQ(4, resultMinMax20);
  }

  {
    FnNoArgs fnMinMax21 = (FnNoArgs) llvm->getFunctionPointer(fnMinMax21Raw);
    intmax_t resultMinMax21 = fnMinMax21();
    ASSERT_EQ(5, resultMinMax21);
  }

  {
    FnPair fnMinMax22 = (FnPair) llvm->getFunctionPointer(fnMinMax22Raw);
    Pair resultMinMax22 = fnMinMax22();
    ASSERT_EQ(-29, resultMinMax22.x);
    ASSERT_EQ(-14, resultMinMax22.y);
  }

  {
    FnTuple4 fnMinMax23 = (FnTuple4) llvm->getFunctionPointer(fnMinMax23Raw);
    Tuple4 resultMinMax23 = fnMinMax23();

    ASSERT_EQ(96, resultMinMax23.a);
    ASSERT_EQ(63, resultMinMax23.b);
    ASSERT_EQ(28, resultMinMax23.c);
  }
}