#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 "loop_analyzer.h"
#include <tuple>
#include <stack>

#include "leak_detector.h"

#if _WIN32 && NDEBUG && __BOOST
#include <boost/thread.hpp>
#endif
extern int passDone;

#include "../Distribution/Distribution.h"
#include "../Distribution/GraphCSR.h"
#include "../Distribution/Arrays.h"
#include "../ParallelizationRegions/ParRegions.h"

#include "errors.h"
#include "../DirectiveProcessing/directive_parser.h"
#include "../DirectiveProcessing/directive_creator.h"

#include "SgUtils.h"
#include "AstWrapper.h"

#include "graph_calls_func.h"
#include "graph_loops_func.h"
#include "../ParallelizationRegions/ParRegions_func.h"
#include "../DynamicAnalysis/gCov_parser_func.h"

#include "expr_transform.h"
#include "../SageAnalysisTool/depInterfaceExt.h"

#include "../VisualizerCalls/get_information.h"
#include "../VisualizerCalls/SendMessage.h"

#include "LoopEndDoConverter/enddo_loop_converter.h"

#include "../DirectiveProcessing/remote_access.h"
#include "../DirectiveProcessing/directive_omp_parser.h"

#define PRINT_ARRAY_ARCS  0
#define PRINT_LOOP_STRUCT 0
#define PRINT_PROF_INFO   0
#define DEB               0

extern REGIME currRegime;
extern std::vector<Messages>* currMessages;

extern int sharedMemoryParallelization;
extern int parallizeFreeLoops;

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;

REGIME currRegime = UNDEF;
std::vector<Messages>* currMessages;

static bool hasArrayAccessInSubscr(SgExpression *exp)
{
    bool retVal = false;
    if (exp->variant() == ARRAY_REF)
        return true;

    if (exp->lhs())
        retVal |= hasArrayAccessInSubscr(exp->lhs());
    if (exp->rhs())
        retVal |= hasArrayAccessInSubscr(exp->rhs());

    return retVal;
}

bool checkExistence(SgExpression *exp, const string& doName)
{
    bool retVal = false;
    if (exp->variant() == VAR_REF)
        if (exp->symbol()->identifier() == doName)
            retVal = true;

    if (exp->lhs())
        retVal |= checkExistence(exp->lhs(), doName);
    if (exp->rhs())
        retVal |= checkExistence(exp->rhs(), doName);

    return retVal;
}

static pair<bool, string> constructArrayRefForPrint(SgArrayRefExp *arrayRef, const int dimNum, SgExpression *subscr)
{
    bool needToPrint = true;
    const int num = arrayRef->numberOfSubscripts();
    string ref(arrayRef->symbol()->identifier());
    ref += "(";
    for (int i = 0; i < num; ++i)
    {
        if (i == dimNum)
        {
            char *copySub = copyOfUnparse(subscr->unparse());
            ref += copySub;
            if (subscr->variant() == INT_VAL)
                needToPrint = false;

            removeFromCollection(copySub);
            delete []copySub;
        }
        else
            ref += "*";

        if (i != num - 1)
            ref += ",";
        else
            ref += ")";
    }
    std::transform(ref.begin(), ref.end(), ref.begin(), ::toupper);
    return make_pair(needToPrint, ref);
}

static void addInfoToMap(map<SgForStmt*, map<SgSymbol*, ArrayInfo>> &loopInfo, SgForStmt *position, SgSymbol *symb, 
                         SgArrayRefExp *arrayRefIn, const int dimNum, const REMOTE_TYPE &value, const int currLine,
                         const int maxDimSize)
{
    ArrayRefExp *arrayRef = new ArrayRefExp(arrayRefIn);
    auto it = loopInfo.find(position);
    if (loopInfo.end() == it)
        it = loopInfo.insert(it, make_pair(position, map<SgSymbol*, ArrayInfo>()));

    auto it1 = it->second.find(symb);
    if (it1 == it->second.end())
        it1 = it->second.insert(it1, make_pair(symb, ArrayInfo()));

    auto it2 = it1->second.arrayAccessUnrec.find(arrayRef);
    if (it2 == it1->second.arrayAccessUnrec.end())
    {
        it2 = it1->second.arrayAccessUnrec.insert(it2, make_pair(arrayRef, make_pair(currLine, vector<REMOTE_TYPE>())));

        it2->second.second.resize(maxDimSize);
        std::fill(it2->second.second.begin(), it2->second.second.end(), REMOTE_NONE);
    }

    if (dimNum == -1)
    {
        for (int z = 0; z < it2->second.second.size(); ++z)
            it2->second.second[z] |= value;
    }
    else
        it2->second.second[dimNum] |= value;

    if (value == REMOTE_TRUE)
        __spf_print(DEB, "RemoteAccess[%d]: true for dim %d and array %s, loop line %d\n", __LINE__, dimNum, symb->identifier(), position->lineNumber());
}

enum { READ_OP, WRITE_OP, UNREC_OP };

static void addInfoToVectors(map<SgForStmt*, map<SgSymbol*, ArrayInfo>> &loopInfo, SgForStmt *position, SgSymbol *symb, 
                      const int dimNum, const pair<int, int> newCoef, int type, const int maxDimSize, const double currentW)
{
    auto itLoop = loopInfo.find(position);
    if (itLoop == loopInfo.end())
        itLoop = loopInfo.insert(itLoop, make_pair(position, map<SgSymbol*, ArrayInfo>()));

    auto itSymb = itLoop->second.find(symb);
    if (itSymb == itLoop->second.end())
        itSymb = itLoop->second.insert(itSymb, make_pair(symb, ArrayInfo()));

    itSymb->second.setDimSize(maxDimSize);

    if (type == READ_OP)
    {
        auto itAdd = itSymb->second.readOps[dimNum].coefficients.find(newCoef);
        //add only uniq 
        if (itAdd == itSymb->second.readOps[dimNum].coefficients.end())
            itAdd = itSymb->second.readOps[dimNum].coefficients.insert(itAdd, make_pair(newCoef, currentW));        
    }
    else if (type == WRITE_OP)
    {
        auto itAdd = itSymb->second.writeOps[dimNum].coefficients.find(newCoef);
        if (itAdd == itSymb->second.writeOps[dimNum].coefficients.end())
            itAdd = itSymb->second.writeOps[dimNum].coefficients.insert(itAdd, make_pair(newCoef, currentW));        
    }
    else if (type == UNREC_OP)
        itSymb->second.unrecReadOps[dimNum] = true;
    else
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
}


