#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>

#include <string>
#include <fstream>
#include <iostream>
#include <algorithm>

#include <vector>
#include <map>
#include <set>
#include <utility>
#include <assert.h>

#include "../DirectiveProcessing/directive_creator_nodist.h"

#include "loop_analyzer_internal.h"
#include "loop_analyzer_nodist.h"

using std::vector;
using std::pair;
using std::tuple;
using std::map;
using std::set;
using std::make_pair;
using std::make_tuple;
using std::get;
using std::string;
using std::wstring;
using std::stack;

extern void createMapLoopGraph(map<int, LoopGraph*>& sortedLoopGraph, const vector<LoopGraph*>* loopGraph);

extern map<DIST::Array*, std::tuple<int, string, string>> tableOfUniqNamesByArray;
static void convertOneLoopNoDist(LoopGraph* currLoop, map<LoopGraph*, map<DIST::Array*, ArrayInfo*>>& outInfo,
                                 const map<SgSymbol*, ArrayInfo>& toConvert,
                                 const map<string, vector<SgExpression*>>& commonBlocks,
                                 const map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>>& declaredArrays,
                                 const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls,
                                 map<tuple<int, string, string>, DIST::Array*>& createdArrays,
                                 bool freeArrays = false)
{
    map<DIST::Array*, ArrayInfo*> toAdd;
    for (auto& conv : toConvert)
    {
        SgSymbol* currentArray = OriginalSymbol(conv.first);
        ArrayInfo* currentInfo = (ArrayInfo*)(&conv.second);

        DIST::Array* arrayToAdd;

        SgStatement* decl = declaratedInStmt(currentArray);
        const char* symbIdent = currentArray->identifier();

        const tuple<int, string, string> uniqKey = getUniqName(commonBlocks, decl, currentArray);

        auto itFound = createdArrays.find(uniqKey);
        if (itFound == createdArrays.end())
        {
            auto itArray = declaredArrays.find(uniqKey);
            if (itArray == declaredArrays.end())
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            arrayToAdd = itArray->second.first;
            itFound = createdArrays.insert(itFound, make_pair(uniqKey, arrayToAdd));
        }
        else
            arrayToAdd = itFound->second;

        set<DIST::Array*> links;
        getRealArrayRefs(arrayToAdd, arrayToAdd, links, arrayLinksByFuncCalls);

        int countOflinks = 0;
        for (auto& linkedArray : links)
        {
            if (arrayToAdd == linkedArray)
                continue;

            ++countOflinks;
            auto key = tableOfUniqNamesByArray[linkedArray];
            auto value = declaredArrays.find(key)->second;
            if (value.second == 0 && createdArrays.find(key) == createdArrays.end())
                createdArrays.insert(make_pair(key, linkedArray));
        }

        if (freeArrays)
            if (countOflinks == 0)
                continue;

        toAdd[arrayToAdd] = currentInfo;

        for (int z = 0; z < currentInfo->getDimSize(); ++z)
        {
            if (currentInfo->readOps[z].coefficients.size() || currentInfo->writeOps[z].coefficients.size())
            {
                arrayToAdd->SetMappedDim(z);

                for (auto& realRef : links)
                    realRef->SetMappedDim(z);
            }
        }
    }

    outInfo[currLoop] = toAdd;
}

static map<LoopGraph*, map<DIST::Array*, ArrayInfo*>>
convertLoopInfoNoDist(const map<SgForStmt*, map<SgSymbol*, ArrayInfo>>& loopInfo,
                      const map<int, LoopGraph*>& sortedLoopGraph,
                      const map<string, vector<SgExpression*>>& commonBlocks,
                      const map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>>& declaredArrays,
                      const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls,
                      map<tuple<int, string, string>, DIST::Array*>& createdArrays)
{
    map<LoopGraph*, map<DIST::Array*, ArrayInfo*>> outInfo;

    for (auto it = loopInfo.begin(); it != loopInfo.end(); ++it)
    {
        auto itGraph = sortedLoopGraph.find(it->first->lineNumber());
        if (itGraph == sortedLoopGraph.end())
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        convertOneLoopNoDist(itGraph->second, outInfo, it->second, commonBlocks, declaredArrays, arrayLinksByFuncCalls, createdArrays);
    }

    return outInfo;
}

