#include "leak_detector.h"

#include <string>
#include <vector>
#include <algorithm>
#include <set>
#include <map>
#include <stack>
#include <string.h>

#include "ParRegions_func.h"
#include "resolve_par_reg_conflicts.h"

#include "graph_calls_func.h"
#include "graph_loops_func.h"
#include "../LoopAnalyzer/loop_analyzer.h"
#include "../DirectiveProcessing/directive_creator.h"
#include "../DirectiveProcessing/insert_directive.h"
#include "SgUtils.h"
#include "expr_transform.h"
#include "FunctionPurifying/function_purifying.h"
#include "uniq_name_creator.h"

using std::map;
using std::pair;
using std::set;
using std::vector;
using std::stack;
using std::string;
using std::wstring;
using std::to_string;
using std::make_pair;

static inline int getRegionExplicitLine(SgStatement *startR)
{
    checkNull(startR, convertFileName(__FILE__).c_str(), __LINE__);

    const string saveName = current_file->filename();
    startR->switchToFile();

    SgStatement *regSt = startR->lexPrev();
    checkNull(regSt, convertFileName(__FILE__).c_str(), __LINE__);

    SgFile::switchToFile(saveName);
    return regSt->lineNumber();
}

static int getIntervalNumber(const int fileId, const int lineNumber, const uint64_t regionId)
{
    int fileMask = 0xFF;
    int lineMask = 0x7FFFF;
    int regionMask = 0xF;

    int filePart = fileMask & fileId;
    int linePart = lineMask & lineNumber;
    int regionPart = regionMask & regionId;

    filePart = filePart << 4;
    linePart = linePart << 12;
    int number = 1 << 31;

    number = number | filePart | linePart | regionPart;

    return number;
}

// array -> common-block
static map<DIST::Array*, const CommonBlock*> allUsedCommonArrays;

static bool isSPF_reg(SgStatement *st)
{
    return st->variant() == SPF_PARALLEL_REG_DIR || st->variant() == SPF_END_PARALLEL_REG_DIR;
}

const vector<const Variable*> getArraySynonyms(DIST::Array *array)
{
    auto arrayBlock = allUsedCommonArrays.find(array);
    if (arrayBlock == allUsedCommonArrays.end())
    {
        auto location = array->GetLocation();
        if (location.first != DIST::l_COMMON)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        //try to find synonym
        for (auto& elem : allUsedCommonArrays)
        {
            if (elem.second->getName() == location.second)
            {
                arrayBlock = allUsedCommonArrays.find(elem.first);
                break;
            }
        }

        if (arrayBlock == allUsedCommonArrays.end())
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    }

    // find array position
    int pos = -1;
    for (auto &var : arrayBlock->second->getVariables())
    {
        if (var->getName() == arrayBlock->first->GetShortName() && var->getType() == ARRAY)
            pos = var->getPosition();
    }

    // get all variables with this position
    return arrayBlock->second->getVariables(pos);
}

static string getStringDeclaration(SgSymbol *symb)
{
    string decl;
    SgFile *oldFile = current_file;

    if (SgFile::switchToFile(symb->getFile()->filename()) != -1)
    {
        auto stat = symb->makeVarDeclStmt();
        auto res = CalculateInteger(stat->expr(0)->copyPtr());
        stat->setExpression(0, res);    
        decl = stat->unparse();
    }
    else
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    if (SgFile::switchToFile(oldFile->filename()) == -1)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    return decl;
}

static void insertStringDeclarations(SgStatement *insertPlace, DIST::Array *array, UniqueNameCreator& unique_name_creator)
{
    auto varsOnPos = getArraySynonyms(array);
    if (varsOnPos.size() && varsOnPos[0]->getName() == array->GetShortName())
    {
        string newArrName, commName;
        unique_name_creator.GetUniqueName(array, newArrName, commName);
        SgSymbol *varSymb = varsOnPos[0]->getSymbol();
        string varName = varSymb->identifier();
        varSymb->changeName(newArrName.c_str());
        string decl = getStringDeclaration(varsOnPos[0]->getSymbol());
        varSymb->changeName(varName.c_str());
        insertPlace->addComment(decl.c_str());
    }
}

static void createSetOfCalledFuncs(const string &funcName, const map<string, FuncInfo*> &funcMap, set<FuncInfo*> &callSet)
{
    set<string> queue;

    queue.insert(funcName);

    while (queue.size())
    {
        auto it = queue.begin();
        FuncInfo *func = getFuncInfo(funcMap, *it);
        queue.erase(it);

        for (auto &call : func->callsFrom)
        {
            FuncInfo *callFunc = getFuncInfo(funcMap, call);
            auto it2 = callSet.find(callFunc);
            if (it2 == callSet.end() && callFunc)
            {
                callSet.insert(it2, callFunc);
                queue.insert(call);
            }
        }
    }
}

static void fillRegionCover(FuncInfo *func, const map<string, FuncInfo*> &funcMap)
{
    if (func->funcPointer->variant() != ENTRY_STAT)
    {
        if (SgFile::switchToFile(func->fileName) != -1)
        {
            SgStatement *iterator = func->funcPointer->GetOriginal();
            FuncInfo *entry = NULL;
            bool isFuncCovered = true;
            bool isEntryCovered = false;
            bool isRegion = false;

            for (; !isSgExecutableStatement(iterator) && !isSPF_reg(iterator) && iterator->variant() != ENTRY_STAT; iterator = iterator->lexNext())
            {
                // skip not executable and not necessary statements
            }

            for (; iterator->lineNumber() < func->linesNum.second; iterator = iterator->lexNext())
            {
                switch (iterator->variant())
                {
                case SPF_PARALLEL_REG_DIR:
                    isRegion = true;
                    break;
                case SPF_END_PARALLEL_REG_DIR:
                    isRegion = false;
                    break;
                case ENTRY_STAT:
                    entry = getFuncInfo(funcMap, string(iterator->symbol()->identifier()));
                    isEntryCovered = true;
                    break;
                default:
                    if (isSPF_stat(iterator) || isDVM_stat(iterator))
                        break;

                    if (!isRegion)
                    {
                        isFuncCovered = false;
                        isEntryCovered = false;
                    }
                    break;
                }
            }
        }
        else
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    }
}

static void recursiveFill(SgStatement *st,
                          SgExpression *exp,
                          ParallelRegion *region,
                          const string &fileName,
                          const string &funcName,
                          const ParallelRegionLines &lines,
                          const map<string, FuncInfo*> &funcMap,
                          const map<string, CommonBlock*> &commonBlocks)
{
    if (exp)
    {
        if (exp->variant() == ARRAY_REF)
        {
            SgArrayRefExp *arrayRef = isSgArrayRefExp(exp);
            SgType *type = exp->symbol()->type();
            if (isArrayRef(arrayRef))
            {
                SgSymbol *arraySymbol = exp->symbol();
                string arrayName = string(arraySymbol->identifier());
                DIST::Array *array = getArrayFromDeclarated(declaratedInStmt(arraySymbol), arrayName);
                FuncInfo *func = getFuncInfo(funcMap, funcName);

                checkNull(array, convertFileName(__FILE__).c_str(), __LINE__);
                checkNull(func, convertFileName(__FILE__).c_str(), __LINE__);

                if (!array->IsNotDistribute() || array->GetLocation().first == DIST::l_PARAMETER)
                {
                    auto commonBlock = isArrayInCommon(commonBlocks, array);
                    if (commonBlock)
                    {
                        array = commonBlock->getFirstSynonym(array);

                        if (isSgExecutableStatement(st))
                            region->AddUsedCommonArray(func, array, lines);

                        allUsedCommonArrays.insert(make_pair(array, commonBlock));
                    }
                    else if (!lines.isImplicit())
                        region->AddUsedLocalArray(func, array, lines);
                }
            }
        }

        recursiveFill(st, exp->rhs(), region, fileName, funcName, lines, funcMap, commonBlocks);
        recursiveFill(st, exp->lhs(), region, fileName, funcName, lines, funcMap, commonBlocks);
    }
}

void fillRegionArrays(vector<ParallelRegion*> &regions,
                      const map<string, vector<FuncInfo*>> &allFuncInfo,
                      const map<string, CommonBlock*> &commonBlocks)
{
    if (regions.size() == 1 && regions[0]->GetName() == "DEFAULT") // only default
        return;

    map<string, FuncInfo*> funcMap;
    createMapOfFunc(allFuncInfo, funcMap);
    
    for (auto &region : regions)
    {
        for (auto &fileLines : region->GetAllLines())
        {
            // switch to current file
            if (SgFile::switchToFile(fileLines.first) != -1)
            {
                for (auto &regionLines : fileLines.second)
                {
                    SgStatement *iterator = NULL;
                    SgStatement *end = NULL;
                    string funcName = "";

                    // implicit lines
                    if (regionLines.isImplicit())
                    {
                        iterator = SgStatement::getStatementByFileAndLine(fileLines.first, regionLines.lines.first);
                        end = SgStatement::getStatementByFileAndLine(fileLines.first, regionLines.lines.second);
                    }
                    else
                    {
                        iterator = regionLines.stats.first->GetOriginal();
                        end = regionLines.stats.second->GetOriginal();
                    }

                    iterator = getFuncStat(iterator);
                    string containsPrefix = "";
                    SgStatement *st_cp = iterator->controlParent();
                    if (st_cp->variant() == PROC_HEDR || st_cp->variant() == PROG_HEDR || st_cp->variant() == FUNC_HEDR)
                        containsPrefix = st_cp->symbol()->identifier() + string(".");
                    funcName = containsPrefix + iterator->symbol()->identifier();

                    if (regionLines.isImplicit())
                        iterator = SgStatement::getStatementByFileAndLine(fileLines.first, regionLines.lines.first);
                    else
                        iterator = regionLines.stats.first->GetOriginal();

                    for (; iterator && iterator != end->lexNext(); iterator = iterator->lexNext())
                    {
                        if (isSPF_stat(iterator) || isDVM_stat(iterator))
                            continue;

                        if (iterator->variant() == ALLOCATE_STMT || iterator->variant() == DEALLOCATE_STMT)
                            continue;

                        for (int i = 0; i < 3; ++i)
                            recursiveFill(iterator, iterator->expr(i), region, fileLines.first, funcName, regionLines, funcMap, commonBlocks);
                    }
                }
            }
            else
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        }
    }
}