static vector<int> matchSubscriptToLoopSymbols(const vector<SgForStmt*> &parentLoops, SgExpression *subscr,
                                               SgArrayRefExp *arrayRefIn, const int side, const int dimNum,
                                               map<SgForStmt*, map<SgSymbol*, ArrayInfo>> &loopInfo,
                                               const int currLine, const int numOfSubscriptions, const double currentW)
{
    SgExpression *origSubscr = subscr;
    ArrayRefExp *arrayRef = new ArrayRefExp(arrayRefIn);
    
    // REVERT_SUBS has been done before REMOTE_ACC PASS
    if (currRegime == REMOTE_ACC)
    {
        auto data = getAttributes<SgExpression*, SgExpression*>(arrayRefIn, set<int>{ ARRAY_REF });
        if (data.size() == 1)
        {
            SgExpression *dataS = data[0]->lhs();
            for (int z = 0; z < dimNum; ++z)
                dataS = dataS->rhs();
            subscr = dataS->lhs();
        }
    }

    int countOfSymbols = 0;
    int position = -1;
    vector<int> allPositions;
    bool hasArrayAcc = hasArrayAccessInSubscr(subscr);
    SgSymbol *currOrigArrayS = OriginalSymbol(arrayRef->symbol());

    if (!hasArrayAcc)
    {
        for (int i = 0; i < (int)parentLoops.size(); ++i)
        {
            if (checkExistence(subscr, parentLoops[i]->doName()->identifier()))
            {
                countOfSymbols++;
                position = i;
                allPositions.push_back(i);
            }
        }
    }

    pair<int, int> coeffs = pair<int, int>(0, 0);
    // more than one loop symbol in subscription
    if (countOfSymbols > 1)
    {
        __spf_print(PRINT_ARRAY_ARCS, " <%d|%d> ", 0, 0);
        if (currRegime == DATA_DISTR || currRegime == SHARED_MEMORY_PAR)
        {
            const pair<bool, string> &arrayRefString = constructArrayRefForPrint(arrayRef, dimNum, origSubscr);
            __spf_print(1, "WARN: array ref '%s' at line %d has more than one loop's variables\n", arrayRefString.second.c_str(), currLine);

            wstring messageE, messageR;
            __spf_printToLongBuf(messageE, L"array ref '%s' has more than one loop's variables", to_wstring(arrayRefString.second).c_str());
            __spf_printToLongBuf(messageR, R54, to_wstring(arrayRefString.second).c_str());

            if (currLine > 0)
                currMessages->push_back(Messages(WARR, currLine, messageR, messageE, 1021));
        }

        for (int i = 0; i < allPositions.size(); ++i)
        {
            if (currRegime == REMOTE_ACC)
            {
                if (side == RIGHT)
                    addInfoToMap(loopInfo, parentLoops[allPositions[i]], currOrigArrayS, arrayRef, dimNum, REMOTE_TRUE, currLine, numOfSubscriptions);
            }
            else
                addInfoToVectors(loopInfo, parentLoops[allPositions[i]], currOrigArrayS, dimNum, make_pair(0, 0), UNREC_OP, numOfSubscriptions, currentW);
        }
    }
    // no loop symbol in subscription
    else if (countOfSymbols == 0)
    {
        __spf_print(PRINT_ARRAY_ARCS, " <%d|%d> ", 0, 0);
        if (currRegime == REMOTE_ACC)
        {
            if (side == RIGHT)
                for (int i = 0; i < (int)parentLoops.size(); ++i)
                    addInfoToMap(loopInfo, parentLoops[i], currOrigArrayS, arrayRef, dimNum, REMOTE_TRUE, currLine, numOfSubscriptions);
        }
        else if (currRegime == DATA_DISTR || currRegime == SHARED_MEMORY_PAR)
        {
            const pair<bool, string> &arrayRefString = constructArrayRefForPrint(arrayRef, dimNum, origSubscr);

            if (!hasArrayAcc)
            {
                if (parentLoops.size() != 0 && (arrayRefString.first || side == LEFT))
                {
                    __spf_print(1, "WARN: array ref '%s' in %d dimension at line %d does not have loop variables\n", arrayRefString.second.c_str(), dimNum + 1, currLine);

                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"array ref '%s' in %d dimension does not have loop variables", to_wstring(arrayRefString.second).c_str(), dimNum + 1);

                    __spf_printToLongBuf(messageR, R55, to_wstring(arrayRefString.second).c_str(), dimNum + 1);

                    if (currLine > 0)
                        currMessages->push_back(Messages(WARR, currLine, messageR, messageE, 1021));
                }
            }
            else
            {
                __spf_print(1, "WARN: array ref '%s' at line %d has indirect access\n", arrayRefString.second.c_str(), currLine);

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"array ref '%s' has indirect access", to_wstring(arrayRefString.second).c_str());

                __spf_printToLongBuf(messageR, R56, to_wstring(arrayRefString.second).c_str());

                if (currLine > 0)
                    currMessages->push_back(Messages(WARR, currLine, messageR, messageE, 1022));
            }
        }
    }
    else
    {
        bool needToCacl = true;
        if (subscr->variant() == VAR_REF)
        {
            if (subscr->symbol()->id() == (parentLoops[position]->doName())->id())
            {
                coeffs.first = 1;
                needToCacl = false;
            }
        }

        if (needToCacl)
            getCoefsOfSubscript(coeffs, subscr, parentLoops[position]->doName());
        __spf_print(PRINT_ARRAY_ARCS, " <%d %d> ", coeffs.first, coeffs.second);

        if (coeffs.first == 0) // && coeffs.second == 0)
        {
            if (currRegime == REMOTE_ACC)
            {
                if (side == RIGHT)
                    addInfoToMap(loopInfo, parentLoops[position], currOrigArrayS, arrayRef, dimNum, REMOTE_TRUE, currLine, numOfSubscriptions);
            }
            else if (currRegime == DATA_DISTR || currRegime == SHARED_MEMORY_PAR)
            {                
                const pair<bool, string> &arrayRefString = constructArrayRefForPrint(arrayRef, dimNum, origSubscr);
                __spf_print(1, "WARN: can not calculate index expression for array ref '%s' at line %d\n", arrayRefString.second.c_str(), currLine);
                addInfoToVectors(loopInfo, parentLoops[position], currOrigArrayS, dimNum, coeffs, UNREC_OP, numOfSubscriptions, currentW);
                if (side == LEFT)
                    allPositions.clear();

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"can not calculate index expression for array ref '%s'", to_wstring(arrayRefString.second).c_str());

                __spf_printToLongBuf(messageR, R57, to_wstring(arrayRefString.second).c_str());

                if (currLine > 0)
                    currMessages->push_back(Messages(WARR, currLine, messageR, messageE, 1023));
            }
        }
        else
        {
            if (currRegime == REMOTE_ACC)
            {
                if (side == RIGHT)
                {
                    vector<ArrayOp>& currOp = loopInfo[parentLoops[position]][currOrigArrayS].arrayAccess[arrayRef].second;

                    if (currOp.size() < numOfSubscriptions)
                        currOp.resize(numOfSubscriptions);

                    //add only uniq 
                    auto itAdd = currOp[dimNum].coefficients.find(coeffs);
                    if (itAdd == currOp[dimNum].coefficients.end())
                        itAdd = currOp[dimNum].coefficients.insert(itAdd, make_pair(coeffs, currentW));
                }

                if (coeffs.first < 0)
                    addInfoToMap(loopInfo, parentLoops[position], currOrigArrayS, arrayRef, dimNum, REMOTE_TRUE, currLine, numOfSubscriptions);
                else
                    //if we found regular access to array - set it false
                    addInfoToMap(loopInfo, parentLoops[position], currOrigArrayS, arrayRef, dimNum, REMOTE_FALSE, currLine, numOfSubscriptions);
            }

            if (coeffs.first < 0 && sharedMemoryParallelization == 0)
            {
                if (currRegime == DATA_DISTR)
                {
                    const pair<bool, string> &arrayRefString = constructArrayRefForPrint(arrayRef, dimNum, origSubscr);
                    const int line = (currLine < 0) ? parentLoops[position]->localLineNumber() : currLine;

                    __spf_print(1, "WARN: coefficient A in A*x+B is not positive for array ref '%s' at line %d, inverse distribution in not supported yet\n", arrayRefString.second.c_str(), line);

                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"coefficient A in A*x+B is not positive for array ref '%s', inverse distribution in not supported yet", to_wstring(arrayRefString.second).c_str());

                    __spf_printToLongBuf(messageR, R58, to_wstring(arrayRefString.second).c_str());

                    if (line > 0)
                        currMessages->push_back(Messages(WARR, line, messageR, messageE, 1024));
                    if (side == LEFT)
                        allPositions.clear();
                    else
                        addInfoToVectors(loopInfo, parentLoops[position], currOrigArrayS, dimNum, coeffs, UNREC_OP, numOfSubscriptions, currentW);
                }
            }
            else
            {
                if (side == LEFT)
                    addInfoToVectors(loopInfo, parentLoops[position], currOrigArrayS, dimNum, coeffs, WRITE_OP, numOfSubscriptions, currentW);
                else
                    addInfoToVectors(loopInfo, parentLoops[position], currOrigArrayS, dimNum, coeffs, READ_OP, numOfSubscriptions, currentW);
            }
        }
    }

    if (currRegime == ARRAY_ACC_CORNER)
    {
        int *valueSubs = new int[2];
        valueSubs[0] = coeffs.first;
        valueSubs[1] = coeffs.second;
#ifdef __SPF
        addToCollection(__LINE__, __FILE__, valueSubs, 2);
#endif
        const vector<int*> &coeffs = getAttributes<SgExpression*, int*>(subscr, set<int>{ INT_VAL });
        if (coeffs.size() == 0)
        {
            subscr->addAttribute(INT_VAL, valueSubs, sizeof(int*));
            if (position != -1 && allPositions.size() == 1 && position < parentLoops.size())
                subscr->addAttribute(FOR_NODE, parentLoops[position], sizeof(SgStatement));
        }
    }

    return allPositions;
}

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);
        }
    }

    if (currRegime == ARRAY_ACC_CORNER)
        return wasFoundForLoop;

    bool ifUnknownArrayAssignFound = false;
    vector<int> canNotMapToLoop;
    for (int i = 0; i < wasFoundForLoop.size(); ++i)
    {
        if (wasFoundForLoop[i] != 1 &&
            // always true for distributed data case
            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 && (currRegime == DATA_DISTR || currRegime == COMP_DISTR || currRegime == SHARED_MEMORY_PAR))
                itLoop->second->hasUnknownArrayAssigns = true;

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

    if (side == LEFT)
    {
        if (ifUnknownArrayAssignFound && (currRegime == DATA_DISTR || currRegime == SHARED_MEMORY_PAR))
        {
            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));
            }
        }
    }
    else if (side == RIGHT)
    {
        SgSymbol* currOrigArrayS = OriginalSymbol(arrayRef->symbol());

        if (currRegime == REMOTE_ACC)
        {
            DIST::Array* currArray = getArrayFromDeclarated(declaratedInStmt(currOrigArrayS), currOrigArrayS->identifier());    
            if (!currArray && currOrigArrayS->type()->variant() == T_ARRAY)
                checkNull(currArray, convertFileName(__FILE__).c_str(), __LINE__);

            if (currArray)
            {
                // for integration with SAPFOR-C
                map<LoopGraph*, map<DIST::Array*, ArrayInfo*>> tmpInfo;
                vector<LoopGraph*> parentGraphLoops;
                auto tmpRef = new ArrayRefExp(arrayRef);

                for (auto& loop : parentLoops)
                {
                    auto it = sortedLoopGraph.find(loop->lineNumber());
                    if (it == sortedLoopGraph.end())
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                    parentGraphLoops.push_back(it->second);
                }

                auto requests = checkArrayRefInLoopForRemoteStatus(ifUnknownArrayAssignFound, sumMatched, numOfSubs, maxMatched,
                                                                   currLine, currArray, wasFoundForLoop, tmpRef,
                                                                   tmpInfo, matchedToDim, sortedLoopGraph, arrayLinksByFuncCalls, 
                                                                   reg, parentGraphLoops);
            
                //clean memory
                for (auto& loop : tmpInfo)
                    for (auto& array : loop.second)
                        delete array.second;

                for (auto& req : requests)
                    addInfoToMap(loopInfo, (SgForStmt*)req.position->loop->GetOriginal(), currOrigArrayS, arrayRef, req.dimNum, req.value, req.currLine, req.maxDimSize);
            }
        }
    }

    return wasFoundForLoop;
}