static vector<int> matchArrayToLoopSymbols(const vector<SgForStmt*> &parentLoops, vector<set<string>>& privatesVarsForLoop,
                                           SgExpression *currExp, const int side,
                                           map<SgForStmt*, map<SgSymbol*, ArrayInfo>> &loopInfo, const int currLine,
                                           map<int, LoopGraph*> &sortedLoopGraph, const ParallelRegion *reg, const double currentW,
                                           const map<DIST::Array*, set<DIST::Array*>> &arrayLinksByFuncCalls)
{
    SgArrayRefExp *arrayRef = (SgArrayRefExp*)currExp;
    int numOfSubs = arrayRef->numberOfSubscripts();

    currExp = currExp->lhs();    
    vector<int> wasFoundForLoop(parentLoops.size());
    vector<int> matched(numOfSubs);
    vector<int> matchedToDim(parentLoops.size());
    std::fill(wasFoundForLoop.begin(), wasFoundForLoop.end(), 0);
    std::fill(matched.begin(), matched.end(), -1);
    std::fill(matchedToDim.begin(), matchedToDim.end(), -1);
    int maxMatched = 0;
    int sumMatched = 0;

    for (int i = 0; i < numOfSubs; ++i)
    {
        vector<int> matchToLoops = matchSubscriptToLoopSymbols(parentLoops, currExp->lhs(), arrayRef, side, i, loopInfo, currLine, numOfSubs, currentW);
        for (int k = 0; k < matchToLoops.size(); ++k)
        {
            wasFoundForLoop[matchToLoops[k]]++;
            matchedToDim[matchToLoops[k]] = i;
        }

        matched[i] = matchToLoops.size();
        sumMatched += matchToLoops.size();
        maxMatched = std::max(maxMatched, (int)matchToLoops.size());
        currExp = currExp->rhs();
    }

    //full array is used, add unknown operations to all loops
    if (numOfSubs == 0)
    {
        SgSymbol *currOrigArrayS = OriginalSymbol(arrayRef->symbol());
        auto arrType = isSgArrayType(currOrigArrayS->type());
        if (arrType != NULL)
        {
            for (int d = 0; d < arrType->dimension(); ++d)
                for (int i = 0; i < parentLoops.size(); ++i)
                    addInfoToVectors(loopInfo, parentLoops[i], currOrigArrayS, d, make_pair(0, 0), UNREC_OP, arrType->dimension(), currentW);
        }
    }

    bool ifUnknownArrayAssignFound = false;
    vector<int> canNotMapToLoop;
    for (int i = 0; i < wasFoundForLoop.size(); ++i)
    {
        if (wasFoundForLoop[i] != 1 && 
            privatesVarsForLoop[i].find(string(arrayRef->symbol()->identifier())) == privatesVarsForLoop[i].end())
        {
            auto itLoop = sortedLoopGraph.find(parentLoops[i]->lineNumber());
            if (itLoop == sortedLoopGraph.end())
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            ifUnknownArrayAssignFound = true;
            if (side == LEFT)
                itLoop->second->hasUnknownArrayAssigns = true;

            itLoop->second->hasUnknownDistributedMap = true;
            canNotMapToLoop.push_back(parentLoops[i]->lineNumber());
        }
    }

    if (side == LEFT)
    {
        if (ifUnknownArrayAssignFound)
        {
            const string arrayRefS = arrayRef->unparse();
            for (auto &line : canNotMapToLoop)
            {
                __spf_print(1, "WARN: can not map write to array '%s' to loop on line %d\n", arrayRefS.c_str(), line);
                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"can not map write to array '%s' to this loop", to_wstring(arrayRefS).c_str());

                __spf_printToLongBuf(messageR, R59, to_wstring(arrayRefS).c_str());

                if (line > 0)
                    currMessages->push_back(Messages(WARR, line, messageR, messageE, 1025));
            }
        }
    }

    return wasFoundForLoop;
}