void fillRegionFunctions(vector<ParallelRegion*> &regions, const map<string, vector<FuncInfo*>> &allFuncInfo)
{
    if (regions.size() == 1 && regions[0]->GetName() == "DEFAULT") // only default
        return;

    // funcName -> funcInfo
    map<string, FuncInfo*> funcMap;
    createMapOfFunc(allFuncInfo, funcMap);

    for (auto &nameFunc : funcMap)
    {
        auto func = nameFunc.second;

        fillRegionCover(func, funcMap);

        // add DEFAULT region (regionId == 0)
        if (func->isIndirect())
        {
            for (auto &callInfo : func->callsFromDetailed)
            {
                auto& callFrom = callInfo.detailCallsFrom;

                auto line = callFrom.second;
                auto call = callFrom.first;
                auto callF = getFuncInfo(funcMap, call);
                auto regs = getAllRegionsByLine(regions, func->fileName, line).size();
                if (callF && !getAllRegionsByLine(regions, func->fileName, line).size())
                    callF->callRegions.insert(0);
            }
        }
    }

    // move DEFAULT region (regionId == 0)
    bool changes = true;
    while (changes)
    {
        changes = false;
        for (auto &nameFunc : funcMap)
        {
            auto func = nameFunc.second;
            if (func->callRegions.size() > 1 && func->callRegions.find(0) != func->callRegions.end())
            {
                for (auto &call : func->callsFrom)
                {
                    auto callF = getFuncInfo(funcMap, call);
                    if (callF && callF->callRegions.find(0) == callF->callRegions.end())
                    {
                        changes = true;
                        callF->callRegions.insert(0);
                    }
                }
            }
        }
    }
}

void fillRegionIntervals(vector<ParallelRegion*> &regions)
{
    if (regions.size() == 1 && regions[0]->GetName() == "DEFAULT") // only default
        return;

    for (auto &region : regions)
    {
        for (auto &fileLines : region->GetAllLinesToModify())
        {
            // switch to current file
            if (SgFile::switchToFile(fileLines.first) != -1)
            {
                for (auto &lines : fileLines.second)
                {
                    // explicit lines
                    if (!lines.isImplicit())
                    {
                        SgStatement *start = NULL;
                        SgStatement *end = NULL;

                        // try to find interval before explicit lines
                        end = lines.stats.first->GetOriginal()->lexPrev()->lexPrev(); // before SPF_PARALLEL_REG_DIR
                        if (end && end->variant() == DVM_ENDINTERVAL_DIR)
                        {
                            for (auto st = end; st; st = st->lexPrev())
                            {
                                if (st->variant() == DVM_INTERVAL_DIR)
                                {
                                    start = st;

                                    Statement *intervalStart = new Statement(start);
                                    Statement *intervalEnd = new Statement(end);
                                    lines.intervalBefore = make_pair(intervalStart, intervalEnd);

                                    break;
                                }
                            }
                        }

                        // try to find interval after explicit lines
                        start = lines.stats.second->GetOriginal()->lexNext()->lexNext(); // after SPF_END_PARALLEL_REG_DIR
                        if (start && start->variant() == DVM_INTERVAL_DIR)
                        {
                            for (auto st = start; st; st = st->lexNext())
                            {
                                if (st->variant() == DVM_ENDINTERVAL_DIR)
                                {
                                    end = st;

                                    Statement *intervalStart = new Statement(start);
                                    Statement *intervalEnd = new Statement(end);
                                    lines.intervalAfter = make_pair(intervalStart, intervalEnd);

                                    break;
                                }
                            }
                        }
                    }
                }
            }
            else
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        }
    }
}

bool checkRegions(const vector<ParallelRegion*> &regions,
                  const map<string, vector<FuncInfo*>> &allFuncInfo,
                  map<string, vector<Messages>> &SPF_messages)
{
    bool noError = true;

    // check if region includes itself
    for (auto &region : regions)
    {
        for (auto &fileLines : region->GetAllLines())
        {
            for (auto &lines : fileLines.second)
            {
                if (!lines.isImplicit())
                {
                    for (auto &lines2 : fileLines.second)
                    {
                        if (lines2.isImplicit() && lines2.lines.first <= lines.lines.first && lines2.lines.second >= lines.lines.second)
                        {
                            __spf_print(1, "parallel region '%s' is included in file '%s' on line %d\n", region->GetName().c_str(),
                                        fileLines.first.c_str(), lines2.lines.first);
                            wstring messageE, messageR;
                            __spf_printToLongBuf(messageE, L"parallel region '%s' is included in file '%s'",
                                                 to_wstring(region->GetName()).c_str(), to_wstring(fileLines.first).c_str());

                            __spf_printToLongBuf(messageR, R65,
                                                 to_wstring(region->GetName()).c_str(), to_wstring(fileLines.first).c_str());

                            getObjectForFileFromMap(fileLines.first.c_str(), SPF_messages).push_back(Messages(ERROR, lines2.lines.first, messageR, messageE, 1033));
                            noError = false;
                        }
                    }
                }
            }
        }
    }

    // check if explicit region lines are included by another region
    for (auto &region : regions)
    {
        for (auto &fileLines : region->GetAllLines())
        {
            for (auto &lines : fileLines.second)
            {
                if (!lines.isImplicit())
                {
                    for (auto line = lines.lines.first; line <= lines.lines.second; ++line)
                    {
                        auto inRegs = getAllRegionsByLine(regions, fileLines.first, line);
                        if (inRegs.size() > 1)
                        {
                            __spf_print(1, "parallel region '%s' has line included in another region on line %d\n", region->GetName().c_str(), line);

                            wstring messageE, messageR;
                            __spf_printToLongBuf(messageE, L"parallel region '%s' has line included in another region, try to run Region conflict resolving pass", to_wstring(region->GetName()).c_str());
                            __spf_printToLongBuf(messageR, R74, to_wstring(region->GetName()).c_str());

                            getObjectForFileFromMap(fileLines.first.c_str(), SPF_messages).push_back(Messages(ERROR, line, messageR, messageE, 1041));

                            noError = false;
                        }
                    }
                }
            }
        }
    }

    // check if there are several entries in each fragment
    map<string, FuncInfo*> funcMap;
    createMapOfFunc(allFuncInfo, funcMap);

    for (auto &region : regions)
    {
        for (auto &fileLines : region->GetAllLines())
        {
            if (SgFile::switchToFile(fileLines.first) != -1)
            {
                for (auto &lines : fileLines.second)
                {
                    if (!lines.isImplicit())
                    {
                        noError = noError && checkRegionEntries(lines.stats.first->GetOriginal()->lexPrev(), lines.stats.second->GetOriginal()->lexNext(), funcMap, regions, SPF_messages);
                    }
                }
            }
            else
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        }
    }

    return noError;
}

static void recursiveReplace(SgStatement *st, SgExpression *exp, const string &from, SgSymbol *to)
{
    if (exp)
    {
        if (exp->variant() == ARRAY_REF || exp->variant() == VAR_REF || exp->variant() == FUNC_CALL)
            if (exp->symbol() && exp->symbol()->identifier() == from)
                exp->setSymbol(to);

        recursiveReplace(st, exp->lhs(), from, to);
        recursiveReplace(st, exp->rhs(), from, to);
    }
}

static void replaceSymbol(const string &fileName, const ParallelRegionLines &lines, const string &origSymbName, SgSymbol *newSymb, bool replaceFunc = false)
{
    if (SgFile::switchToFile(fileName) != -1)
    {
        // explicit lines
        if (!lines.isImplicit())
        {
            SgStatement *iterator = lines.stats.first->GetOriginal();
            SgStatement *end = lines.stats.second->GetOriginal()->lexNext();

            for (; iterator != end; iterator = iterator->lexNext())
            {
                // skip not executable statements and change symbols only in executable section
                if (!isSgExecutableStatement(iterator) && (iterator->variant() != VAR_DECL && iterator->variant() != EXTERN_STAT))
                    continue;

                if (iterator->symbol() && iterator->symbol()->identifier() == origSymbName)
                    iterator->setSymbol(*newSymb);

                for (int i = 0; i < 3; ++i)
                    recursiveReplace(iterator, iterator->expr(i), origSymbName, newSymb);

                if (replaceFunc && isSgExecutableStatement(iterator))
                    break;
            }
        }
    }
    else
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
}

