#include "passmanager.h"

#include "gtest/gtest.h"

using namespace std;

TEST(Loop, SimpleLoop1){
    string code =
R"CODE(
    main = function:: int; entry {
        input  = [1..5]:: [int].

        loop fold(input->el::int, 0->sum)::int
        {
            sum + el
        }
    }

)CODE";

    xreate::PassManager* man = xreate::PassManager::prepareForCode(move(code));
    int (*funcMain)() = (int (*)()) man->run();

    int answerActual = funcMain();
    ASSERT_EQ(15, answerActual);
}

TEST(Loop, Break1){
    string code =
R"CODE(
    main = function:: int; entry {
        input  = [1..10]:: [int].

        loop fold(input->el::int, 0->sum)::int
        {
            if (sum>5)::int {
                sum:: int; break

            } else {sum+el}
        }
    }

)CODE";

    xreate::PassManager* man = xreate::PassManager::prepareForCode(move(code));
    int (*funcMain)() = (int (*)()) man->run();

    int answerActual = funcMain();
    ASSERT_EQ(6, answerActual);
}

TEST(Loop, NestedLoopsSimple1){
    string code =
R"CODE(
    main = function:: int; entry {
        listX  = [1..5]:: [int].
        loop fold(listX->x::int, 0->acc)::int
        {
            listY  = [1..5]:: [int].

            row = loop fold(listY->y::int, 1->acc):: int {
                acc * ( y + x)
            }.

            acc + row
        }
    }

)CODE";

    xreate::PassManager* man = xreate::PassManager::prepareForCode(move(code));
    int (*funcMain)() = (int (*)()) man->run();
    int answerActual = funcMain();

    ASSERT_EQ(55320, answerActual);
}

TEST(Loop, NestedLoopsBreak1){
    string code =
R"CODE(
    main = function:: int; entry {
        listX  = [1..5]:: [int].
        loop fold(listX->x::int, 0->acc)::int
        {
            listY  = [1..5]:: [int].
            row = loop fold(listY->y::int, 1->acc):: int {
                res = acc * ( y + x) :: int.

                if (res > 20):: int {
                    20:: int; break

                } else {
                    res
                }
            }.

            acc + row
        }
    }

)CODE";

    xreate::PassManager* man = xreate::PassManager::prepareForCode(move(code));
    int (*funcMain)() = (int (*)()) man->run();
    int answerActual = funcMain();

    ASSERT_EQ(100, answerActual);
}

TEST(Loop, NestedLoopsBreak2){
    string code =
R"CODE(
    main = function:: int; entry {
        listX  = [1..3]:: [int].
        loop fold(listX->x::int, 0->acc)::int
        {
            listY  = [1..5]:: [int].
            row = loop fold(listY->y::int, 1->acc):: int {
                res = acc * y :: int.

                if (res > 24):: int {
                    24:: int; break

                } else {
                    res
                }
            }.

            if (x==3)::int{
                acc:: int; break

            } else {
                acc + row
            }
        }
    }

)CODE";

    xreate::PassManager* man = xreate::PassManager::prepareForCode(move(code));
    int (*funcMain)() = (int (*)()) man->run();
    int answerActual = funcMain();

    ASSERT_EQ(48, answerActual);
}

//TEST nested loop breaks.
//TEST 2 breaks^ outer loop break, inner loop break

TEST(Loop, InfiniteLoop1){

    string code =
R"Code(
    fac = function(x:: int):: int{
        range = [2..x] :: [int].

        loop fold(range->i::int, 1->acc)::int {
            acc * i
        }
    }

    main = function:: int; entry {
        loop fold inf(2->state) :: int {
            if (fac(state)==120)::int {
                state::int; break

            } else {state + 1}
        }
    }

)Code" ;

    xreate::PassManager* man = xreate::PassManager::prepareForCode(move(code));
    int (*funcMain)() = (int (*)()) man->run();

    int answerActual = funcMain();
    ASSERT_EQ(5, answerActual);
}