static void mapArrayRef(SgStatement* currentSt, SgExpression* currExp,
                        const vector<SgForStmt*>& parentLoops, const int side, const int lineNum,
                        map<SgForStmt*, map<SgSymbol*, ArrayInfo>>& loopInfo,
                        vector<set<string>>& privatesVarsForLoop,
                        map<int, LoopGraph*>& sortedLoopGraph, map<string, pair<SgSymbol*, SgStatement*>>& notMappedDistributedArrays,
                        set<string>& mappedDistrbutedArrays,
                        const ParallelRegion* reg, const double currentW, const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls)
{
    const char* printSide = NULL;
    if (PRINT_ARRAY_ARCS)
        printBlanks(2, (int)parentLoops.size());
    if (side == LEFT)
        printSide = "W_OP";
    else
        printSide = "R_OP";

    __spf_print(PRINT_ARRAY_ARCS, "%s to array <%s> on line %d: ", printSide, OriginalSymbol(currExp->symbol())->identifier(), lineNum);
    bool wasMapped = false;
    vector<int> matched = matchArrayToLoopSymbols(parentLoops, privatesVarsForLoop, currExp, side, loopInfo, lineNum, sortedLoopGraph, reg, currentW, arrayLinksByFuncCalls);
    for (int z = 0; z < matched.size(); ++z)
        wasMapped |= (matched[z] != 0);

    if (parentLoops.size() == 0)
    {
        SgSymbol* symb = currExp->symbol();
        if (symb->type()->variant() == T_ARRAY)
            notMappedDistributedArrays[symb->identifier()] = make_pair(symb, currentSt);
    }
    else
    {
        if (wasMapped)
            mappedDistrbutedArrays.insert(currExp->symbol()->identifier());
        else
        {
            SgSymbol* symb = currExp->symbol();
            if (symb->type()->variant() == T_ARRAY)
                notMappedDistributedArrays[symb->identifier()] = make_pair(symb, currentSt);
        }
    }
    __spf_print(PRINT_ARRAY_ARCS, "\n");
}

static void findArrayRef(const vector<SgForStmt*>& parentLoops, SgExpression* currExp, const int lineNum, const int side,
                         map<SgForStmt*, map<SgSymbol*, ArrayInfo>>& loopInfo,
                         vector<set<string>>& privatesVarsForLoop, map<int, LoopGraph*>& sortedLoopGraph,
                         map<string, pair<SgSymbol*, SgStatement*>>& notMappedDistributedArrays,
                         set<string>& mappedDistrbutedArrays, SgStatement* currentSt, const ParallelRegion* reg, const double currentW,
                         const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls)
{
    int nextSide = side;
    if (isArrayRef(currExp))
    {
        mapArrayRef(currentSt, currExp, parentLoops, side, lineNum, loopInfo, privatesVarsForLoop, sortedLoopGraph,
                    notMappedDistributedArrays, mappedDistrbutedArrays, reg, currentW, arrayLinksByFuncCalls);
        nextSide = (side == LEFT) ? RIGHT : side;
    }

    bool needToContinue = true;
    if (currExp->variant() == FUNC_CALL)
    {
        SgFunctionCallExp* funcExp = (SgFunctionCallExp*)currExp;
        auto currFunc = isUserFunctionInProject(funcExp->funName()->identifier());
        if (currFunc)
        {
            for (int z = 0; z < funcExp->numberOfArgs(); ++z)
            {
                if ((currFunc->funcParams.inout_types[z] & OUT_BIT) != 0)
                    nextSide = LEFT;
                else
                    nextSide = RIGHT;
                findArrayRef(parentLoops, funcExp->arg(z), lineNum, nextSide, loopInfo, privatesVarsForLoop, sortedLoopGraph,
                             notMappedDistributedArrays, mappedDistrbutedArrays, currentSt, reg, currentW, arrayLinksByFuncCalls);
            }
            needToContinue = false;
        }
    }

    if (needToContinue)
    {
        if (currExp->lhs())
            findArrayRef(parentLoops, currExp->lhs(), lineNum, nextSide, loopInfo, privatesVarsForLoop, sortedLoopGraph,
                         notMappedDistributedArrays, mappedDistrbutedArrays, currentSt, reg, currentW, arrayLinksByFuncCalls);
        if (currExp->rhs())
            findArrayRef(parentLoops, currExp->rhs(), lineNum, nextSide, loopInfo, privatesVarsForLoop, sortedLoopGraph,
                         notMappedDistributedArrays, mappedDistrbutedArrays, currentSt, reg, currentW, arrayLinksByFuncCalls);
    }
}