static inline string getContains(SgStatement *funcSt)
{
    string containsName;
    SgStatement *st_cp = funcSt->controlParent();
    if (st_cp->variant() == PROC_HEDR || st_cp->variant() == PROG_HEDR || st_cp->variant() == FUNC_HEDR)
        containsName = st_cp->symbol()->identifier() + std::string(".");
    return containsName;
}

static void recReplaceFuncCalls(SgStatement *st, SgExpression *exp, const ParallelRegionLines &lines, const map<string, FuncInfo*> &funcMap, const uint64_t regionId)
{
    if (exp)
    {
        if (exp->variant() == FUNC_CALL)
        {
            SgStatement *externFuncSt = getFuncStat(st);
            SgProcHedrStmt *procH = (SgProcHedrStmt*)externFuncSt;
            string contains = getContains(procH);

            string funcName = exp->symbol()->identifier();
            string fullFuncName = contains + funcName;
            FuncInfo *func = getFuncInfo(funcMap, fullFuncName);

            if (func)
            {
                string newFuncName = func->getFuncNameByRegion(funcName, regionId);
                SgSymbol *newSymb = new SgSymbol(exp->symbol()->variant());
                newSymb->changeName(newFuncName.c_str());
                exp->setSymbol(*newSymb);

                // for extern
                replaceSymbol(current_file->filename(), lines, funcName, newSymb, true);
            }
        }

        recReplaceFuncCalls(st, exp->rhs(), lines, funcMap, regionId);
        recReplaceFuncCalls(st, exp->lhs(), lines, funcMap, regionId);
    }
}

static void replaceFuncCalls(const ParallelRegionLines &lines, const map<string, FuncInfo*> &funcMap, uint64_t regionId = 0)
{
    for (auto st = lines.stats.first->GetOriginal();
         st != lines.stats.second->GetOriginal()->lexNext();
         st = st->lexNext())
    {
        if (st->variant() == PROC_STAT)
        {
            SgStatement *externFuncSt = getFuncStat(st);
            SgProcHedrStmt *procH = (SgProcHedrStmt*)externFuncSt;
            string contains = getContains(procH);

            string funcName = st->symbol()->identifier();
            string fullFuncName = contains + funcName;
            FuncInfo *func = getFuncInfo(funcMap, fullFuncName);
            
            if (func)
            {
                string newFuncName = func->getFuncNameByRegion(funcName, regionId);
                SgSymbol *newSymb = new SgSymbol(st->symbol()->variant());
                newSymb->changeName(newFuncName.c_str());
                st->setSymbol(*newSymb);
            }
        }

        for (int i = 0; i < 3; ++i)
            recReplaceFuncCalls(st, st->expr(i), lines, funcMap, regionId);
    }
}

static map<string, map<DIST::Array*, SgStatement*>> createdCommonBlocks; // file -> array -> new common statement
static map<string, map<DIST::Array*, pair<SgSymbol*, SgSymbol*>>> createdCommonArrays; // file -> array -> (orig, copy)

static SgStatement* createCommonBlock(SgFile *file, DIST::Array *array, UniqueNameCreator& unique_name_creator)
{
    string newArrName, commBlockName;

    unique_name_creator.GetUniqueName(array, newArrName, commBlockName);
    string fileName = file->filename();

    if (SgFile::switchToFile(fileName) != -1)
    {
        auto fileArrayBlock = createdCommonBlocks.find(fileName);
        if (fileArrayBlock == createdCommonBlocks.end())
            fileArrayBlock = createdCommonBlocks.insert(fileArrayBlock, make_pair(fileName, map<DIST::Array*, SgStatement*>()));

        auto arrayBlock = fileArrayBlock->second.find(array);
        if (arrayBlock == fileArrayBlock->second.end())
        {
            // new common-block is not created for array at this file, so create it
            // creating new common-block statement
            //TODO: consistence with declaration
            //string commBlockName = checkSymbNameAndCorrect(array->GetShortName() + "_r");
            SgStatement *commDecl = new SgStatement(COMM_STAT);

            SgSymbol *commSymb = new SgSymbol(VARIABLE_NAME, commBlockName.c_str());
            SgExprListExp *commList = new SgExprListExp(COMM_LIST);
            SgExprListExp *curNode = NULL;
            commDecl->setExpression(0, *commList);
            commList->setSymbol(commSymb);

            // need to add
            // check if need to create new common array symbol
            SgSymbol *newArrSymb = NULL;

            auto it = createdCommonArrays.find(fileName);
            if (it == createdCommonArrays.end())
                it = createdCommonArrays.insert(it, make_pair(fileName, map<DIST::Array*, pair<SgSymbol*, SgSymbol*>>()));

            auto itt = it->second.find(array);
            if (itt == it->second.end()) // need to create symbol
            {
                //TODO: consistence with declaration
                //string newArrName = checkSymbNameAndCorrect(varsOnPos[0]->getName() + "_c");

                newArrSymb = new SgSymbol(VARIABLE_NAME, newArrName.c_str(), file->firstStatement());
                SgType *type = new SgType(T_ARRAY);
                newArrSymb->setType(type);

                itt = it->second.insert(itt, make_pair(array, make_pair((SgSymbol*)NULL, newArrSymb)));
            }
            else
                // just use previous created symbol
                newArrSymb = itt->second.second;

            checkNull(newArrSymb, convertFileName(__FILE__).c_str(), __LINE__);

            // inserting newArrSymb to COMM_STAT
            if (!curNode)
            {
                curNode = new SgExprListExp();
                commList->setLhs(curNode);
            }
            else
            {
                SgExprListExp *newNode = new SgExprListExp();
                curNode->setRhs(newNode);
                curNode = (SgExprListExp*)curNode->rhs();
            }

            SgExpression *arrNode = new SgExpression(VAR_REF, NULL, NULL, newArrSymb);
            curNode->setLhs(arrNode);

            fileArrayBlock->second.insert(make_pair(array, commDecl));

            return commDecl;
        }

        return arrayBlock->second;
    }
    else
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    return NULL;
}

// func -> arrays; funcs where new common statement inserted
static map<FuncInfo*, set<DIST::Array*>> insertedCommonBlocks;

static void insertCommonBlock(FuncInfo *func, DIST::Array *array, UniqueNameCreator& unique_name_creator)
{
    SgFile *file = func->funcPointer->GetOriginal()->getFile();
    SgStatement *insertPlace = NULL;

    for (auto iterator = func->funcPointer->GetOriginal()->lexNext();
         !isSgExecutableStatement(iterator) || isSPF_stat(iterator) && !isSPF_reg(iterator);
         iterator = iterator->lexNext())
    {
        insertPlace = iterator;
    }

    //NULL - no decl stats in function!
    if (!insertPlace)
        insertPlace = func->funcPointer->GetOriginal();

    SgStatement *commDecl = createCommonBlock(file, array, unique_name_creator);
    SgStatement *copyDecl = commDecl->copyPtr();

    auto st = insertPlace->controlParent();
    if (st->variant() == GLOBAL)
        st = insertPlace;

    const int nextLine = insertPlace->lexNext()->lineNumber();
    insertPlace->insertStmtAfter(*copyDecl, *st);
    insertPlace->lexNext()->setlineNumber(nextLine);

    // create declaration via comment
    insertStringDeclarations(insertPlace->lexNext(), array, unique_name_creator);
}

// file -> lines -> arrays; lines where arrays copying is inserted
static map<string, map<ParallelRegionLines, set<string>>> copiedArrays;