static void mapArrayRef(SgStatement* currentSt, SgExpression* currExp, 
                        const vector<SgForStmt*>& parentLoops, vector<set<string>>& privatesVarsForLoop,
                        const int side, const int lineNum,
                        map<SgForStmt*, map<SgSymbol*, ArrayInfo>>& loopInfo,
                        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, const set<string> &privatesVars, 
                         vector<set<string>>& privatesVarsForLoop, map<int, LoopGraph*> &sortedLoopGraph, 
                         const map<string, vector<SgExpression*>> &commonBlocks,
                         const map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>> &declaredArrays, 
                         bool wasDistributedArrayRef, 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))
    {
        //... and current array is not in private list
        if (sharedMemoryParallelization || 
            privatesVars.find(string(OriginalSymbol(currExp->symbol())->identifier())) == privatesVars.end())
        {
            if (wasDistributedArrayRef)
            {
                int depth = 1;
                for (int i = parentLoops.size() - 1; i >= 0; --i, ++depth)
                {
                    auto itLoop = sortedLoopGraph.find(parentLoops[i]->lineNumber());
                    if (itLoop == sortedLoopGraph.end())
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                    if (itLoop->second->perfectLoop != depth)
                        break;

                    if (!(sharedMemoryParallelization && side == RIGHT))
                        itLoop->second->hasIndirectAccess = true;
                }
                mapArrayRef(currentSt, currExp, parentLoops, privatesVarsForLoop, side, lineNum, loopInfo, sortedLoopGraph,
                            notMappedDistributedArrays, mappedDistrbutedArrays, reg, currentW, arrayLinksByFuncCalls);                
            }
            else
            {
                wasDistributedArrayRef = true;
                mapArrayRef(currentSt, currExp, parentLoops, privatesVarsForLoop, side, lineNum, loopInfo, sortedLoopGraph,
                            notMappedDistributedArrays, mappedDistrbutedArrays, reg, currentW, arrayLinksByFuncCalls);
            }
        }
        else if (currRegime == DATA_DISTR && side == LEFT)
        {
            auto symb = OriginalSymbol(currExp->symbol());
            SgStatement *decl = declaratedInStmt(symb);
            auto uniqKey = getUniqName(commonBlocks, decl, symb);

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

            //TODO: array access to non distributed arrays, add CONSISTENT
            if (itFound->second.first->GetDistributeFlagVal() != DIST::DISTR)
            {
                set<string> loopsPrivates;
                set<Symbol*> loopsPrivatesS;
                set<string> loopsRedUnited;
                map<string, set<Symbol*>> loopsReductions;
                map<string, set<tuple<Symbol*, Symbol*, int>>> loopsReductionsLoc;

                
                for (int z = 0; z < parentLoops.size(); ++z)
                {
                    auto& loop = parentLoops[z];
                    for (auto &data : getAttributes<SgStatement*, SgStatement*>(loop, set<int>{ SPF_ANALYSIS_DIR }))
                    {
                        fillPrivatesFromComment(new Statement(data), loopsPrivatesS);
                        for (auto& elem : loopsPrivatesS)
                            loopsPrivates.insert(elem->GetOriginal()->identifier());
                        fillReductionsFromComment(new Statement(data), loopsReductions);
                        fillReductionsFromComment(new Statement(data), loopsReductionsLoc);
                    }
                }

                for (auto &elem : loopsReductions)
                {
                    for (auto &setElem : elem.second)
                    {
                        loopsPrivates.insert(setElem->GetOriginal()->identifier());
                        loopsRedUnited.insert(setElem->GetOriginal()->identifier());
                    }
                }

                for (auto &elem : loopsReductionsLoc)
                {
                    for (auto &setElem : elem.second)
                    {
                        loopsPrivates.insert(get<0>(setElem)->GetOriginal()->identifier());
                        loopsPrivates.insert(get<1>(setElem)->GetOriginal()->identifier());
                        loopsRedUnited.insert(get<0>(setElem)->GetOriginal()->identifier());
                        loopsRedUnited.insert(get<1>(setElem)->GetOriginal()->identifier());
                    }
                }

                const string key = string(OriginalSymbol(currExp->symbol())->identifier());
                if (loopsPrivates.find(key) == loopsPrivates.end())
                {
                    for (auto& loop : parentLoops)
                    {
                        __spf_print(1, "WARN: write to non distributed array '%s' in loop on line %d\n", symb->identifier(), loop->lineNumber());

                        wstring messageE, messageR;
                        __spf_printToLongBuf(messageE, L"write to non distributed array '%s' in this loop", to_wstring(symb->identifier()).c_str());

                        __spf_printToLongBuf(messageR, R61, to_wstring(symb->identifier()).c_str());

                        if (loop->lineNumber() > 0)
                            currMessages->push_back(Messages(WARR, loop->lineNumber(), messageR, messageE, 1026));
                        sortedLoopGraph[loop->lineNumber()]->hasWritesToNonDistribute = true;
                    }
                }

                if (loopsPrivates.find(key) != loopsPrivates.end() || loopsRedUnited.find(key) != loopsRedUnited.end())
                {
                    auto currOrigArrayS = OriginalSymbol(currExp->symbol());
                    if (currOrigArrayS->type()->variant() == T_ARRAY)
                    {
                        DIST::Array* currArray = getArrayFromDeclarated(declaratedInStmt(currOrigArrayS), currOrigArrayS->identifier());
                        checkNull(currArray, convertFileName(__FILE__).c_str(), __LINE__);
                        {
                            set<DIST::Array*> realArrayRefs;
                            getRealArrayRefs(currArray, currArray, realArrayRefs, arrayLinksByFuncCalls);

                            for (auto& array : realArrayRefs)
                                array->SetPrivateInLoopStatus(true);
                        }
                    }
                }
            }
        }
        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, privatesVars, privatesVarsForLoop, sortedLoopGraph,
                             commonBlocks, declaredArrays, wasDistributedArrayRef, notMappedDistributedArrays, 
                             mappedDistrbutedArrays, currentSt, reg, currentW, arrayLinksByFuncCalls);
            }
            needToContinue = false;
        }
    }
    
    if (needToContinue)
    {
        if (currExp->lhs())
            findArrayRef(parentLoops, currExp->lhs(), lineNum, nextSide, loopInfo, privatesVars, privatesVarsForLoop, sortedLoopGraph,
                         commonBlocks, declaredArrays, wasDistributedArrayRef, notMappedDistributedArrays, 
                         mappedDistrbutedArrays, currentSt, reg, currentW, arrayLinksByFuncCalls);
        if (currExp->rhs())
            findArrayRef(parentLoops, currExp->rhs(), lineNum, nextSide, loopInfo, privatesVars, privatesVarsForLoop, sortedLoopGraph,
                         commonBlocks, declaredArrays, wasDistributedArrayRef, notMappedDistributedArrays,
                         mappedDistrbutedArrays, currentSt, reg, currentW, arrayLinksByFuncCalls);
    }
}

#define FIRST(x)  get<0>(x)
#define SECOND(x) get<1>(x)
#define THIRD(x)  get<2>(x)

map<string, string> shortFileNames;
static int uniqfileNames = 0;

string getShortName(const tuple<int, string, string> &uniqKey)
{
    const char *declFileName = SECOND(uniqKey).c_str();
    const char *varName = THIRD(uniqKey).c_str();
    const int position = FIRST(uniqKey);

    map<string, string>::iterator it;
    it = shortFileNames.find(string(declFileName));

    string retVal = "";
    if (it == shortFileNames.end())
    {
        retVal = string("f") + std::to_string(uniqfileNames) + string("_");
        shortFileNames[string(declFileName)] = retVal;
        uniqfileNames++;
    }
    else
        retVal = it->second;
    return retVal + std::to_string(position) + string("_") + string(varName);
}

static int fillSizes(SgExpression *res, int &left, int &right)
{
    int err = 0;
    if (res->lhs()->variant() == INT_VAL)
        left = res->lhs()->valueInteger();
    else if (res->lhs()->variant() == MINUS_OP)
        left = -1 * res->lhs()->lhs()->valueInteger();
    else
        err = -1;

    if (res->rhs()->variant() == INT_VAL)
        right = res->rhs()->valueInteger();
    else if (res->rhs()->variant() == MINUS_OP)
        right = -1 * res->rhs()->lhs()->valueInteger();
    else
        err = -1;

    return err;
}

static pair<Expression*, Expression*> getElem(SgExpression *exp)
{
    if (exp->lhs() && exp->rhs())
        return make_pair(new Expression(exp->lhs()), new Expression(exp->rhs()));
    else if (exp->lhs())
        return make_pair(new Expression(exp->lhs()), (Expression*)NULL);
    else if (exp->rhs())
        return make_pair((Expression*)NULL, new Expression(exp->rhs()));
    else
        return make_pair((Expression*)NULL, (Expression*)NULL);
}

static void fillIdsFromEx(SgExpression* ex, set<string>& ids)
{
    if (ex)
    {
        if (ex->variant() == VAR_REF)
            ids.insert(ex->symbol()->identifier());

        fillIdsFromEx(ex->lhs(), ids);
        fillIdsFromEx(ex->rhs(), ids);
    }
}

static void doReplacement(SgExpression *ex, const map<string, int> &idsToIdx, const map<int, set<int>> &values)
{
    if (ex)
    {
        if (ex->lhs() && ex->lhs()->variant() == VAR_REF)
        {
            string key = ex->lhs()->symbol()->identifier();
            auto it = idsToIdx.find(key);
            if (it != idsToIdx.end())
            {
                if (values.find(it->second) != values.end())
                {
                    const int value = *values.find(it->second)->second.begin();
                    ex->setLhs(*new SgValueExp(value));
                }
            }
        }

        if (ex->rhs() && ex->rhs()->variant() == VAR_REF)
        {
            string key = ex->rhs()->symbol()->identifier();
            auto it = idsToIdx.find(key);
            if (it != idsToIdx.end())
            {
                const int value = *values.find(it->second)->second.begin();
                ex->setRhs(*new SgValueExp(value));
            }
        }

        doReplacement(ex->lhs(), idsToIdx, values);
        doReplacement(ex->rhs(), idsToIdx, values);
    }
}