void loopAnalyzerNoDist(SgFile* file, vector<ParallelRegion*>& regions, map<tuple<int, string, string>, DIST::Array*>& createdArrays,
                        vector<Messages>& messagesForFile, const map<string, vector<FuncInfo*>>& AllfuncInfo,
                        const map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>>& declaredArrays,
                        const map<SgStatement*, set<tuple<int, string, string>>>& declaratedArraysSt,
                        const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls,
                        const map<SgStatement*, vector<DefUseList>>& defUseByPlace,
                        vector<LoopGraph*>* loopGraph)
{
    currMessages = &messagesForFile;
    currRegime = DATA_DISTR; //?

    map<string, vector<SgExpression*>> commonBlocks;
    map<int, LoopGraph*> sortedLoopGraph;
    map<int, pair<SgForStmt*, pair<set<string>, set<string>>>> allLoops;

    createMapLoopGraph(sortedLoopGraph, loopGraph);

    int funcNum = file->numberOfFunctions();
    __spf_print(PRINT_PROF_INFO, "functions num in file = %d\n", funcNum);

    vector<SgStatement*> modules;
    findModulesInFile(file, modules);

    map<string, SgStatement*> modulesByName;
    for (int i = 0; i < modules.size(); ++i)
        modulesByName[modules[i]->symbol()->identifier()] = modules[i];

    map<string, FuncInfo*> funcByName;
    createMapOfFunc(AllfuncInfo, funcByName);

    const vector<FuncInfo*>& funcInfo = AllfuncInfo.find(file->filename())->second;

    for (int i = 0; i < funcNum; ++i)
    {
        createNeededException();

        string fName = file->functions(i)->symbol()->identifier();
#if _WIN32
        if (file->functions(i)->variant() != MODULE_STMT)
            sendMessage_2lvl(wstring(L"обработка функции '") + wstring(fName.begin(), fName.end()) + L"'");
        else
            sendMessage_2lvl(wstring(L"обработка модуля '") + wstring(fName.begin(), fName.end()) + L"'");
#else
        if (file->functions(i)->variant() != MODULE_STMT)
            sendMessage_2lvl(wstring(L"processing function '") + wstring(fName.begin(), fName.end()) + L"'");
        else
            sendMessage_2lvl(wstring(L"processing module '") + wstring(fName.begin(), fName.end()) + L"'");
#endif

        set<SgSymbol*> delcsSymbViewed;
        set<SgStatement*> delcsStatViewed;

        if (funcInfo[i]->doNotAnalyze)
            continue;

        map<SgForStmt*, map<SgSymbol*, ArrayInfo>> loopInfo;
        set<int> loopWithOutArrays;

        SgStatement* st = file->functions(i);
        string funcName = "";
        if (st->variant() == PROG_HEDR)
        {
            SgProgHedrStmt* progH = (SgProgHedrStmt*)st;
            __spf_print(PRINT_PROF_INFO, "*** Program <%s> started at line %d / %s\n", progH->symbol()->identifier(), st->lineNumber(), st->fileName());
            funcName = progH->symbol()->identifier();
        }
        else if (st->variant() == PROC_HEDR)
        {
            SgProcHedrStmt* procH = (SgProcHedrStmt*)st;
            __spf_print(PRINT_PROF_INFO, "*** Function <%s> started at line %d / %s\n", procH->symbol()->identifier(), st->lineNumber(), st->fileName());
            funcName = procH->symbol()->identifier();
        }
        else if (st->variant() == FUNC_HEDR)
        {
            SgFuncHedrStmt* funcH = (SgFuncHedrStmt*)st;
            __spf_print(PRINT_PROF_INFO, "*** Function <%s> started at line %d / %s\n", funcH->symbol()->identifier(), st->lineNumber(), st->fileName());
            funcName = funcH->symbol()->identifier();
        }

        vector<LoopGraph*> loopsForFunction;
        for (auto& loop : *loopGraph)
        {
            auto fStat = getFuncStat(loop->loop->GetOriginal());
            if (fStat->symbol()->identifier() == funcName)
                loopsForFunction.push_back(loop);
        }

        commonBlocks.clear();
        getCommonBlocksRef(commonBlocks, st, st->lastNodeOfStmt());
        __spf_print(PRINT_PROF_INFO, "  number of common blocks %d\n", (int)commonBlocks.size());

        SgStatement* lastNode = st->lastNodeOfStmt();
        vector<SgForStmt*> parentLoops;
        vector<set<string>> privatesVarsForLoop;

        //For remote access
        pair<SgForStmt*, LoopGraph*>* under_dvm_dir = NULL;

        map<string, pair<SgSymbol*, SgStatement*>> notMappedDistributedArrays;
        set<string> mappedDistrbutedArrays;

        double currentWeight = 1.0;
        while (st != lastNode)
        {
            createNeededException();

            if (st == NULL)
            {
                currMessages->push_back(Messages(ERROR, 1, R128, L"internal error in analysis, parallel directives will not be generated for this file!", 3008));

                __spf_print(1, "internal error in analysis, parallel directives will not be generated for this file!\n");
                break;
            }

            if (st->variant() == CONTAINS_STMT)
                break;

            if (!__gcov_doesThisLineExecuted(st->fileName(), st->lineNumber()))
            {
                st = st->lexNext();
                continue;
            }

            const int currentLine = st->lineNumber() < -1 ? st->localLineNumber() : st->lineNumber();
            ParallelRegion* currReg = getRegionByLine(regions, st->fileName(), currentLine);
            if (currReg == NULL)
            {
                st = st->lexNext();
                continue;
            }

            if (isSgExecutableStatement(st) == NULL)
                delcsStatViewed.insert(st);

            //printf("new st with var = %d, on line %d\n", st->variant(), st->lineNumber());
            const int currV = st->variant();
            if (currV == FOR_NODE)
            {   
                //tryToFindPrivateInAttributes(st, privatesVars);

                set<string> toAdd;
                tryToFindPrivateInAttributes(st, toAdd);

                if (PRINT_LOOP_STRUCT)
                    printBlanks(2, (int)parentLoops.size());
                __spf_print(PRINT_LOOP_STRUCT, "FOR NODE on line %d\n", st->lineNumber());

                parentLoops.push_back((SgForStmt*)st);
                changeLoopWeight(currentWeight, sortedLoopGraph, st->lineNumber());
                privatesVarsForLoop.push_back(toAdd);
            }
            else if (currV == CONTROL_END)
            {
                SgStatement* contrlParent = st->controlParent();
                if (contrlParent)
                {
                    if (contrlParent->variant() == FOR_NODE)
                    {
                        changeLoopWeight(currentWeight, sortedLoopGraph, contrlParent->lineNumber(), false);

                        if (loopInfo.find((SgForStmt*)contrlParent) == loopInfo.end() && !sortedLoopGraph[contrlParent->lineNumber()]->hasUnknownDistributedMap)
                            loopWithOutArrays.insert(contrlParent->lineNumber());

                        set<string> unitedPrivates;
                        for (int p = 0; p < parentLoops.size(); ++p)
                            for (auto& privVar : privatesVarsForLoop[p])
                                unitedPrivates.insert(privVar);

                        set<string> setDiff;

                        allLoops[contrlParent->lineNumber()] = make_pair((SgForStmt*)contrlParent, make_pair(unitedPrivates, setDiff));
                        parentLoops.pop_back();
                        privatesVarsForLoop.pop_back();
                    }
                }
                else
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            }
            else if (currV == ASSIGN_STAT)
            {
                if (st->expr(0))
                    findArrayRef(parentLoops, st->expr(0), st->lineNumber(), LEFT, loopInfo, privatesVarsForLoop,
                        sortedLoopGraph, notMappedDistributedArrays,
                        mappedDistrbutedArrays, st, currReg, currentWeight, arrayLinksByFuncCalls);
                if (st->expr(1))
                    findArrayRef(parentLoops, st->expr(1), st->lineNumber(), RIGHT, loopInfo, privatesVarsForLoop,
                        sortedLoopGraph, notMappedDistributedArrays,
                        mappedDistrbutedArrays, st, currReg, currentWeight, arrayLinksByFuncCalls);
            }
            else if (currV == IF_NODE || currV == ELSEIF_NODE || currV == LOGIF_NODE || currV == SWITCH_NODE)
            {
                SgStatement* before = NULL;
                if (st->expr(0))
                {
                    findArrayRef(parentLoops, st->expr(0), st->lineNumber(), RIGHT, loopInfo, privatesVarsForLoop,
                        sortedLoopGraph, notMappedDistributedArrays,
                        mappedDistrbutedArrays, st, currReg, currentWeight, arrayLinksByFuncCalls);
                }
            }
            else if (currV == PROC_STAT)
            {
                auto func = isUserFunctionInProject(st->symbol()->identifier());
                if (func != NULL)
                {
                    SgExpression* parList = st->expr(0);
                    set<DIST::Array*> toRedistr;
                    if (parList)
                    {
                        SgExprListExp* list = isSgExprListExp(parList);
                        for (int z = 0; z < list->length(); ++z)
                        {
                            SgExpression* par = list->elem(z);
                            if ((func->funcParams.inout_types[z] & OUT_BIT) != 0)
                                findArrayRef(parentLoops, par, st->lineNumber(), LEFT, loopInfo, privatesVarsForLoop,
                                    sortedLoopGraph, notMappedDistributedArrays,
                                    mappedDistrbutedArrays, st, currReg, currentWeight, arrayLinksByFuncCalls);
                            else
                                findArrayRef(parentLoops, par, st->lineNumber(), RIGHT, loopInfo, privatesVarsForLoop,
                                    sortedLoopGraph, notMappedDistributedArrays,
                                    mappedDistrbutedArrays, st, currReg, currentWeight, arrayLinksByFuncCalls);
                        }

                    }
                }
            }
            else if (currV == USE_STMT)
            {
                if (st->lineNumber() > 0)
                {
                    auto itF = modulesByName.find(st->symbol()->identifier());

                    if (itF == modulesByName.end())
                    {
                        wstring messageE, messageR;
                        __spf_printToLongBuf(messageE, L"Module with name '%s' must be placed in current file", to_wstring(st->symbol()->identifier()).c_str());

                        __spf_printToLongBuf(messageR, R62, to_wstring(st->symbol()->identifier()).c_str());

                        currMessages->push_back(Messages(ERROR, st->lineNumber(), messageR, messageE, 1028));

                        __spf_print(1, "Module at line %d with name '%s' must be placed in current file\n", st->lineNumber(), st->symbol()->identifier());
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                    }
                }
            }
            else
            {   
                if (isDVM_stat(st) == false && isSgExecutableStatement(st))
                {
                    int const var = st->variant();
                    int side = (var == READ_STAT || var == WRITE_STAT || var == PRINT_STAT) ? LEFT : RIGHT;

                    for (int z = 0; z < 3; ++z)
                        if (st->expr(z))
                            findArrayRef(parentLoops, st->expr(z), st->lineNumber(), side, loopInfo, privatesVarsForLoop,
                                sortedLoopGraph, notMappedDistributedArrays,
                                mappedDistrbutedArrays, st, currReg, currentWeight, arrayLinksByFuncCalls);

                }
            }

            st = st->lexNext();
        }

        auto convertedLoopInfo = convertLoopInfoNoDist(loopInfo, sortedLoopGraph, commonBlocks, declaredArrays, arrayLinksByFuncCalls, createdArrays);

        processLoopInformationForFunction(convertedLoopInfo);

        //find dependencies for loops in function
        initAnnotationsSysExt(0);
        set<SgStatement*> funcWasInit;
        map<SgExpression*, string> collection;

        int idx = 0;
        for (auto& loop : convertedLoopInfo)
        {
            ++idx;
            createNeededException();

            string fName = file->functions(i)->symbol()->identifier();
#ifdef _WIN32
            sendMessage_2lvl(wstring(L"обработка цикла ") + std::to_wstring(idx) + L"/" + std::to_wstring(convertedLoopInfo.size()));
#else
            sendMessage_2lvl(wstring(L"processing loop ") + std::to_wstring(idx) + L"/" + std::to_wstring(convertedLoopInfo.size()));
#endif
            tryToFindDependencies(loop.first, allLoops, funcWasInit, file, regions, currMessages, collection, funcByName, defUseByPlace);

        }

        for (auto& loopLine : loopWithOutArrays)
        {
            if (loopLine > 0)
            {
                tryToFindDependencies(sortedLoopGraph[loopLine], allLoops, funcWasInit, file, regions, currMessages, collection, funcByName, defUseByPlace);
                sortedLoopGraph[loopLine]->withoutDistributedArrays = true;

                //TODO: enable linear writes to non distr arrays for CONSISTENT
                bool hasWritesToArray = false;
                //TODO: add IPA for non pure
                bool hasNonPureProcedures = false;

                auto loopRef = sortedLoopGraph[loopLine];
                SgStatement* loopSt = loopRef->loop;

                for (SgStatement* start = loopSt->lexNext(); start != loopSt->lastNodeOfStmt(); start = start->lexNext())
                {

                    if (start->variant() == PROC_STAT && isIntrinsicFunctionName(start->symbol()->identifier()) == 0)
                    {
                        checkNull(isSgCallStmt(start), convertFileName(__FILE__).c_str(), __LINE__);

                        auto itF = funcByName.find(isSgCallStmt(start)->name()->identifier());
                        bool isPure = false;
                        if (itF != funcByName.end())
                            isPure = itF->second->isPure;

                        if (!isPure)
                        {
                            hasNonPureProcedures = true;
                            loopRef->hasNonPureProcedures = true;

                            messagesForFile.push_back(Messages(WARR, start->lineNumber(), R80, L"Only pure procedures were supported", 1044));
                        }
                    }

                    for (int z = 1; z < 3; ++z)
                        if (hasNonPureFunctions(start->expr(z), loopRef, messagesForFile, start->lineNumber(), funcByName))
                            hasNonPureProcedures = true;
                }

                if (hasWritesToArray || hasNonPureProcedures)
                    loopRef->withoutDistributedArrays = false;
            }
        }

        sendMessage_2lvl(L"");

        createParallelDirectivesNoDist(convertedLoopInfo, regions, arrayLinksByFuncCalls, messagesForFile);
        for (auto& loopLine : loopWithOutArrays)
        {
            auto loopRef = sortedLoopGraph[loopLine];
            if (loopRef->withoutDistributedArrays && loopRef->region && !loopRef->hasLimitsToParallel() && loopRef->lineNum > 0)
            {
                int nesting = 0;
                LoopGraph* it = loopRef;
                for (int z = 0; z < loopRef->perfectLoop; ++z, it->children.size() ? it = it->children[0] : it)
                    if (it->withoutDistributedArrays && it->region && !it->hasLimitsToParallel() && it->lineNum > 0)
                        ++nesting;

                map<LoopGraph*, map<DIST::Array*, ArrayInfo*>> convertedLoopInfo;

                it = loopRef;
                for (int z = 0; z < nesting; ++z, it->children.size() ? it = it->children[0] : it)
                    convertedLoopInfo.insert(make_pair(it, map<DIST::Array*, ArrayInfo*>()));

                createParallelDirectivesNoDist(convertedLoopInfo, regions, map<DIST::Array*, set<DIST::Array*>>(), messagesForFile);
            }
        }

        __spf_print(PRINT_PROF_INFO, "Function ended\n");
    }
}