/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 * 
 * loops.cpp
 *
 *  Created on: -
 *      Author: pgess <v.melnychenko@xreate.org>
 */

#include "xreatemanager.h"

#include "gtest/gtest.h"

using namespace std;

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

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

)CODE";

    xreate::XreateManager* man = xreate::XreateManager::prepare(move(code));
    int (*funcMain)() = (int (*)()) man->run();

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

TEST(Loop, LoopFoldFinal){
    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; final

            } else {sum+el}
        }
    }

)CODE";

    xreate::XreateManager* man = xreate::XreateManager::prepare(move(code));
    int (*funcMain)() = (int (*)()) man->run();

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

TEST(Loop, NestedLoops1){
    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::XreateManager* man = xreate::XreateManager::prepare(move(code));
    int (*funcMain)() = (int (*)()) man->run();
    int answerActual = funcMain();

    ASSERT_EQ(55320, answerActual);
}

TEST(Loop, NestedLoopsFinal1){
    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; final

                } else {
                    res
                }
            }.

            acc + row
        }
    }

)CODE";

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

    ASSERT_EQ(100, answerActual);
}

TEST(Loop, NestedLoopsFinal2){
    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; final

                } else {
                    res
                }
            }.

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

            } else {
                acc + row
            }
        }
    }

)CODE";

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

    ASSERT_EQ(48, answerActual);
}

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 (2->state) :: int {
            if (fac(state)==120)::int {
                state::int; final

            } else {state + 1}
        }
    }

)Code" ;

    xreate::XreateManager* man = xreate::XreateManager::prepare(move(code));
    int (*funcMain)() = (int (*)()) man->run();

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

TEST(Loop, Doc_LoopInfinite){
    string code =
R"Code(
    //infinite loop
    answer = loop (2->x) :: int
    {
      if(IsPerfect(x)):: int {x} else {x+1}
    }.
)Code" ;

    string code2 =
R"Code(
    //loop exits after first perfect number is found
    answer2 = loop (2->x) :: int
    {
      if(IsPerfect(x))::int {x:: int; final} else {x+1}
    }.
)Code" ;

    string codeWrapper =
R"Code(
    IsPerfect = function(x::int):: bool {x==6}
    test = function::int; entry
    {
        %TEST%
        %TEST2%

        answer + answer2
    }
)Code" ;

    codeWrapper.replace(codeWrapper.find("%TEST%"), 6, code);
    codeWrapper.replace(codeWrapper.find("%TEST2%"), 7, code2);
    xreate::XreateManager* man = xreate::XreateManager::prepare(string(codeWrapper));
    ASSERT_TRUE(man);
}

TEST(Loop, Doc_LoopLoopFold){
    string code =
R"CODE(
    numbers = {4, 8, 7, 1, 5}::                  [int].
    min = loop fold(numbers->x:: int, 10->acc):: int
    {
      if (acc > x):: int {x} else {acc}
    }.
)CODE";

    string codeWrapper =
R"CODE(
    main = function:: int; entry
    {
        %TEST%
        min
    }
)CODE";

    codeWrapper.replace(codeWrapper.find("%TEST%"), 6, code);
    xreate::XreateManager* man = xreate::XreateManager::prepare(move(codeWrapper));
    int (*funcMain)() = (int (*)()) man->run();

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

TEST(Loop, Doc_LoopLoopMap){
    string code =
R"CODE(
    numbers = {4, 8, 7, 1, 5}::                  [int].
    min = loop fold(numbers->x:: int, 10->acc):: int
    {
      if (acc > x):: int {x} else {acc}
    }.
)CODE";

    string codeWrapper =
R"CODE(
    main = function:: int; entry
    {
        odd_numbers = {1, 3, 5}:: [int].
        even_numbers = loop map(odd_numbers->number::int) :: [int]
            { 2 * number }.

        even_numbers[0]
    }
)CODE";

    //codeWrapper.replace(codeWrapper.find("%TEST%"), 6, code);
    xreate::XreateManager* man = xreate::XreateManager::prepare(move(codeWrapper));
    ASSERT_TRUE(man);
}