static SgExpression* replaceConstatantProcedurePars(SgExpression *dimList, SgStatement *proc, const map<string, vector<FuncInfo*>>& allFuncInfo)
{
    if (proc == NULL)
        return dimList;
    
    if (allFuncInfo.size() == 0)
        return dimList;

    const string procN = proc->symbol()->identifier();
    map<string, FuncInfo*> mapFunc;
    createMapOfFunc(allFuncInfo, mapFunc);

    auto it = mapFunc.find(procN);
    if (it == mapFunc.end())
        return dimList;

    FuncInfo* currF = it->second;
    if (currF->funcParams.countOfPars == 0)
        return dimList;

    set<string> ids;
    fillIdsFromEx(dimList, ids);

    if (ids.size() == 0)
        return dimList;

    set<int> idxFound;
    map<string, int> idsToIdx;
    for (int z = 0; z < currF->funcParams.countOfPars; ++z)
    {
        if (ids.find(currF->funcParams.identificators[z]) != ids.end())
        {
            idxFound.insert(z);
            idsToIdx[currF->funcParams.identificators[z]] = z;
        }
    }
    
    if (idxFound.size() == 0 || currF->callsTo.size() == 0)
        return dimList;
    
    map<int, set<int>> values;

    //TODO: many call levels of functions
    for (int z = 0; z < currF->callsTo.size(); ++z)
    {
        FuncInfo* callOfThis = currF->callsTo[z];
        for (int p = 0; p < callOfThis->callsFromDetailed.size(); ++p)
        {
            if (callOfThis->callsFromDetailed[p].detailCallsFrom.first == procN)
            {
                for (auto& par : idxFound)
                {
                    auto parType = callOfThis->callsFromDetailed[p].actualParams.parametersT[par];
                    if (parType != SCALAR_INT_T)
                        return dimList;
                    else
                    {
                        if (callOfThis->callsFromDetailed[p].actualParams.parameters[par] == NULL)
                            return dimList;
                        values[par].insert(((int*)(callOfThis->callsFromDetailed[p].actualParams.parameters[par]))[0]);
                    }
                }
            }
        }
    }

    for (auto& elem : values)
        if (elem.second.size() != 1)
            return dimList;

    doReplacement(dimList, idsToIdx, values);
    return dimList;
}

vector<pair<Expression*, Expression*>> getArraySizes(vector<pair<int, int>> &sizes, SgSymbol *symb, SgStatement *decl, 
                                                     const map<DIST::Array*, set<DIST::Array*>> &arrayLinksByFuncCalls,
                                                     const map<string, vector<FuncInfo*>> &allFuncInfo)
{
    SgArrayType *type = isSgArrayType(symb->type());
    vector<pair<Expression*, Expression*>> retVal;

    if (type != NULL)
    {
        SgExpression *dimList = type->getDimList()->copyPtr();
        int consistInAllocates = 0;
        set<string> allocValues;
        SgExpression *alloc = NULL;

        
        dimList = replaceConstatantProcedurePars(dimList, getFuncStat(decl, { MODULE_STMT, BLOCK_DATA }), allFuncInfo);

        while (dimList)
        {
            SgExpression *res = ReplaceArrayBoundSizes(dimList->lhs()->copyPtr());
            if (res && res->variant() == INT_VAL)
            {
                sizes.push_back(make_pair(1, res->valueInteger()));
                retVal.push_back(make_pair((Expression*)NULL, new Expression(dimList->lhs())));
            }
            else if (res && res->variant() == DDOT)
            {
                int err, tmpRes;
                if (res->lhs())
                {
                    err = CalculateInteger(res->lhs(), tmpRes);
                    if (err != -1)
                        res->setLhs(new SgValueExp(tmpRes));
                }

                if (res->rhs())
                {
                    err = CalculateInteger(res->rhs(), tmpRes);
                    if (err != -1)
                        res->setRhs(new SgValueExp(tmpRes));
                }

                int left, right;
                bool ok = res->lhs() && res->rhs();
                if (ok)
                {
                    int err = fillSizes(res, left, right);
                    ok = (err == 0);
                }

                if (ok)
                {
                    sizes.push_back(make_pair(left, right));
                    retVal.push_back(getElem(dimList->lhs()));
                }
                else
                {
                    if (alloc == NULL)
                    {
                        for (auto &data : getAttributes<SgStatement*, SgStatement*>(decl, set<int>{ ALLOCATE_STMT }))
                        {
                            if (data->variant() != ALLOCATE_STMT)
                                continue;

                            //TODO:
                            if (string(data->fileName()) != current_file->filename())
                                continue;

                            SgExpression *list = data->expr(0);
                            while (list)
                            {
                                SgArrayRefExp *arrayRef = isSgArrayRefExp(list->lhs());
                                if (arrayRef != NULL)
                                {
                                    SgSymbol *origS = OriginalSymbol(arrayRef->symbol());
                                    DIST::Array *currArray = getArrayFromDeclarated(declaratedInStmt(origS), origS->identifier());

                                    string toCmp = string(origS->identifier());
                                    //TODO: extend 
                                    if (currArray && currArray->GetLocation().first == DIST::l_PARAMETER)
                                    {
                                        auto it = arrayLinksByFuncCalls.find(currArray);
                                        if (it != arrayLinksByFuncCalls.end())
                                        {
                                            bool found = false;
                                            for (auto &elem : it->second)
                                            {
                                                if (elem->GetLocation().first != DIST::l_PARAMETER)
                                                {
                                                    if (elem->GetShortName() == string(symb->identifier()))
                                                    {
                                                        consistInAllocates++;
                                                        alloc = list->lhs()->lhs();
                                                        allocValues.insert(alloc->unparse());
                                                        found = true;
                                                        break;
                                                    }
                                                }
                                            }
                                            if (found)
                                                break;
                                        }
                                    }
                                    else
                                    {
                                        if (toCmp == string(symb->identifier()))
                                        {
                                            consistInAllocates++;
                                            alloc = list->lhs()->lhs();
                                            allocValues.insert(alloc->unparse());
                                            break;
                                        }
                                    }
                                }
                                list = list->rhs();
                            }
                        }
                    }
                    else // set next in list 
                        alloc = alloc->rhs();

                    //TODO: dont check string representations of alloc expression!! check integer result, if all strings are equal
                    if (consistInAllocates != 1 && allocValues.size() != 1)
                    {
                        sizes.push_back(make_pair(-1, -1));
                        retVal.push_back(make_pair((Expression*)NULL, (Expression*)NULL));
                    }
                    else
                    {
                        SgExpression *result = ReplaceArrayBoundSizes(alloc->lhs()->copyPtr());
                        if (result->lhs())
                        {
                            err = CalculateInteger(result->lhs(), tmpRes);
                            if (err != -1)
                                result->setLhs(new SgValueExp(tmpRes));
                        }

                        if (result->rhs())
                        {
                            err = CalculateInteger(result->rhs(), tmpRes);
                            if (err != -1)
                                result->setRhs(new SgValueExp(tmpRes));
                        }

                        if (result->variant() == INT_VAL)
                        {
                            sizes.push_back(make_pair(1, result->valueInteger()));
                            retVal.push_back(make_pair((Expression*)NULL, new Expression(alloc->lhs())));
                        }
                        else if (result->variant() == DDOT)
                        {
                            retVal.push_back(getElem(alloc->lhs()));
                            int left = 0, right = 0;
                            bool ok = result->lhs() && result->rhs();

                            if (ok)
                            {
                                int err = fillSizes(result, left, right);
                                ok = (err == 0);
                            }

                            if (ok)
                                sizes.push_back(make_pair(left, right));
                            else
                                sizes.push_back(make_pair(-1, -1));
                        }
                        else
                        {
                            sizes.push_back(make_pair(-1, -1));
                            retVal.push_back(make_pair((Expression*)NULL, (Expression*)NULL));
                        }
                    }
                }
            }
            else
            {
                sizes.push_back(make_pair(-1, -1));
                retVal.push_back(make_pair((Expression*)NULL, (Expression*)NULL));
            }
            dimList = dimList->rhs();
        }
    }

    return retVal;
}