static void insertArrayCopying(const string &fileName, const ParallelRegionLines &regionLines, SgSymbol *origSymb, SgSymbol *newSymb)
{
    if (SgFile::switchToFile(fileName) != -1)
    {
        if (regionLines.isImplicit())
            return;

        auto it = copiedArrays.find(fileName);
        if (it == copiedArrays.end())
            it = copiedArrays.insert(it, make_pair(fileName, map<ParallelRegionLines, set<string>>()));

        auto it2 = it->second.find(regionLines);
        if (it2 == it->second.end())
            it2 = it->second.insert(it2, make_pair(regionLines, set<string>()));

        const string arrName = origSymb->identifier();
        auto it3 = it2->second.find(arrName);

        if (it3 == it2->second.end())
        {
            // A_reg = A
            SgAssignStmt* assign = new SgAssignStmt(*new SgArrayRefExp(*newSymb), *new SgArrayRefExp(*origSymb));            
            assign->setlineNumber(getNextNegativeLineNumber()); // before region
            regionLines.stats.first->GetOriginal()->lexPrev()->insertStmtBefore(*assign, *regionLines.stats.first->GetOriginal()->controlParent());

            // A = A_reg
            assign = new SgAssignStmt(*new SgArrayRefExp(*origSymb), *new SgArrayRefExp(*newSymb));
            //TODO: bug with insertion
            //assign->setlineNumber(getNextNegativeLineNumber()); // after region
            regionLines.stats.second->GetOriginal()->lexNext()->insertStmtAfter(*assign, *regionLines.stats.first->GetOriginal()->controlParent());            

            it2->second.insert(arrName);
            // TODO: SPF_ANALYSIS(decl)

            if ((origSymb->attributes() & IN_BIT) || (origSymb->attributes() & OUT_BIT))
            {
                auto func = getFuncStat(regionLines.stats.first->GetOriginal()); 
                auto fromIntent = fillFromIntent(func);

                //TODO: 
                if (fromIntent.size())
                {
                    for (auto& elem : fromIntent)
                    {
                        string ident = origSymb->identifier();
                        if (elem.second.find(ident) != elem.second.end())
                        {
                            if (elem.first->variant() == INTENT_STMT)
                            {
                                SgIntentStmt* stat = (SgIntentStmt*)elem.first;
                                if (stat->numberOfArgs() == 1)
                                    stat->deleteStmt();
                                else
                                {
                                    SgExpression* par = NULL;
                                    SgExpression* ex = stat->expr(0);
                                    while (ex)
                                    {
                                        if (ex->lhs()->symbol()->identifier() == ident)
                                        {
                                            if (par)
                                                par->setRhs(ex->rhs());
                                            else
                                                stat->setExpression(0, ex->rhs());
                                            break;
                                        }

                                        par = ex;
                                        ex = ex->rhs();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    else
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
}

// return true if error exists
static bool replaceCommonArray(const string &fileName,
                               const set<string> &arraySynonymNames,
                               const ParallelRegionLines &lines,
                               map<string, vector<Messages>> &SPF_messages,
                               const bool needInsertCopying = false)
{
    auto it = createdCommonArrays.find(fileName);
    if (it == createdCommonArrays.end())
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    SgStatement *begin = getFuncStat(lines.stats.first->GetOriginal());
    SgStatement *end = begin->lastNodeOfStmt();

    if (begin->symbol() == NULL)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    const string fName = begin->symbol()->identifier();

    // get common-blocks ref
    DIST::Array *array = NULL;
    map<string, vector<SgExpression*>> commonBlocksRef;
    getCommonBlocksRef(commonBlocksRef, begin, end);

    for (auto &commonBlockRef : commonBlocksRef)
    {
        for (auto &commExp : commonBlockRef.second)
        {
            for (auto exp = commExp->lhs(); exp; exp = exp->rhs())
            {
                SgSymbol *varSymb = exp->lhs()->symbol();
                string varName = varSymb->identifier();

                if (arraySynonymNames.find(varName) != arraySynonymNames.end())
                {
                    array = getArrayFromDeclarated(declaratedInStmt(varSymb), varName);

                    if (array)
                    {
                        auto varsOnPos = getArraySynonyms(array);

                        if (!varsOnPos.size())
                            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                        auto var = varsOnPos[0];

                        if (SgFile::switchToFile(var->getSymbol()->getFile()->filename()) != -1)
                        {
                            DIST::Array *commArr = getArrayFromDeclarated(declaratedInStmt(var->getSymbol()), var->getName());
                            checkNull(commArr, convertFileName(__FILE__).c_str(), __LINE__);

                            auto itt = it->second.find(commArr);
                            if (itt == it->second.end())
                                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                            replaceSymbol(fileName, lines, varName, itt->second.second);
                            if (needInsertCopying)
                                insertArrayCopying(fileName, lines, varSymb, itt->second.second);

                            // no error
                            return false;
                        }
                        else
                            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                    }
                }
            }
        }
    }

    if (!array)
    {
        string toPrint;
        for (auto &arrayName : arraySynonymNames)
            toPrint += " '" + arrayName + '\'';
        __spf_print(1, "wrong parallel region position, there is no common-block in current function '%s' with any of such arrays:%s on line %d\n",
                    fName.c_str(), toPrint.c_str(), lines.lines.first);

        wstring messageE, messageR;
        __spf_printToLongBuf(messageE, L"wrong parallel region position, there is no common-block in current function '%s' with any of such arrays:%s",
                             to_wstring(fName).c_str(), to_wstring(toPrint).c_str());
        __spf_printToLongBuf(messageR, R66, to_wstring(fName).c_str(), to_wstring(toPrint).c_str());

        getObjectForFileFromMap(fileName.c_str(), SPF_messages).push_back(Messages(ERROR, lines.lines.first, messageR, messageE, 1034));
        // error
        return true;
    }

    // no error
    return false;
}

pair<SgSymbol*, SgSymbol*> copyArray(const pair<string, int> &place,
                                     const DIST::Array *array,
                                     const vector<ParallelRegionLines>& lines,
                                     const string &suffix, 
                                     string& filename, 
                                     map<string, map<int, set<string>>>& newDeclsToInclude,
                                     map<string, map<int, set<string>>>& copied)
{
    string fileName = place.first;    
    int switchRes = SgFile::switchToFile(fileName);
    bool isIncludeFile = false;
    if (switchRes == -1)
    {
        isIncludeFile = true;
        set<string> files;
        for (auto& elem : lines)
        {
            if (elem.stats.first)
                files.insert(elem.stats.first->fileName());
            else if (elem.stats.second)
                files.insert(elem.stats.second->fileName());
        }

        if (files.size() != 1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        else
        {
            switchRes = SgFile::switchToFile(*files.begin());
            if (switchRes != -1)
                filename = *files.begin();
        }
    }

    if (switchRes != -1)
    {
        //TODO: consistence with declaration 
        //string newArrName = checkSymbNameAndCorrect(array->GetShortName() + suffix);
        string newArrName = array->GetShortName() + suffix;
        SgStatement* decl = SgStatement::getStatementByFileAndLine(place.first, place.second);
        checkNull(decl, convertFileName(__FILE__).c_str(), __LINE__);

        SgStatement* func = getFuncStat(decl, { MODULE_STMT });
        const auto range = make_pair(func->lineNumber(), func->lastNodeOfStmt()->lineNumber());

        SgSymbol *arrSymb = array->GetDeclSymbol(fileName, range, getAllFilesInProject())->GetOriginal();

        SgSymbol *newArrSymb = NULL;        
        SgStatement *newDecl = NULL;

        // for what? to skip .h stats?
        if (decl->fileName() != fileName)
        {
            while (!isSgExecutableStatement(decl) || isSPF_stat(decl) && !isSPF_reg(decl))
                decl = decl->lexNext();
            decl = decl->lexPrev();
        }

        newArrSymb = &arrSymb->copy();
        newArrSymb->changeName(newArrName.c_str());
        newDecl = makeDeclaration(NULL, { newArrSymb });
        
        if (IS_ALLOCATABLE(arrSymb))
        {
            newArrSymb->setAttribute(newArrSymb->attributes() | ALLOCATABLE_BIT);
            if (((SgVarDeclStmt*)newDecl)->addAttributeExpression(new SgExpression(ALLOCATABLE_OP)) == false)
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            // add allocate/deallocate operators
            for (auto &data : getAttributes<SgStatement*, SgStatement*>(decl, set<int>{ ALLOCATE_STMT, DEALLOCATE_STMT }))
            {
                SgExpression *list = data->expr(0);

                set<string> allocated;
                while (list)
                {
                    SgArrayRefExp* arrayRef = isSgArrayRefExp(list->lhs());
                    if (arrayRef != NULL)
                        allocated.insert(string(OriginalSymbol(arrayRef->symbol())->identifier()));
                    list = list->rhs();
                }

                list = data->expr(0);
                while (list)
                {
                    SgArrayRefExp *arrayRef = isSgArrayRefExp(list->lhs());
                    if (arrayRef != NULL)
                    {
                        if (string(OriginalSymbol(arrayRef->symbol())->identifier()) == arrSymb->identifier() && 
                            allocated.find(newArrSymb->identifier()) == allocated.end())
                        {
                            // add parameter to allocate() operator
                            if (data->variant() == ALLOCATE_STMT)
                            {
                                while (list->rhs())
                                    list = list->rhs();

                                auto copy = arrayRef->copyPtr();
                                copy->setSymbol(newArrSymb);

                                list->setRhs(new SgExpression(EXPR_LIST, copy));                                
                            }
                            // add parameter to deallocate() operator
                            else if (data->variant() == DEALLOCATE_STMT)
                            {
                                SgExprListExp* newNode = new SgExprListExp();
                                newNode->setLhs(new SgArrayRefExp(*newArrSymb));
                                newNode->setRhs(data->expr(0));
                                data->setExpression(0, *newNode);
                            }
                            break;
                        }
                    }
                    list = list->rhs();
                }
            }
        }

        if (!isIncludeFile)
        {
            if (!(copied.count(decl->fileName()) &&
                  copied[decl->fileName()].count(decl->lineNumber()) &&
                  copied[decl->fileName()][decl->lineNumber()].count(newDecl->unparse())))
            {
                decl->insertStmtAfter(*newDecl, *decl->controlParent());
                copied[decl->fileName()][decl->lineNumber()].insert(newDecl->unparse());
            }
        }
        else
            newDeclsToInclude[decl->fileName()][decl->lineNumber()].insert(newDecl->unparse());

        return make_pair(arrSymb, newArrSymb);
    }
    else
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
}

static void copyFunction(ParallelRegion *region,
                         FuncInfo *func,
                         const map<string, FuncInfo*> &funcMap,
                         UniqueNameCreator& unique_name_creator,
                         const string &suffix = "")
{
    if (SgFile::switchToFile(func->fileName) != -1)
    {
        SgStatement *funcStat = func->funcPointer->GetOriginal();
        SgStatement *newFuncStat = NULL;
        SgSymbol *funcSymb = func->funcPointer->GetOriginal()->symbol();
        SgSymbol *newFuncSymb = NULL;
        SgFile *file = func->funcPointer->GetOriginal()->getFile();
        string newFuncName = string(funcSymb->identifier()) + suffix;
                
        // create copy function symbol and copy function for original function
        SgStatement* copyFunc = duplicateProcedure(funcStat, &newFuncName, false, false, false);
        newFuncSymb = copyFunc->symbol();

        if (SgFile::switchToFile(func->fileName) == -1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        // set line numbers
        for (auto origStat = funcStat, copyStat = copyFunc;
             origStat != funcStat->lastNodeOfStmt()->lexNext();
             origStat = origStat->lexNext(), copyStat = copyStat->lexNext())
        {
            if (copyStat->variant() != origStat->variant())
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            copyStat->setlineNumber(origStat->lineNumber());
            BIF_FILE_NAME(copyStat->thebif) = BIF_FILE_NAME(origStat->thebif);
        }

        // replace all func calls in copy function
        Statement *begin = new Statement(copyFunc);
        Statement *end = new Statement(copyFunc->lastNodeOfStmt());
        pair<Statement*, Statement*> beginEnd = make_pair(begin, end);
        pair<int, int> newLines = make_pair(beginEnd.first->lineNumber(), beginEnd.second->lineNumber());
        ParallelRegionLines newFuncLines(newLines, beginEnd);
        replaceFuncCalls(newFuncLines, funcMap, region->GetId());

        if (funcStat->variant() == FUNC_HEDR)
            replaceSymbol(current_file->filename(), newFuncLines, func->funcName, newFuncSymb);

        // try to find common-block and add new if common-block exists
        for (auto origStat = funcStat, copyStat = copyFunc;
             origStat && (!isSgExecutableStatement(origStat) || isSPF_stat(origStat));
             origStat = origStat->lexNext(), copyStat = copyStat->lexNext())
        {
            if (origStat->variant() == COMM_STAT)
            {
                for (auto &arrayBlock : allUsedCommonArrays)
                    createCommonBlock(file, arrayBlock.first, unique_name_creator);

                auto usedCommonArrays = region->GetUsedCommonArrays().find(func);
                if (usedCommonArrays != region->GetUsedCommonArrays().end())
                {
                    for (auto &arrayLines : usedCommonArrays->second)
                    {
                        SgStatement *commDecl = createCommonBlock(file, arrayLines.first, unique_name_creator);
                        SgStatement *copyDecl = commDecl->copyPtr();

                        while (!isSgExecutableStatement(copyStat) || isSPF_stat(copyStat))
                            copyStat = copyStat->lexNext();

                        copyStat = copyStat->lexPrev();
                        copyStat->insertStmtAfter(*copyDecl, *copyStat->controlParent());

                        // making declaration of new common array symbol via comment through files
                        insertStringDeclarations(copyStat->lexNext(), arrayLines.first, unique_name_creator);
                    }

                    auto it = createdCommonArrays.find(file->filename());
                    if (it == createdCommonArrays.end())
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                    // replace common arrays to new common arrays in executable code section
                    SgStatement *iterator = begin->GetOriginal();

                    for (; iterator != end->GetOriginal() && (!isSgExecutableStatement(iterator) || isSPF_stat(iterator)); iterator = iterator->lexNext())
                        ;

                    Statement *start = new Statement(iterator);
                    ParallelRegionLines lines(make_pair(start->lineNumber(), end->lineNumber()), make_pair(start, end));

                    // get common-blocks ref
                    map<string, vector<SgExpression*>> commonBlocksRef;
                    getCommonBlocksRef(commonBlocksRef, func->funcPointer->GetOriginal(), func->funcPointer->GetOriginal()->lastNodeOfStmt());

                    for (auto &commonBlockRef : commonBlocksRef)
                    {
                        for (auto &commExp : commonBlockRef.second)
                        {
                            for (auto exp = commExp->lhs(); exp; exp = exp->rhs())
                            {
                                SgSymbol *varSymb = exp->lhs()->symbol();
                                string varName = varSymb->identifier();
                                DIST::Array *array = getArrayFromDeclarated(declaratedInStmt(varSymb), varName);

                                if (array && !array->IsNotDistribute())
                                {
                                    auto varsOnPos = getArraySynonyms(array);

                                    if (!varsOnPos.size())
                                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                                    auto var = varsOnPos[0];

                                    if (SgFile::switchToFile(var->getSymbol()->getFile()->filename()) != -1)
                                    {
                                        DIST::Array *commArr = getArrayFromDeclarated(declaratedInStmt(var->getSymbol()), var->getName());
                                        checkNull(commArr, convertFileName(__FILE__).c_str(), __LINE__);

                                        auto itt = it->second.find(commArr);
                                        if (itt == it->second.end())
                                            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                                        replaceSymbol(func->fileName, lines, varName, itt->second.second);
                                    }
                                    else
                                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                                }
                            }
                        }
                    }
                }

                break;
            }
        }
    }
    else
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
}

void fillUsedArraysInExp(const pair<Statement*, Statement*> &interval, const int exp, set<DIST::Array*> &varSet)
{
    if (exp > 2 || exp < 0)
        return;

    for (auto st = interval.first->GetOriginal()->lexNext(); st != interval.second->GetOriginal(); st = st->lexNext())
    {
        // after CONVERT_ASSIGN_TO_LOOP
        if (st->variant() == ASSIGN_STAT)
        {
            auto varSymb = st->expr(exp)->symbol();
            checkNull(varSymb, convertFileName(__FILE__).c_str(), __LINE__);
            auto array = getArrayFromDeclarated(declaratedInStmt(varSymb), varSymb->identifier());
            checkNull(array, convertFileName(__FILE__).c_str(), __LINE__);
            varSet.insert(array);
        }
    }
}

bool checkRegionsResolving(const vector<ParallelRegion*> &regions,
                           const map<string, vector<FuncInfo*>> &allFuncInfo,
                           const map<string, CommonBlock*> &commonBlocks,
                           map<string, vector<Messages>> &SPF_messages, bool sharedMemoryParallelization)
{
    bool error = false;

    if (regions.size())
    {
        map<string, FuncInfo*> funcMap;
        createMapOfFunc(allFuncInfo, funcMap);

        // check functions
        for (auto &nameFunc : funcMap)
        {
            auto func = nameFunc.second;
            if (func->callRegions.size() > 1)
            {
                string outText = "";
                for (auto &regId : func->callRegions)
                {
                    auto reg = getRegionById(regions, regId);
                    if (!reg && regId)
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                    if (regId)
                        outText += "'" + reg->GetName() + "' ";
                    else
                        outText += "'DEFAULT' ";
                }
                __spf_print(1, "parallel regions %shave common function '%s' which is used inside them\n", outText.c_str(), nameFunc.first.c_str());

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"parallel regions %shave common function '%s' which is used inside them",
                                     to_wstring(outText).c_str(), to_wstring(nameFunc.first).c_str());
                __spf_printToLongBuf(messageR, R133, to_wstring(outText).c_str(), to_wstring(nameFunc.first).c_str());

                ParallelRegion *reg = NULL;
                for (auto &regId : func->callRegions)
                {
                    if (regId)
                    {
                        reg = getRegionById(regions, regId);
                        break;
                    }
                }

                if (!reg)
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                auto lines = reg->GetAllLines();
                bool ok = false;
                for (auto &linePair : lines)
                {
                    for (auto &line : linePair.second)
                    {
                        if (line.stats.first && line.stats.second)
                        {
                            getObjectForFileFromMap(linePair.first.c_str(), SPF_messages).push_back(Messages(ERROR, getRegionExplicitLine(line.stats.first), messageR, messageE, 3012));
                            error = true;
                            ok = true;
                            break;
                        }
                    }
                    if (ok)
                        break;
                }
                if (ok == false)
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            }
        }

        if (sharedMemoryParallelization)
            return error;

        // check local arrays
        for (auto &reg : regions)
        {
            for (auto &funcArrays : reg->GetUsedLocalArrays())
            {
                if (SgFile::switchToFile(funcArrays.first->fileName) != -1)
                {
                    for (auto &arrayLines : funcArrays.second)
                    {
                        auto regsByArr = arrayLines.first->GetRegionsName();
                        bool notResolved = false;

                        if (regsByArr.size() > 1)
                        {
                            // check if array is used only in region and its related interval
                            if (regsByArr.size() == 2 && regsByArr.find("default") != regsByArr.end())
                            {
                                auto array = arrayLines.first;
                                for (auto &line : array->GetUsagePlaces(funcArrays.first->fileName, &funcArrays.first->linesNum))
                                {
                                    auto inRegs = getAllRegionsByLine(regions, funcArrays.first->fileName, line);
                                    if (!inRegs.size())
                                    {
                                        bool inInterval = false;
                                        for (auto &regLines : arrayLines.second)
                                        {
                                            // check interval existing
                                            if (regLines.intervalBefore.first && regLines.intervalBefore.second && regLines.intervalAfter.first && regLines.intervalAfter.second)
                                            {
                                                if (line > regLines.intervalBefore.first->lineNumber() && line < regLines.intervalAfter.second->lineNumber())
                                                    inInterval = true;
                                            }
                                        }

                                        if (!inInterval)
                                            notResolved = true;
                                    }
                                }
                            }
                            else
                                notResolved = true;
                        }

                        if (notResolved)
                        {

                            string regions = "";
                            for (auto &reg : regsByArr)
                                regions += "'" + reg + "' ";
                            __spf_print(1, "parallel regions %shave local array '%s' which is used inside them\n",
                                        regions.c_str(), arrayLines.first->GetShortName().c_str());

                            wstring messageE, messageR;
                            if (arrayLines.first->GetLocation().first == DIST::l_MODULE)
                                __spf_printToLongBuf(messageE, L"parallel regions %shave module array '%s' which is used inside them, try to run Region conflict resolving pass",
                                    to_wstring(regions).c_str(), to_wstring(arrayLines.first->GetShortName()).c_str());
                            else
                                __spf_printToLongBuf(messageE, L"parallel regions %shave local array '%s' which is used inside them, try to run Region conflict resolving pass",
                                                     to_wstring(regions).c_str(), to_wstring(arrayLines.first->GetShortName()).c_str());

                            if (arrayLines.first->GetLocation().first == DIST::l_MODULE)
                                __spf_printToLongBuf(messageR, R152, to_wstring(regions).c_str(), to_wstring(arrayLines.first->GetShortName()).c_str());
                            else
                                __spf_printToLongBuf(messageR, R134, to_wstring(regions).c_str(), to_wstring(arrayLines.first->GetShortName()).c_str());                            


                            auto lines = reg->GetAllLines();
                            bool ok = false;
                            for (auto &linePair : lines)
                            {
                                for (auto &line : linePair.second)
                                {
                                    if (line.stats.first && line.stats.second)
                                    {
                                        getObjectForFileFromMap(linePair.first.c_str(), SPF_messages).push_back(Messages(ERROR, getRegionExplicitLine(line.stats.first), messageR, messageE, 3013));
                                        error = true;
                                        ok = true;
                                        break;
                                    }
                                }
                                if (ok)
                                    break;
                            }
                            if (ok == false)
                                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                        }
                    }
                }
                else
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            }
        }

        // check common arrays
        for (auto &reg : regions)
        {
            for (auto &funcArrays : reg->GetUsedCommonArrays())
            {
                for (auto &arrayLines : funcArrays.second)
                {
                    auto commonBlock = isArrayInCommon(commonBlocks, arrayLines.first);
                    checkNull(commonBlock, convertFileName(__FILE__).c_str(), __LINE__);
                    if (!arrayLines.second.size())
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                    // check common block name and variables count
                    auto lines = arrayLines.second[0].lines;
                    string commonBlockName = commonBlock->getName();
                    auto pos = commonBlockName.rfind("_r");
                    if (!arrayLines.second[0].isImplicit() && (pos != commonBlockName.length() - 2 || commonBlock->getVariables().size() != 1))
                    {
                        __spf_print(1, "parallel region '%s' has common array '%s' which is used inside and outside region on lines %d-%d\n",
                                    reg->GetName().c_str(), arrayLines.first->GetShortName().c_str(), lines.first, lines.second);

                        wstring messageE, messageR;
                        __spf_printToLongBuf(messageE, L"parallel region '%s' has common array '%s' which is used inside and outside region, try to run Region conflict resolving pass",
                                             to_wstring(reg->GetName()).c_str(), to_wstring(arrayLines.first->GetShortName()).c_str());

                        __spf_printToLongBuf(messageR, R135, to_wstring(reg->GetName()).c_str(), to_wstring(arrayLines.first->GetShortName()).c_str());

                        
                        getObjectForFileFromMap(funcArrays.first->fileName.c_str(), SPF_messages).push_back(Messages(ERROR, getRegionExplicitLine(arrayLines.second[0].stats.first), messageR, messageE, 3014));
                        error = true;
                    }
                }
            }
        }

        // check intervals and arrays copying
        for (auto &reg : regions)
        {
            for (auto &fileLines : reg->GetAllLines())
            {
                if (SgFile::switchToFile(fileLines.first) != -1)
                {
                    // check array copying existing
                    for (auto &lines : fileLines.second)
                    {
                        if (!lines.isImplicit())
                        {
                            // check interval existing
                            if (!lines.intervalBefore.first || !lines.intervalBefore.second || !lines.intervalAfter.first || !lines.intervalAfter.second)
                            {
                                __spf_print(1, "parallel region '%s' does not have DVM interval for fragment on line %d\n",
                                            reg->GetName().c_str(), lines.lines.first);

                                wstring messageE, messageR;
                                __spf_printToLongBuf(messageE, L"parallel region '%s' does not have DVM interval for fragment, try to run Region conflict resolving pass", to_wstring(reg->GetName()).c_str());
                                __spf_printToLongBuf(messageR, R136, to_wstring(reg->GetName()).c_str());

                                getObjectForFileFromMap(fileLines.first.c_str(), SPF_messages).push_back(Messages(ERROR, getRegionExplicitLine(lines.stats.first), messageR, messageE, 3015));
                                error = true;
                            }
                            // check arrays
                            else
                            {
                                set<DIST::Array*> leftBefore;
                                set<DIST::Array*> rightAfter;

                                fillUsedArraysInExp(lines.intervalBefore, 0, leftBefore);
                                fillUsedArraysInExp(lines.intervalAfter, 1, rightAfter);

                                // check left and right sets if common array is used at this lines
                                for (auto funcArrays : reg->GetUsedCommonArrays())
                                {
                                    if (funcArrays.first->fileName == fileLines.first)
                                    {
                                        for (auto &arrayLines : funcArrays.second)
                                        {
                                            for (auto &lines2 : arrayLines.second)
                                            {
                                                if (lines == lines2)
                                                {
                                                    if (leftBefore.find(arrayLines.first) == leftBefore.end() || rightAfter.find(arrayLines.first) == rightAfter.end())
                                                    {
                                                        __spf_print(1, "parallel region '%s' does not have copying of array '%s' in DVM interval on line %d\n",
                                                                    reg->GetName().c_str(), arrayLines.first->GetShortName().c_str(), lines.lines.first);

                                                        wstring messageE, messageR;
                                                        __spf_printToLongBuf(messageE, L"parallel region '%s' does not have copying of array '%s' in DVM interval, try to run Region conflict resolving pass",
                                                                             to_wstring(reg->GetName()).c_str(), to_wstring(arrayLines.first->GetShortName()).c_str());

                                                        __spf_printToLongBuf(messageR, R139, to_wstring(reg->GetName()).c_str(), to_wstring(arrayLines.first->GetShortName()).c_str());

                                                        getObjectForFileFromMap(fileLines.first.c_str(), SPF_messages).push_back(Messages(ERROR, lines.lines.first, messageR, messageE, 3018));
                                                        error = true;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }

                                // check left and right sets if local array is used at this lines
                                for (auto funcArrays : reg->GetUsedLocalArrays())
                                {
                                    if (funcArrays.first->fileName == fileLines.first)
                                    {
                                        for (auto &arrayLines : funcArrays.second)
                                        {
                                            for (auto &lines2 : arrayLines.second)
                                            {
                                                if (lines == lines2)
                                                {
                                                    if (leftBefore.find(arrayLines.first) == leftBefore.end() || rightAfter.find(arrayLines.first) == rightAfter.end())
                                                    {
                                                        __spf_print(1, "parallel region '%s' does not have copying of array '%s' in DVM interval on line %d\n",
                                                                    reg->GetName().c_str(), arrayLines.first->GetShortName().c_str(), lines.lines.first);

                                                        wstring messageE, messageR;
                                                        __spf_printToLongBuf(messageE, L"parallel region '%s' does not have copying of array '%s' in DVM interval, try to run Region conflict resolving pass",
                                                                             to_wstring(reg->GetName()).c_str(), to_wstring(arrayLines.first->GetShortName()).c_str());

                                                        __spf_printToLongBuf(messageR, R138, to_wstring(reg->GetName()).c_str(), to_wstring(arrayLines.first->GetShortName()).c_str());

                                                        getObjectForFileFromMap(fileLines.first.c_str(), SPF_messages).push_back(Messages(ERROR, lines.lines.first, messageR, messageE, 3017));
                                                        error = true;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                else
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            }
        }
    }

    return error;
}

//check: A = B
static bool isArrayAssign(SgStatement *st)
{
    if (st->variant() != ASSIGN_STAT)
        return false;
    if (st->expr(0)->variant() != ARRAY_REF)
        return false;
    if (st->expr(1)->variant() != ARRAY_REF)
        return false;

    if (st->expr(0)->lhs() != NULL || st->expr(0)->rhs() != NULL)
        return false;
    if (st->expr(1)->lhs() != NULL || st->expr(1)->rhs() != NULL)
        return false;

    return true;
}

static void compliteUseOnlyList(SgStatement *func, const string &location, const string &s)
{
    if (isSgProgHedrStmt(func) == NULL)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    for (SgStatement* st = func->lexNext(); st; st = st->lexNext())
    {
        if (isSgExecutableStatement(st))
            break;
        if (st->variant() == CONTAINS_STMT)
            break;

        if (st->variant() == USE_STMT)            
        {
            if (st->symbol()->identifier() == location)
            {
                SgExpression* ex = st->expr(0);
                
                if (ex && ex->variant() == ONLY_NODE)
                {
                    SgExpression* toInsert = new SgExpression(RENAME_NODE);
                    SgExpression* list = new SgExpression(EXPR_LIST);

                    toInsert->setLhs(new SgVarRefExp(findSymbolOrCreate(st->getFile(), s)));

                    list->setRhs(ex->lhs());
                    list->setLhs(toInsert);
                    ex->setLhs(list);
                }
            }
        }
    }
}

int resolveParRegions(vector<ParallelRegion*> &regions, const map<string, vector<FuncInfo*>> &allFuncInfo, 
                      map<string, vector<Messages>> &SPF_messages, bool sharedMemoryParallelization,
                      map<string, map<int, set<string>>> &newDeclsToInclude)
{
    bool error = false;

    map<string, FuncInfo*> funcMap;
    createMapOfFunc(allFuncInfo, funcMap);

    UniqueNameCreator unique_name_creator(allFuncInfo);

    if (sharedMemoryParallelization == 0)
    {
        map<string, map<int, set<string>>> copied;

        for (auto& region : regions)
        {
            __spf_print(1, "[%s]: create local arrays\n", region->GetName().c_str());

            // creating new local arrays
            for (auto& funcArrays : region->GetUsedLocalArrays())
            {
                for (auto& arrayLines : funcArrays.second)
                {
                    if (arrayLines.first->GetRegionsName().size() > 1)
                    {
                        bool fromModule = (arrayLines.first->GetLocation().first == DIST::l_MODULE);
                        const string locationName = arrayLines.first->GetLocation().second;

                        auto place = *arrayLines.first->GetDeclInfo().begin();
                        string fileName = place.first;
                        string suffix = "_l";

                        if (fromModule)
                        {
                            fileName = funcArrays.first->fileName;
                            suffix = "_m";
                        }

                        auto origCopy = copyArray(place, arrayLines.first, arrayLines.second, suffix + to_string(region->GetId()), fileName, newDeclsToInclude, copied);
                        for (auto& lines : arrayLines.second)
                        {
                            replaceSymbol(fileName, lines, origCopy.first->identifier(), origCopy.second);
                            insertArrayCopying(fileName, lines, origCopy.first, origCopy.second);
                        }

                        // complete USE ONLY list
                        if (fromModule)
                            compliteUseOnlyList(funcArrays.first->funcPointer, locationName, origCopy.second->identifier());
                    }
                }
            }

            __spf_print(1, "[%s]: create common arrays\n", region->GetName().c_str());

            // creating new common-blocks for files with explicit lines
            for (auto& fileLines : region->GetAllLines())
            {
                if (SgFile::switchToFile(fileLines.first) != -1)
                {
                    for (auto& lines : fileLines.second)
                    {
                        if (!lines.isImplicit())
                        {
                            SgStatement* iterator = getFuncStat(lines.stats.first->GetOriginal());
                            string funcName = iterator->symbol()->identifier();
                            FuncInfo* func = getFuncInfo(funcMap, funcName);

                            auto it = insertedCommonBlocks.find(func);
                            if (it == insertedCommonBlocks.end())
                                it = insertedCommonBlocks.insert(it, make_pair(func, set<DIST::Array*>()));

                            for (auto& arrayBlock : allUsedCommonArrays)
                            {
                                auto varsOnPos = getArraySynonyms(arrayBlock.first);
                                auto itt = it->second.find(arrayBlock.first);
                                // insert only one of all array synonyms
                                if (itt == it->second.end() && arrayBlock.first->GetShortName() == varsOnPos[0]->getName())
                                {
                                    // need to insert common-block
                                    insertCommonBlock(func, arrayBlock.first, unique_name_creator);
                                    it->second.insert(arrayBlock.first);
                                }
                            }
                        }
                    }
                }
                else
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            }

            // insert array copying in explicit lines (and replace common arrays in this lines)
            for (auto& funcArrays : region->GetUsedCommonArrays())
            {
                for (auto& arrayLines : funcArrays.second)
                {
                    for (auto& lines : arrayLines.second)
                    {
                        // get all synonyms for array
                        set<string> arraySynonyms;
                        auto varsOnPos = getArraySynonyms(arrayLines.first);
                        for (auto& var : varsOnPos)
                            arraySynonyms.insert(var->getName());

                        // replace common arrays to new common arrays and insert copying in explicit lines
                        if (lines.isImplicit())
                        {
                            // replace common arrays in explicit lines and insert copying
                            // finding explicit lines where implicit lines are called from
                            // forall lines, lines2: lines.isImplicit(), lines2.isExplicit ==>
                            //     exists func, func2: lines in func, lines2 in func2, func2 calls func explicit or implicit ==>
                            //         insert copying of common-arrays in lines2 used in lines
                            for (auto& fileLines : region->GetAllLines())
                            {
                                if (SgFile::switchToFile(fileLines.first) != -1)
                                {
                                    for (auto& lines2 : fileLines.second)
                                    {
                                        if (!lines2.isImplicit())
                                        {
                                            SgStatement* iterator = getFuncStat(lines2.stats.first->GetOriginal());
                                            string func2Name = iterator->symbol()->identifier();
                                            set<FuncInfo*> callSet;
                                            createSetOfCalledFuncs(func2Name, funcMap, callSet);

                                            auto it = callSet.find(funcArrays.first);
                                            if (it != callSet.end())
                                            {
                                                bool tempErr = replaceCommonArray(fileLines.first, arraySynonyms, lines2, SPF_messages, true);
                                                error = error || tempErr;
                                            }
                                        }
                                    }
                                }
                                else
                                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                            }
                        }
                        else
                        {
                            if (SgFile::switchToFile(funcArrays.first->fileName) != -1)
                            {
                                bool tempErr = replaceCommonArray(funcArrays.first->fileName, arraySynonyms, lines, SPF_messages, true);
                                error = error || tempErr;
                            }
                            else
                                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                        }
                    }
                }
            }
        }

        // creating new common-blocks for main program unit
        for (auto& funcPair : funcMap)
        {
            auto func = funcPair.second;
            if (func->isMain)
            {
                if (SgFile::switchToFile(func->fileName) != -1)
                {
                    auto it = insertedCommonBlocks.find(func);
                    if (it == insertedCommonBlocks.end())
                        it = insertedCommonBlocks.insert(it, make_pair(func, set<DIST::Array*>()));

                    for (auto& arrayBlock : allUsedCommonArrays)
                    {
                        auto varsOnPos = getArraySynonyms(arrayBlock.first);
                        auto itt = it->second.find(arrayBlock.first);
                        // insert only one of all array synonyms
                        if (itt == it->second.end() && arrayBlock.first->GetShortName() == varsOnPos[0]->getName())
                        {
                            // need to insert common-block
                            insertCommonBlock(func, arrayBlock.first, unique_name_creator);
                            it->second.insert(arrayBlock.first);
                        }
                    }
                }
                else
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                break;
            }
        }
    }

    __spf_print(1, "create functions\n");

    // creating new functions
    for (auto &fileFuncs : allFuncInfo)
    {
        if (SgFile::switchToFile(fileFuncs.first) != -1)
        {
            for (auto &func : fileFuncs.second)
            {
                // callRegions = {i}, i = 1, 2, ...
                if (func->callRegions.size() == 1 && *(func->callRegions.begin()) != 0)
                {
                    // need just create new common-blocks and replace arrays
                    SgFile *file = func->funcPointer->GetOriginal()->getFile();
                    ParallelRegion *region = getRegionById(regions, *(func->callRegions.begin()));

                    auto usedCommonArrays = region->GetUsedCommonArrays().find(func);
                    if (usedCommonArrays != region->GetUsedCommonArrays().end())
                    {
                        for (auto &arrayBlock : allUsedCommonArrays)
                            createCommonBlock(file, arrayBlock.first, unique_name_creator);

                        auto it = insertedCommonBlocks.find(func);
                        if (it == insertedCommonBlocks.end())
                            it = insertedCommonBlocks.insert(it, make_pair(func, set<DIST::Array*>()));

                        for (auto &arrayLines : usedCommonArrays->second)
                        {
                            auto itt = it->second.find(arrayLines.first);
                            if (itt == it->second.end())
                            {
                                // need to insert common-block
                                insertCommonBlock(func, arrayLines.first, unique_name_creator);
                                it->second.insert(arrayLines.first);

                                // replace common arrays to new common arrays in executable code section
                                SgStatement *iterator = func->funcPointer->GetOriginal();

                                for (; iterator != func->funcPointer->GetOriginal()->lastNodeOfStmt() &&
                                    (!isSgExecutableStatement(iterator) || isSPF_stat(iterator)); iterator = iterator->lexNext())
                                    ;

                                // get all synonyms for array
                                set<string> arraySynonyms;
                                auto varsOnPos = getArraySynonyms(arrayLines.first);
                                for (auto &var : varsOnPos)
                                    arraySynonyms.insert(var->getName());

                                Statement *start = new Statement(iterator);
                                Statement *end = new Statement(func->funcPointer->GetOriginal()->lastNodeOfStmt());
                                pair<Statement*, Statement*> beginEnd = make_pair(start, end);
                                pair<int, int> funcLines = make_pair(beginEnd.first->GetOriginal()->lineNumber(), beginEnd.second->GetOriginal()->lineNumber());
                                ParallelRegionLines lines(funcLines, beginEnd);
                                bool tempErr = replaceCommonArray(fileFuncs.first, arraySynonyms, lines, SPF_messages);
                                error = error || tempErr;
                            }
                        }
                    }
                }
                // callRegions = {i1, i2, ...}
                else if (func->callRegions.size() > 1)
                {
                    // need copy function for every region, the exeption is defalut region
                    for (auto &regionId : func->callRegions)
                        if (regionId)
                            copyFunction(getRegionById(regions, regionId), func, funcMap, unique_name_creator, string("_r") + to_string(regionId));
                }
            }
        }
        else
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    }

    // replace function calls in funcs with callRegions = {i}, i = 1, 2, ...
    for (auto &fileFuncs : allFuncInfo)
    {
        if (SgFile::switchToFile(fileFuncs.first) != -1)
        {
            for (auto &func : fileFuncs.second)
            {
                if (func->callRegions.size() == 1 && *(func->callRegions.begin()))
                {
                    int regionId = *(func->callRegions.begin());
                    Statement *begin = func->funcPointer;
                    Statement *end = new Statement(begin->GetOriginal()->lastNodeOfStmt());
                    ParallelRegionLines funcLines(make_pair(begin->GetOriginal()->lineNumber(), end->GetOriginal()->lineNumber()), make_pair(func->funcPointer, end));
                    replaceFuncCalls(funcLines, funcMap, regionId);
                }
            }
        }
        else
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    }

    // replace function calls in explicit lines
    for (auto &region : regions)
    {
        for (auto &fileLines : region->GetAllLines())
        {
            if (SgFile::switchToFile(fileLines.first) != -1)
            {
                for (auto &lines : fileLines.second)
                {
                    if (!lines.isImplicit())
                        replaceFuncCalls(lines, funcMap, region->GetId());
                }
            }
            else
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        }
    }

    if (sharedMemoryParallelization == 0)
    {
        __spf_print(1, "insert DVM intervals\n");

        // inserting dvm intervals
        for (auto& region : regions)
        {
            for (auto& fileLines : region->GetAllLines())
            {
                if (SgFile::switchToFile(fileLines.first) != -1)
                {
                    for (auto& lines : fileLines.second)
                    {
                        if (!lines.isImplicit())
                        {
                            // create DVM INTERVAL before region
                            SgStatement* start = NULL;
                            SgStatement* end = lines.stats.first->GetOriginal()->lexPrev()->lexPrev();

                            for (SgStatement* st = end; st && st->lineNumber() < 0 && isArrayAssign(st); st = st->lexPrev())
                                start = st;

                            // DVM END INTERVAL
                            SgStatement* interval = new SgStatement(DVM_ENDINTERVAL_DIR);
                            interval->setlineNumber(lines.stats.first->GetOriginal()->lineNumber());
                            end->insertStmtAfter(*interval, *end->controlParent());

                            // DVM INTERVAL N
                            interval = new SgStatement(DVM_INTERVAL_DIR);
                            SgExprListExp* newNode = new SgExprListExp();
                            int val = getIntervalNumber(current_file_id, lines.stats.first->GetOriginal()->lexPrev()->lineNumber(), region->GetId());
                            newNode->setLhs(new SgValueExp(val));
                            interval->setExpression(0, *newNode);
                            interval->setlineNumber(lines.stats.first->GetOriginal()->lineNumber());
                            if (start)
                                start->insertStmtBefore(*interval, *start->controlParent());
                            else
                                end->insertStmtAfter(*interval, *end->controlParent());


                            // create DVM INTERVAL after region
                            start = lines.stats.second->GetOriginal()->lexNext()->lexNext();
                            end = NULL;

                            for (SgStatement* st = start; st && !st->lineNumber() && isArrayAssign(st); st = st->lexNext())
                                end = st;

                            // DVM INTERVAL N
                            interval = new SgStatement(DVM_INTERVAL_DIR);
                            newNode = new SgExprListExp();
                            val = getIntervalNumber(current_file_id, lines.stats.second->GetOriginal()->lexNext()->lineNumber(), region->GetId());
                            newNode->setLhs(new SgValueExp(val));
                            interval->setExpression(0, *newNode);
                            interval->setlineNumber(lines.stats.second->GetOriginal()->lineNumber());
                            start->insertStmtBefore(*interval, *start->controlParent());

                            // DVM END INTERVAL
                            interval = new SgStatement(DVM_ENDINTERVAL_DIR);
                            interval->setlineNumber(lines.stats.second->GetOriginal()->lineNumber());
                            if (end)
                                end->insertStmtAfter(*interval, *end->controlParent());
                            else
                                start->insertStmtBefore(*interval, *start->controlParent());
                        }
                    }
                }
                else
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            }
        }
    }

    return (error ? 1 : 0);
}

int printCheckRegions(const char *fileName, const vector<ParallelRegion*> &regions, const map<string, vector<FuncInfo*>> &allFuncInfo)
{
    map<string, FuncInfo*> funcMap;
    createMapOfFunc(allFuncInfo, funcMap);

    string outText = "";
    string elems = "";
    
    for (auto &region : regions)
    {
        outText += "*** REGION '" + region->GetName() + "'\n";
        for (auto &nameFunc : funcMap)
        {
            if (nameFunc.second->callRegions.size() > 1)
            {
                for (auto &regId : nameFunc.second->callRegions)
                    if (region->GetId() == regId)
                        elems += " '" + nameFunc.first + '\'';
            }
        }

        if (elems.size())
            outText += "  COMMON FUNCTIONS:" + elems + '\n';

        set<string> arrays;
        for (auto &funcArrays : region->GetUsedCommonArrays())
            for (auto &arrayLines : funcArrays.second)
                arrays.insert(arrayLines.first->GetShortName());

        elems.clear();
        for (auto &arrayName : arrays)
            elems += " '" + arrayName + '\'';

        if (elems.size())
            outText += "  COMMON ARRAYS:" + elems + '\n';

        elems.clear();
        for (auto &funcArrays : region->GetUsedLocalArrays())
        {
            for (auto &arrayLines : funcArrays.second)
            {
                string toPrint = "";
                for (auto &lines : arrayLines.second)
                {
                    toPrint += "[" + to_string(lines.lines.first);
                    toPrint += "-" + to_string(lines.lines.second);
                    toPrint += "]";
                }

                elems += "    [" + funcArrays.first->funcName + ", " + arrayLines.first->GetShortName() + ", " + toPrint + "]\n";
            }
        }

        if (elems.size())
            outText += "  LOCAL ARRAYS in [FUNC, ARRAY, [LINES]]:\n" + elems;
    }

    outText += "*** SUMMARY\n";
    
    elems.clear();
    for (auto &nameFunc : funcMap)
        if (nameFunc.second->callRegions.size() > 1)
            elems += " '" + nameFunc.first + '\'';

    if (elems.size())
        outText += "  ALL COMMON FUNCTIONS : " + elems + '\n';

    elems.clear();
    for (auto &commonArrayCommonBlock : allUsedCommonArrays)
        elems += " '" + commonArrayCommonBlock.first->GetShortName() + '\'';

    if (elems.size())
        outText += "  ALL COMMON ARRAYS:" + elems + '\n';
           
    if (fileName == NULL)
        __spf_print(1, "%s", outText.c_str());    
    else
    {
        FILE *file = fopen(fileName, "w");
        if (file == NULL)
        {
            __spf_print(1, "can not open file '%s'\n", fileName);
            return -1;
        }
        fprintf(file, "%s", outText.c_str());
        fclose(file);
    }
    return 0;
}

void insertRealignsBeforeFragments(ParallelRegion *reg, SgFile *file, const set<DIST::Array*> &distrArrays, const map<DIST::Array*, 
                                   set<DIST::Array*>>& arrayLinksByFuncCalls)
{
    auto currFile = new File(file);

    if (distrArrays.size() == 0)
        return;
    if (reg->GetName() == "DEFAULT")
        return;

    auto lines = reg->GetLines(file->filename());
    if (lines)
    {
        set<DIST::Array*> withoutTempl;
        for (auto& elem : distrArrays)
            if (elem->IsArray())
                withoutTempl.insert(elem);

        for (auto& elem : *lines)
        {
            if (!elem.isImplicit())
            {
                checkNull(elem.stats.first, convertFileName(__FILE__).c_str(), __LINE__);
                checkNull(elem.stats.second, convertFileName(__FILE__).c_str(), __LINE__);

                checkNull(elem.intervalBefore.first, convertFileName(__FILE__).c_str(), __LINE__);
                checkNull(elem.intervalBefore.second, convertFileName(__FILE__).c_str(), __LINE__);

                auto toRealign = createRealignRules(elem.intervalBefore.first, reg->GetId(), currFile, "", arrayLinksByFuncCalls, withoutTempl, make_pair(0, 0));
                auto cp = elem.intervalBefore.first->controlParent();

                if (toRealign.second.size() == 0)
                    break;

                for (auto& rule : toRealign.second)
                {
                    elem.intervalBefore.first->insertStmtBefore(*new SgStatement(DVM_NEW_VALUE_DIR), *cp);
                    elem.intervalBefore.first->insertStmtBefore(*createStatFromExprs(((CreatedDirective*)rule)->sageData), *cp);
                }
            }
        }
    }

    delete currFile;
}