void recalculateArraySizes(set<DIST::Array*> &arraysDone, const set<DIST::Array*> &allArrays, 
                           const map<DIST::Array*, set<DIST::Array*>> &arrayLinksByFuncCalls,
                           const map<string, vector<FuncInfo*>> &allFuncInfo)
{
    for (auto& array : allArrays)
    {
        auto itF = arraysDone.find(array);
        if (itF != arraysDone.end())
            continue;

        itF = arraysDone.insert(itF, array);
        if (array->IsTemplate())
            continue;

        SgSymbol* symb = array->GetDeclSymbol()->GetOriginal();
        if (!symb)
            continue;
        
        auto& sizeInfo = array->GetSizes();
        bool needToUpdate = false;
        for (auto& elem : sizeInfo)
        {
            if (elem.first == elem.second)
            {
                needToUpdate = true;
                break;
            }
        }

        if (!needToUpdate)
            continue;

        auto& declInfo = array->GetDeclInfo();
        bool wasSelect = false;

        vector<int> files;
        pair<string, int> mainDecl;
        for (auto& elem : declInfo)
        {
            int fileId = SgFile::switchToFile(elem.first);
            if (fileId != -1)
            {
                files.push_back(fileId);
                mainDecl = elem;

                SgFile* tmpfile = &(CurrentProject->file(fileId));
                wasSelect = true;
                break;
            }
        }

        if (!wasSelect)
        {
            //try to find in includes
            for (int i = CurrentProject->numberOfFiles() - 1; i >= 0; --i)
            {
                SgFile* file = &(CurrentProject->file(i));

                for (SgStatement* st = file->firstStatement(); st; st = st->lexNext())
                {
                    for (auto& elem : declInfo)
                    {
                        if (make_pair(string(st->fileName()), st->lineNumber()) == elem)
                        {
                            wasSelect = true;
                            break;
                        }
                    }

                    if (wasSelect)
                    {
                        //wasSelect = false;
                        SgStatement* decl = declaratedInStmt(symb);
                        vector<pair<int, int>> sizes;
                        auto sizesEx = getArraySizes(sizes, symb, decl, arrayLinksByFuncCalls, allFuncInfo);
                        array->SetSizes(sizes);
                        array->SetSizesExpr(sizesEx);
                    }
                }

                if (wasSelect)
                    break;
            }

            if (!wasSelect)
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        }
        else
        {
            if (wasSelect)
            {
                if (array->GetLocation().first == DIST::l_MODULE)
                {
                    files.clear();
                    for (int i = CurrentProject->numberOfFiles() - 1; i >= 0; --i)
                        files.push_back(i);
                }

                for (auto& file : files)
                {
                    CurrentProject->file(file);
                    SgStatement* decl = NULL;
                    string currF = current_file->filename();

                    if (array->GetLocation().first == DIST::l_MODULE)
                    {
                        SgStatement* st = current_file->firstStatement();
                        while (st)
                        {
                            if (st->lineNumber() == mainDecl.second && st->fileName() == mainDecl.first)
                            {
                                decl = st;
                                symb = NULL;

                                SgExpression* ex = decl->expr(0);
                                while (ex)
                                {
                                    if (ex->lhs() && isArrayRef(ex->lhs()))
                                    {
                                        auto s = ex->lhs()->symbol();
                                        if (s && s->identifier() == array->GetShortName())
                                        {
                                            symb = s;
                                            break;
                                        }
                                    }
                                    ex = ex->rhs();
                                }
                                break;
                            }
                            st = st->lexNext();
                        }
                    }
                    else
                    {
                        if (symb->getFileId() != current_file_id)
                        {
                            SgStatement* st = current_file->firstStatement();
                            int lastLine = 1;
                            while (st)
                            {
                                if (st->lineNumber() > lastLine)
                                    lastLine = st->lineNumber();
                                st = st->lexNext();
                            }
                            symb = array->GetDeclSymbol(currF, make_pair(1, lastLine), getAllFilesInProject())->GetOriginal();
                            
                        }
                        
                        decl = declaratedInStmt(symb, NULL, false);
                    }

                    if (decl == NULL)
                        continue;
                    if (symb == NULL)
                    {
                        decl->unparsestdout();
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                    }

                    vector<pair<int, int>> sizes;
                    auto sizesEx = getArraySizes(sizes, symb, decl, arrayLinksByFuncCalls, allFuncInfo);
                    if (array->GetDimSize() != sizes.size())
                    {
                        sizes.clear();
                        auto sizesEx = getArraySizes(sizes, symb, decl, arrayLinksByFuncCalls, allFuncInfo);
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                    }

                    array->SetSizes(sizes);
                    array->SetSizesExpr(sizesEx);

                    bool needToContinue = false;
                    for (auto& elem : sizes)
                    {
                        if (elem.first == elem.second)
                        {
                            needToContinue = true;
                            break;
                        }
                    }

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

bool isIntrinsic(const char *funName)
{
    if (intrinsicF.find(funName) == intrinsicF.end())
        return false;
    else
        return true;
}

static set<string> getPrivatesFromModule(SgStatement *mod, 
                                         const map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>> &declaredArrays,
                                         const map<SgStatement*, set<tuple<int, string, string>>> &declaratedArraysSt,
                                         const map<string, SgStatement*> &modulesByName)
{
    set<string> privates;
    SgStatement *end = mod->lastNodeOfStmt();
    while (mod != end && mod->lineNumber() > 0)
    {
        if (mod->variant() == CONTAINS_STMT)
            break;

        if (mod->variant() == USE_STMT)
        {
            auto itF = modulesByName.find(mod->symbol()->identifier());
            if (itF == modulesByName.end())
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            const set<string> recPrivates = getPrivatesFromModule(itF->second, declaredArrays, declaratedArraysSt, modulesByName);
            for (auto it = recPrivates.begin(); it != recPrivates.end(); ++it)
                privates.insert(*it);
        }
        else
        {
            tryToFindPrivateInAttributes(mod, privates);
            fillNonDistrArraysAsPrivate(mod, declaredArrays, declaratedArraysSt, privates);
        }
        mod = mod->lexNext();
    }
    return privates;
}

extern map<DIST::Array*, std::tuple<int, string, string>> tableOfUniqNamesByArray;
static void convertOneLoop(LoopGraph *currLoop, map<LoopGraph*, map<DIST::Array*, ArrayInfo*>> &outInfo,
                           const map<SgSymbol*, ArrayInfo> &toConvert,
                           const set<string> &privateArrays,
                           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();

        if (privateArrays.find(symbIdent) == privateArrays.end() || sharedMemoryParallelization)
        {
            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;

            if (!sharedMemoryParallelization && arrayToAdd->IsNotDistribute() == true)
                continue;

            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*>> 
convertLoopInfo(const map<SgForStmt*, map<SgSymbol*, ArrayInfo>> &loopInfo,
                const map<int, LoopGraph*> &sortedLoopGraph,
                const set<string> &privateArrays,
                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__);
        
        convertOneLoop(itGraph->second, outInfo, it->second, privateArrays, commonBlocks, declaredArrays, arrayLinksByFuncCalls, createdArrays); 
    }

    return outInfo;
}

inline static void fillPrivatesFromDecl(SgExpression *ex, set<SgSymbol*> &delcsSymbViewed, set<SgStatement*> &delcsStatViewed,
                                        const map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>> &declaredArrays,
                                        const map<SgStatement*, set<tuple<int, string, string>>> &declaratedArraysSt,
                                        set<string> &privatesVars)
{
    if (!ex)
        return;

    if (isArrayRef(ex))
    {
        SgSymbol *symb = ex->symbol();
        if (symb->type())
        {
            if (symb->type()->variant() == T_ARRAY)
            {
                SgSymbol *s = ex->symbol();
                auto it = delcsSymbViewed.find(s);
                if (it == delcsSymbViewed.end())
                {
                    delcsSymbViewed.insert(it, s);
                    SgStatement *decl = declaratedInStmt(s);

                    auto itD = delcsStatViewed.find(decl);
                    if (itD == delcsStatViewed.end())
                    {
                        delcsStatViewed.insert(itD, decl);
                        tryToFindPrivateInAttributes(decl, privatesVars);
                        fillNonDistrArraysAsPrivate(decl, declaredArrays, declaratedArraysSt, privatesVars);
                    }
                }
            }
        }
    }

    fillPrivatesFromDecl(ex->rhs(), delcsSymbViewed, delcsStatViewed, declaredArrays, declaratedArraysSt, privatesVars);
    fillPrivatesFromDecl(ex->lhs(), delcsSymbViewed, delcsStatViewed, declaredArrays, declaratedArraysSt, privatesVars);
}

static void changeLoopWeight(double &currentWeight, const map<int, LoopGraph*> &sortedLoopGraph, const int line, bool increase = true)
{
    auto loopIt = sortedLoopGraph.find(line);
    if (loopIt == sortedLoopGraph.end())
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    if (increase)
        currentWeight *= loopIt->second->countOfIters;
    else
        currentWeight /= loopIt->second->countOfIters;
}

static bool hasNonPureFunctions(SgExpression *ex, LoopGraph *loopRef, vector<Messages> &messagesForFile, const int line, const map<string, FuncInfo*> &funcByName)
{
    bool retVal = false;

    if (ex == NULL)
        return retVal;

    if (ex->variant() == FUNC_CALL)
    {
        if (isIntrinsicFunctionName(ex->symbol()->identifier()) == 0)
        {
            auto itF = funcByName.find(ex->symbol()->identifier());
            bool isPure = false; 
            if (itF != funcByName.end())
                isPure = itF->second->isPure;

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

                messagesForFile.push_back(Messages(WARR, line, R79, L"Only pure procedures were supported", 1044));

            }
        }
    }
    bool retL = false, retR = false;
    if (ex->lhs())
        retL = hasNonPureFunctions(ex->lhs(), loopRef, messagesForFile, line, funcByName);
    if (ex->rhs())
        retR = hasNonPureFunctions(ex->rhs(), loopRef, messagesForFile, line, funcByName);

    return retVal || retL || retR;
}

void fillFromModule(SgSymbol* s, const map<string, set<string>>& privatesByModule, set<string>& privates)
{
    if (s)
    {
        auto it = privatesByModule.find(s->identifier());
        if (it != privatesByModule.end())
            privates.insert(it->second.begin(), it->second.end());
    }
}

static SgStatement* takeOutConditions(stack<SgExpression*>& conditions, stack<SgStatement*>& ifBlocks, SgStatement* st)
{
    auto res = createIfConditions(conditions, ifBlocks, st);

    auto before = st->lexPrev();
    for (auto& elem : res)
        if (elem)
            st->insertStmtBefore(*elem, *st->controlParent());

    SgLabel* lab = st->label();
    if (lab) // move lab to first condition
    {
        st->deleteLabel(true);
        if (res.size() == 0)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        res[0]->setLabel(*lab);
    }
    return before;
}

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

void loopAnalyzer(SgFile *file, vector<ParallelRegion*> &regions, map<tuple<int, string, string>, DIST::Array*> &createdArrays,
                  vector<Messages> &messagesForFile, REGIME regime, 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,
                  bool skipDeps, vector<LoopGraph*> *loopGraph)
{
    currMessages = &messagesForFile;
    currRegime = regime;

    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, set<string>> privatesByModule;
    if(!sharedMemoryParallelization)
        for (int i = 0; i < modules.size(); ++i)
            privatesByModule[modules[i]->symbol()->identifier()] = getPrivatesFromModule(modules[i], declaredArrays, declaratedArraysSt, modulesByName);

    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();
        sendMessage_2lvl(0, (file->functions(i)->variant() != MODULE_STMT), fName);

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

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

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

        set<string> privatesVars;

        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);
        }

        if(!sharedMemoryParallelization)
        {
            SgStatement* tmpModFind = st;
            while (tmpModFind->variant() != GLOBAL)
            {
                tmpModFind = tmpModFind->controlParent();
                if (tmpModFind->variant() == MODULE_STMT)
                    fillFromModule(tmpModFind->symbol(), privatesByModule, privatesVars);
            }
        }

        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;

        const ParallelRegionLines* prevParLines = NULL;
        double prevLinesWeight = 1.0;
        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();
            auto regAndLines = getRegionAndLinesByLine(regions, st->fileName(), currentLine);

            auto *currReg = regAndLines.first;
            auto *parLines = regAndLines.second;
            if (currReg == NULL)
            {
                st = st->lexNext();
                continue;
            }

            if (parLines != prevParLines)
            {
                prevParLines = parLines;
                auto newParLinesWeight = parLines ? parLines->weight : 1.0;

                if (prevParLines)
                    currentWeight /= prevLinesWeight;

                if (parLines)
                    currentWeight *= newParLinesWeight;

                prevLinesWeight = newParLinesWeight;
                prevParLines = parLines;
            }

            if (isSgExecutableStatement(st) == NULL)
                delcsStatViewed.insert(st);
            else if (!sharedMemoryParallelization && 
                     !isDVM_stat(st) && !isSPF_stat(st))
                for (int i = 0; i < 3; ++i)
                    fillPrivatesFromDecl(st->expr(i), delcsSymbViewed, delcsStatViewed, declaredArrays, declaratedArraysSt, privatesVars);

            //printf("new st with var = %d, on line %d\n", st->variant(), st->lineNumber());
            const int currV = st->variant();
            if (currV == FOR_NODE)
            {
                if(!sharedMemoryParallelization)
                {
                    tryToFindPrivateInAttributes(st, privatesVars);
                    fillNonDistrArraysAsPrivate(st, declaredArrays, declaratedArraysSt, 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);

                if (regime == REMOTE_ACC)
                {
                    SgStatement *prev = st->lexPrev();
                    if (prev)
                    {
                        if (prev->variant() == DVM_PARALLEL_ON_DIR)
                        {
                            auto it = sortedLoopGraph.find(st->lineNumber());
                            if (it == sortedLoopGraph.end())
                                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                            under_dvm_dir = new pair<SgForStmt*, LoopGraph*>((SgForStmt*)st, it->second);
                        }
                    }
                }
            }
            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;
                        if(!sharedMemoryParallelization)
                            for (auto &privVars : privatesVars)
                                if (unitedPrivates.find(privVars) == unitedPrivates.end())
                                    setDiff.insert(privVars);                        

                        allLoops[contrlParent->lineNumber()] = make_pair((SgForStmt*)contrlParent, make_pair(unitedPrivates, setDiff));
                        parentLoops.pop_back();
                        privatesVarsForLoop.pop_back();

                        if (regime == REMOTE_ACC)
                        {
                            if (under_dvm_dir)
                            {
                                if (contrlParent == under_dvm_dir->first && under_dvm_dir->second->userDvmDirective == NULL)
                                {
                                    ParallelRegion *currReg = getRegionByLine(regions, under_dvm_dir->first->fileName(), under_dvm_dir->first->lineNumber());

                                    if (currReg)
                                    {
                                        const DIST::Arrays<int> &allArrays = currReg->GetAllArrays();
                                        DIST::GraphCSR<int, double, attrType> &reducedG = currReg->GetReducedGraphToModify();
                                        const DataDirective &data = currReg->GetDataDir();
                                        const vector<int> &currVar = currReg->GetCurrentVariant();

                                        const pair<LoopGraph*, const ParallelDirective*> currDir = make_pair(under_dvm_dir->second, under_dvm_dir->second->directive);
                                        auto convertedLoopInfo = convertLoopInfo(loopInfo, sortedLoopGraph, privatesVars, commonBlocks, declaredArrays, arrayLinksByFuncCalls, createdArrays);

                                        auto uniqRemotes = createRemoteInParallel(currDir, allArrays, convertedLoopInfo, reducedG, data, currVar, messagesForFile, currReg->GetId(), arrayLinksByFuncCalls, funcByName);
                                        addRemotesToDir(under_dvm_dir, uniqRemotes);
                                    }

                                    delete under_dvm_dir;
                                    under_dvm_dir = NULL;
                                }
                            }
                        }
                    }
                }
                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, privatesVars, privatesVarsForLoop, 
                                 sortedLoopGraph, commonBlocks, declaredArrays, false, notMappedDistributedArrays, 
                                 mappedDistrbutedArrays, st, currReg, currentWeight, arrayLinksByFuncCalls);
                if (st->expr(1))
                    findArrayRef(parentLoops, st->expr(1), st->lineNumber(), RIGHT, loopInfo, privatesVars, privatesVarsForLoop,
                                 sortedLoopGraph, commonBlocks, declaredArrays, false, notMappedDistributedArrays, 
                                 mappedDistrbutedArrays, st, currReg, currentWeight, arrayLinksByFuncCalls);

                if (regime == REMOTE_ACC)
                {
                    if (st->expr(1))
                    {
                        bool cond = true;
                        if (st->expr(1)->variant() == FUNC_CALL)
                        {
                            string fName = ((SgFunctionCallExp*)st->expr(1))->funName()->identifier();
                            cond = (fName != "allocated");
                        }

                        // detect copy operator: A(:, :, :) = B(:, :, :)
                        if (st->expr(1)->variant() == ARRAY_REF && st->expr(0)->variant() == ARRAY_REF)
                        {
                            set<SgSymbol*> ddots;
                            vector<SgExpression*> dummy;
                            fillVars(st->expr(0), { DDOT }, ddots, dummy);
                            fillVars(st->expr(1), { DDOT }, ddots, dummy);

                            if (ddots.size() != 0)
                                cond = false;
                        }

                        // detect copy operator: A = B
                        if (st->expr(1)->variant() == ARRAY_REF && st->expr(0)->variant() == ARRAY_REF)
                        {
                            SgExpression *left = st->expr(0);
                            SgExpression *right = st->expr(1);
                            if (left->lhs() == NULL && left->rhs() == NULL &&
                                right->lhs() == NULL && right->rhs() == NULL)
                                cond = false;
                        }

                        // detect operator: A[(:,:,:)] = F(X[(:,:,:)], Y[(:,:,:)], Z[(:,:,:)], ...)
                        //TODO: check right part
                        if (st->expr(0)->variant() == ARRAY_REF)
                        {
                            SgExpression *left = st->expr(0);
                            set<SgSymbol*> ddots;
                            vector<SgExpression*> dummy;
                            fillVars(st->expr(0), { DDOT }, ddots, dummy);

                            if ((left->lhs() == NULL && left->rhs() == NULL ) || ddots.size() != 0)
                                cond = false;
                        }

                        if (cond)
                        {
                            const DIST::Arrays<int> &allArrays = currReg->GetAllArrays();
                            if (under_dvm_dir == NULL)
                                createRemoteDir<1>(st, funcByName, sortedLoopGraph, allArrays, currReg->GetDataDir(), currReg->GetCurrentVariant(), currReg->GetId(), *currMessages, arrayLinksByFuncCalls);
                        }
                    }
                    
                    if (st->expr(0))
                    {
                        const DIST::Arrays<int>& allArrays = currReg->GetAllArrays();
                        if (under_dvm_dir == NULL)
                            createRemoteDir<0>(st, funcByName, sortedLoopGraph, allArrays, currReg->GetDataDir(), currReg->GetCurrentVariant(), currReg->GetId(), *currMessages, arrayLinksByFuncCalls);
                    }
                }
            }
            else if (currV == IF_NODE || currV == ELSEIF_NODE || currV == LOGIF_NODE || currV == SWITCH_NODE)
            {
                SgStatement* before = NULL;
                // convert IF conditions if access to dist array appears
                if (regime == REMOTE_ACC && under_dvm_dir == NULL)
                {
                    stack<SgExpression*> conditions;
                    stack<SgStatement*> ifBlocks;
                    bool needToConv = false;
                    bool prevWithSameLine = (st->lexPrev()->lineNumber() == st->lineNumber());

                    if (currV == IF_NODE)
                    {
                        needToConv = isNeedToConvertIfCondition(st->expr(0));
                        conditions.push(st->expr(0));
                        ifBlocks.push(st);

                        SgIfStmt* ifS = (SgIfStmt*)st;
                        auto body = ifS->falseBody();
                        while (body)
                        {
                            if (body->variant() == ELSEIF_NODE)
                            {
                                needToConv = needToConv || isNeedToConvertIfCondition(body->expr(0)->copyPtr());
                                conditions.push(body->expr(0));
                                ifBlocks.push(body);
                            }
                            else
                                break;
                            body = body->lastNodeOfStmt();
                        }

                        needToConv = needToConv && !prevWithSameLine;
                    }
                    else if (currV == LOGIF_NODE)
                    {
                        needToConv = isNeedToConvertIfCondition(st->expr(0)) && !prevWithSameLine;
                        conditions.push(st->expr(0));
                        ifBlocks.push(st);
                    }
                    else if (currV == SWITCH_NODE)
                    {
                        needToConv = isNeedToConvertIfCondition(st->expr(0)) && !prevWithSameLine;
                        conditions.push(st->expr(0));
                        ifBlocks.push(st);
                    }

                    if (needToConv && !conditions.empty())
                        st = before = takeOutConditions(conditions, ifBlocks, st);
                }

                if (st->expr(0))
                {
                    findArrayRef(parentLoops, st->expr(0), st->lineNumber(), RIGHT, loopInfo, privatesVars, privatesVarsForLoop,
                                 sortedLoopGraph, commonBlocks, declaredArrays, false, notMappedDistributedArrays, 
                                 mappedDistrbutedArrays, st, currReg, currentWeight, arrayLinksByFuncCalls);
                    if (regime == REMOTE_ACC)
                        if (under_dvm_dir == NULL)
                            createRemoteDir<0>(st, funcByName, sortedLoopGraph, currReg->GetAllArrays(), currReg->GetDataDir(), currReg->GetCurrentVariant(), currReg->GetId(), *currMessages, arrayLinksByFuncCalls);                    
                }

                if (regime == REMOTE_ACC && before)
                    st = before;
            }
            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, privatesVars, privatesVarsForLoop,
                                             sortedLoopGraph, commonBlocks, declaredArrays, false, notMappedDistributedArrays, 
                                             mappedDistrbutedArrays, st, currReg, currentWeight, arrayLinksByFuncCalls);
                            else
                                findArrayRef(parentLoops, par, st->lineNumber(), RIGHT, loopInfo, privatesVars, privatesVarsForLoop,
                                             sortedLoopGraph, commonBlocks, declaredArrays, false, notMappedDistributedArrays, 
                                             mappedDistrbutedArrays, st, currReg, currentWeight, arrayLinksByFuncCalls);

                            if (regime == REMOTE_ACC)
                            {
                                if (isArrayRef(par) && isPassFullArray(par))
                                {
                                    SgSymbol *s = OriginalSymbol(par->symbol());
                                    DIST::Array *inPar = getArrayFromDeclarated(declaratedInStmt(s), s->identifier());
                                    if (inPar && !inPar->IsNotDistribute())
                                    {
                                        if (func->funcParams.parametersT[z] != ARRAY_T)
                                            toRedistr.insert(inPar->GetTemplateArray(currReg->GetId()));
                                        else
                                        {
                                            if (inPar->GetDimSize() != ((DIST::Array*)func->funcParams.parameters[z])->GetDimSize())
                                                toRedistr.insert(inPar->GetTemplateArray(currReg->GetId()));
                                        }
                                    }
                                }
                            }
                        }

                        if (regime == REMOTE_ACC && list->length())
                            if (under_dvm_dir == NULL)
                                createRemoteDir<0>(st, funcByName, sortedLoopGraph, currReg->GetAllArrays(), currReg->GetDataDir(), currReg->GetCurrentVariant(), currReg->GetId(), *currMessages, arrayLinksByFuncCalls);
                    }

                    if (regime == REMOTE_ACC)
                    {
                        const DataDirective &dataDirectives = currReg->GetDataDir();
                        const vector<int> &currentVariant = currReg->GetCurrentVariant();
                        auto &tmp = dataDirectives.distrRules;
                        std::vector<std::pair<DIST::Array*, const DistrVariant*>> currentVar;
                        for (int z1 = 0; z1 < currentVariant.size(); ++z1)
                            currentVar.push_back(make_pair(tmp[z1].first, &tmp[z1].second[currentVariant[z1]]));

                        for (auto &toRed : toRedistr)
                        {
                            auto cp = st->controlParent();
                            auto redist = new SgStatement(DVM_REDISTRIBUTE_DIR);
                            auto newRule = new SgExprListExp();
                            newRule->setLhs(*new SgExpression(KEYWORD_VAL, "*"));
                            for (int z = 1; z < toRed->GetDimSize(); ++z)
                                newRule->append(*new SgExpression(KEYWORD_VAL, "*"));
                            auto templS = new SgSymbol(VARIABLE_NAME, toRed->GetShortName().c_str());
                            redist->setExpression(0, *new SgVarRefExp(*templS));
                            redist->setExpression(1, *newRule);

                            st->insertStmtBefore(*redist, *cp);
                            
                            redist = new SgStatement(DVM_REDISTRIBUTE_DIR);
                            newRule = new SgExprListExp();
                            
                            const DistrVariant *varD = NULL;
                            for (auto &elem : currentVar)
                            {
                                if (elem.first == toRed)
                                {
                                    varD = elem.second;
                                    break;
                                }
                            }

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

                            for (int z = 0; z < toRed->GetDimSize(); ++z)
                            {
                                char *type = "*";
                                if (varD->distRule[z] == distType::BLOCK)
                                    type = "BLOCK";

                                if (z == 0)
                                    newRule->setLhs(*new SgExpression(KEYWORD_VAL, type));
                                else
                                    newRule->append(*new SgExpression(KEYWORD_VAL, type));
                            }
                            redist->setExpression(0, *new SgVarRefExp(*templS));
                            redist->setExpression(1, *newRule);

                            st->insertStmtAfter(*redist, *cp);
                        }
                    }
                }
            }
            else if (currV == USE_STMT)
            {
                if (st->lineNumber() > 0 && !sharedMemoryParallelization)
                {
                    auto itF = privatesByModule.find(st->symbol()->identifier());

                    if (itF == privatesByModule.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__);
                    }

                    
                    for (auto it = itF->second.begin(); it != itF->second.end(); ++it)
                        privatesVars.insert(*it);
                }
            }
            else
            {
                if(!sharedMemoryParallelization)
                {
                    tryToFindPrivateInAttributes(st, privatesVars);
                    fillNonDistrArraysAsPrivate(st, declaredArrays, declaratedArraysSt, privatesVars);
                }

                if (isDVM_stat(st) == false && isSgExecutableStatement(st))
                {
                    if (regime == REMOTE_ACC)
                    {
                        if (st->expr(0) &&
                            (currV == SWITCH_NODE || currV == FORALL_STAT || currV == WHILE_NODE ||
                                currV == WHERE_NODE || currV == ALLDO_NODE || currV == ARITHIF_NODE ||
                                currV == ASSGOTO_NODE || currV == COMGOTO_NODE))
                        {
                            const DIST::Arrays<int> &allArrays = currReg->GetAllArrays();
                            if (under_dvm_dir == NULL)
                                createRemoteDir<0>(st, funcByName, sortedLoopGraph, allArrays, currReg->GetDataDir(), currReg->GetCurrentVariant(), currReg->GetId(), *currMessages, arrayLinksByFuncCalls);
                        }
                    }
                    else
                    {
                        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, privatesVars, privatesVarsForLoop,
                                             sortedLoopGraph, commonBlocks, declaredArrays, false, notMappedDistributedArrays,
                                             mappedDistrbutedArrays, st, currReg, currentWeight, arrayLinksByFuncCalls);
                    }
                }
            }

            st = st->lexNext();
        }

        auto convertedLoopInfo = convertLoopInfo(loopInfo, sortedLoopGraph, privatesVars, commonBlocks, declaredArrays, arrayLinksByFuncCalls, createdArrays);
        if (regime == DATA_DISTR || regime == SHARED_MEMORY_PAR)
        {
            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();

                if (!skipDeps)
                {
                    string fName = file->functions(i)->symbol()->identifier();
                    sendMessage_2lvl(1, idx, (int)convertedLoopInfo.size());

                    tryToFindDependencies(loop.first, allLoops, funcWasInit, file, regions, currMessages, collection, funcByName, defUseByPlace);
                }
            }

            vector<LoopGraph*> tmpLoops;
            map<SgSymbol*, ArrayInfo> tmpToConvert;

            if (file->functions(i)->variant() != PROG_HEDR)
            {
                for (auto &notMapped : notMappedDistributedArrays)
                {
                    if (mappedDistrbutedArrays.find(notMapped.first) == mappedDistrbutedArrays.end())
                    {
                        auto reg = getRegionByLine(regions, notMapped.second.second->fileName(), notMapped.second.second->lineNumber());
                        if (reg)
                        {
                            LoopGraph *tmpLoop = new LoopGraph();

                            tmpLoop->region = reg;
                            tmpLoop->loopType = LoopType::FOR;

                            tmpLoops.push_back(tmpLoop);

                            ArrayInfo toAdd;
                            SgSymbol *arrayS = notMapped.second.first;
                            toAdd.setDimSize(((SgArrayType*)arrayS->type())->dimension());
                                                        
                            for (int z = 0; z < toAdd.getDimSize(); ++z)
                                toAdd.readOps[z] = ArrayOp(make_pair(make_pair(1, 0), 1.0));
                            
                            tmpToConvert[arrayS] = toAdd;
                            convertOneLoop(tmpLoop, convertedLoopInfo, tmpToConvert, privatesVars, commonBlocks, declaredArrays, arrayLinksByFuncCalls, createdArrays, true);
                        }
                    }
                }
            }
            
            if(!sharedMemoryParallelization)
                addToDistributionGraph(convertedLoopInfo, arrayLinksByFuncCalls);

            for (auto &toDel : tmpLoops)
            {
                convertedLoopInfo.erase(toDel);
                delete toDel;
            }
            tmpToConvert.clear();

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

                // TODO: add messages!
                for (auto &loopLine : loopWithOutArrays)
                {
                    if (sortedLoopGraph[loopLine]->withoutDistributedArrays && loopLine > 0)
                    {
                        //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;

                        map<string, set<Symbol*>> reductions;
                        map<string, set<tuple<Symbol*, Symbol*, int>>> reductionsLoc;
                        set<Symbol*> privatesS;
                                                
                        for (auto &data : getAttributes<SgStatement*, SgStatement*>(loopSt, set<int>{ SPF_ANALYSIS_DIR, SPF_PARALLEL_DIR }))
                        {
                            Statement *dataSt = new Statement(data);
                            fillReductionsFromComment(dataSt, reductions);
                            fillReductionsFromComment(dataSt, reductionsLoc);
                            fillPrivatesFromComment(dataSt, privatesS);
                        }
                        
                        for (auto &red : reductions)
                            privatesS.insert(red.second.begin(), red.second.end());
                        for (auto &red : reductionsLoc)
                        {
                            for (auto &elem : red.second)
                            {
                                privatesS.insert(get<0>(elem));
                                privatesS.insert(get<1>(elem));
                            }
                        }

                        set<string> privates;
                        for (auto& elem : privatesS)
                            privates.insert(elem->GetOriginal()->identifier());

                        for (SgStatement *start = loopSt->lexNext(); start != loopSt->lastNodeOfStmt(); start = start->lexNext())
                        {
                            //TODO: detect write in proc calls
                            if (start->variant() == ASSIGN_STAT)
                            {
                                if (start->expr(0)->variant() == ARRAY_REF || start->expr(0)->variant() == ARRAY_OP)
                                {
                                    SgSymbol* s = NULL;
                                    if (start->expr(0)->variant() == ARRAY_REF)
                                        s = start->expr(0)->symbol();
                                    else if (start->expr(0)->variant() == ARRAY_OP)
                                        s = start->expr(0)->lhs()->symbol();

                                    if (s && privates.find(s->identifier()) == privates.end())
                                        if (sharedMemoryParallelization == 0)
                                            hasWritesToArray = true;
                                }
                            }

                            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;
                    }
                }
                if (parallizeFreeLoops)
                    selectFreeLoopsForParallelization(loopsForFunction, funcName, (regime == DATA_DISTR), regions, messagesForFile);
            }
            sendMessage_2lvl(2);
        }
        else if (regime == COMP_DISTR)
        {
            createParallelDirectives(convertedLoopInfo, regions, arrayLinksByFuncCalls, messagesForFile);
            if (parallizeFreeLoops)
                selectFreeLoopsForParallelization(loopsForFunction, funcName, (regime == DATA_DISTR), regions, messagesForFile);
        }
        
        if(regime == SHARED_MEMORY_PAR)
            createParallelDirectives(convertedLoopInfo, regions, arrayLinksByFuncCalls, messagesForFile);

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

void arrayAccessAnalyzer(SgFile *file, vector<Messages> &messagesForFile, const map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>> &declaredArrays, REGIME regime)
{
    currMessages = &messagesForFile;
    currRegime = regime;

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

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

    const map<DIST::Array*, set<DIST::Array*>> arrayLinksByFuncCalls;
    for (int i = 0; i < funcNum; ++i)
    {
        map<SgForStmt*, map<SgSymbol*, ArrayInfo>> loopInfo;
        set<string> privatesVars;
        vector<set<string>> privatesVarsForLoop;

        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();
        }

        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;
                
        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;

            const int currV = st->variant();
            if (currV == FOR_NODE)
            {
                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);
                sortedLoopGraph[st->lineNumber()] = new LoopGraph();
                privatesVarsForLoop.push_back(set<string>());
            }
            else if (currV == CONTROL_END)
            {
                SgStatement *contrlParent = st->controlParent();
                if (contrlParent)
                {
                    if (contrlParent->variant() == FOR_NODE)
                    {
                        parentLoops.pop_back();
                        delete sortedLoopGraph[contrlParent->lineNumber()];
                        sortedLoopGraph.erase(contrlParent->lineNumber());
                        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, privatesVars, privatesVarsForLoop,
                                 sortedLoopGraph, commonBlocks, declaredArrays, false, notMappedDistributedArrays, 
                                 mappedDistrbutedArrays, st, NULL, currentWeight, arrayLinksByFuncCalls);
                if (st->expr(1))
                    findArrayRef(parentLoops, st->expr(1), st->lineNumber(), RIGHT, loopInfo, privatesVars, privatesVarsForLoop,
                                 sortedLoopGraph, commonBlocks, declaredArrays, false, notMappedDistributedArrays, 
                                 mappedDistrbutedArrays, st, NULL, currentWeight, arrayLinksByFuncCalls);
            }
            else if (currV == IF_NODE || currV == ELSEIF_NODE || currV == LOGIF_NODE || currV == SWITCH_NODE)
            {
                if (st->expr(0))
                    findArrayRef(parentLoops, st->expr(0), st->lineNumber(), RIGHT, loopInfo, privatesVars, privatesVarsForLoop,
                                 sortedLoopGraph, commonBlocks, declaredArrays, false, notMappedDistributedArrays,
                                 mappedDistrbutedArrays, st, NULL, currentWeight, arrayLinksByFuncCalls);
            }
            else if (currV == PROC_STAT)
            {
                if (st->expr(0))
                {
                    if (isIntrinsicFunctionName(st->symbol()->identifier()))
                        findArrayRef(parentLoops, st->expr(0), st->lineNumber(), RIGHT, loopInfo, privatesVars, privatesVarsForLoop,
                                     sortedLoopGraph, commonBlocks, declaredArrays, false, notMappedDistributedArrays, 
                                     mappedDistrbutedArrays, st, NULL, currentWeight, arrayLinksByFuncCalls);
                    else
                    {
                        SgExpression *listEx = st->expr(0);
                        while (listEx)
                        {
                            findArrayRef(parentLoops, listEx->lhs(), st->lineNumber(), LEFT, loopInfo, privatesVars, privatesVarsForLoop,
                                         sortedLoopGraph, commonBlocks, declaredArrays, false, notMappedDistributedArrays,
                                         mappedDistrbutedArrays, st, NULL, currentWeight, arrayLinksByFuncCalls);
                            listEx = listEx->rhs();
                        }
                    }
                }
            }
            st = st->lexNext();
        }
        __spf_print(PRINT_PROF_INFO, "Function ended\n");
    }
}

//TODO: copy all functions from FDVM and fix them
static inline int getStructureSize(SgSymbol *s)
{
    return 0;
    //printInternalError(convertFileName(__FILE__).c_str(), __LINE__);       
}

static inline int getNumericTypeLength(SgType *t)
{
    SgExpression *le;
    SgValueExp *ve;
    if (t->variant() == T_STRING)  
        return (0);

    if (TYPE_RANGES(t->thetype)) 
    {
        le = t->length();
        if (ve = isSgValueExp(le))
            return ve->intValue();
        else
            return 0;
    }

    if (TYPE_KIND_LEN(t->thetype)) 
    {
        le = t->selector()->lhs();
        if (ve = isSgValueExp(le))
            if (t->variant() == T_COMPLEX || t->variant() == T_DCOMPLEX)
                return 2 * ve->intValue();
            else
                return ve->intValue();
        else
            return 0;
    }

    return 0;
}

static inline int getIntrinsicTypeSize(SgType *t)
{
    const int default_real_size = 4;
    const int default_integer_size = 4;
    switch (t->variant()) 
    {
    case T_INT:
    case T_BOOL:     
        return default_integer_size;
    case T_FLOAT:    
        return default_real_size;
    case T_COMPLEX: 
        return 2 * default_real_size;
    case T_DOUBLE:
        return 2 * default_real_size;
    case T_DCOMPLEX: 
        return 4 * default_real_size;
    case T_STRING:
    case T_CHAR:
        return 1;
    default:
        return 0;
    }
}

static SgExpression* getLengthOfKindExpr(SgType *t, SgExpression *se, SgExpression *le)
{
    switch (t->variant())
    {
    case T_INT:
    case T_FLOAT:
    case T_BOOL:
    case T_DOUBLE:
        return se->lhs();
    case T_COMPLEX:
    case T_DCOMPLEX:
        return &(*new SgValueExp(2) * (*(se->lhs())));
    case T_CHAR:
    case T_STRING:
    {
        SgExpression *length, *kind;
        if (se->rhs() && se->rhs()->variant() == LENGTH_OP)
        {
            length = se->rhs()->lhs();
            kind = se->lhs()->lhs();
        }
        else if (se->rhs() && se->rhs()->variant() != LENGTH_OP)
        {
            length = se->lhs()->lhs();
            kind = se->rhs()->lhs();
        }
        else
        {
            length = se->lhs();
            kind = NULL;
        }
        length = le ? le : length;
        if (kind)
            return &(*length * (*kind));
        else
            return length;
    }
    default:
        return(NULL);
    }
}

SgExpression* getTypeLengthExpr(SgType *t)
{
    SgExpression *len;
    SgExpression *selector;
    if (t->variant() == T_DERIVED_TYPE)
        return new SgValueExp(getStructureSize(t->symbol()));

    len = TYPE_RANGES(t->thetype) ? t->length() : NULL;
    selector = TYPE_KIND_LEN(t->thetype) ? t->selector() : NULL;

    if (!len && !selector) //the number of bytes is not specified in type declaration statement
        return (new SgValueExp(getIntrinsicTypeSize(t)));
    else if (len && !selector)   //INTEGER*2,REAL*8,CHARACTER*(N+1)
        return CalculateInteger(len->copyPtr());
    else
        return CalculateInteger(getLengthOfKindExpr(t, selector, len)->copyPtr()); //specified kind or/and len
}

int getSizeOfType(SgType *t)
{
    int len = -1;
    if (IS_INTRINSIC_TYPE(t))
        return getIntrinsicTypeSize(t);
    if (t->variant() == T_DERIVED_TYPE)  
        return getStructureSize(t->symbol());
    if (len = getNumericTypeLength(t))
        return len;

    SgExpression *le = getTypeLengthExpr(t);
    if (le->isInteger()) 
    {
        len = le->valueInteger();
        len = len < 0 ? 0 : len; //according to standard F90
    }
    else
        len = -1; //may be error situation
    return len;
}

void insertSpfAnalysisBeforeParalleLoops(const vector<LoopGraph*> &loops)
{
    for (auto &loop : loops)
    {
        SgStatement *spfStat = new SgStatement(SPF_ANALYSIS_DIR);
        spfStat->setlineNumber(loop->lineNum);
        spfStat->setLocalLineNumber(0);
        spfStat->setFileName((char*)current_file->filename());

        if (!loop->hasLimitsToParallel())
        {
            loop->loop->addAttribute(SPF_ANALYSIS_DIR, spfStat, sizeof(SgStatement));
            //uncomment it to debug private analysis
            //loop->loop->insertStmtBefore(*spfStat, *loop->loop->controlParent());
        }
        insertSpfAnalysisBeforeParalleLoops(loop->children);
    }
}
#undef PRINT_ARRAY_ARCS
#undef PRINT_LOOP_STRUCT
#undef PRINT_PROF_INFO
#undef FIRST
#undef SECOND
#undef THIRD
