#include "leak_detector.h"

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include <assert.h>
#include <string>
#include <vector>
#include <map>
#include <set>

#include "dvm.h"
#include "../Sapfor.h"
#include "graph_loops.h"
#include "graph_calls_func.h"
#include "../SageAnalysisTool/depInterfaceExt.h"
#include "SgUtils.h"
#include "errors.h"
#include "directive_parser.h"
#include "expr_transform.h"
#include "../LoopAnalyzer/loop_analyzer.h"
#include "../DirectiveProcessing/directive_omp_parser.h"

using std::string;
using std::wstring;
using std::vector;
using std::map;
using std::set;
using std::pair;
using std::tuple;

static void addToattribute(SgStatement *toAttr, SgStatement *curr, const int variant)
{
    // move SgStatement to attribute
    SgStatement *toAdd = new SgStatement(toAttr->variant(), NULL, toAttr->symbol(), toAttr->expr(0), toAttr->expr(1), toAttr->expr(2));
    toAdd->setlineNumber(toAttr->lineNumber());
    toAdd->setLocalLineNumber(SPF_USER_DIR);

    curr->addAttribute(variant, toAdd, sizeof(SgStatement));
    //copy comments to st
    if (toAttr->comments() && variant != SPF_END_PARALLEL_REG_DIR && variant != SPF_PARALLEL_REG_DIR)
    {
        string comments(toAttr->comments());

        if (curr->comments())
            comments += "\n" + string(curr->comments());
        if (comments.size() != 0)
            curr->setComments(comments.c_str());
    }
}

void fillVars(SgExpression* exp, const set<int>& types, set<string>& identifierList, vector<SgExpression*>& funcCalls)
{
    if (exp)
    {
        if (exp->variant() == FUNC_CALL)
            funcCalls.push_back(exp);

        if (types.find(exp->variant()) != types.end())
        {
            if (exp->symbol())
                identifierList.insert(OriginalSymbol(exp->symbol())->identifier());
            else if (exp->variant() == DDOT)
                identifierList.insert(exp->symbol()->identifier()); // BAD solution
        }

        fillVars(exp->lhs(), types, identifierList, funcCalls);
        fillVars(exp->rhs(), types, identifierList, funcCalls);
    }
}

void fillVars(SgExpression *exp, const set<int> &types, set<SgSymbol*> &identifierList, vector<SgExpression*> &funcCalls)
{
    if (exp)
    {
        if (exp->variant() == FUNC_CALL)
            funcCalls.push_back(exp);

        if (types.find(exp->variant()) != types.end())
        {
            if (exp->symbol())
                identifierList.insert(OriginalSymbol(exp->symbol()));
            else if (exp->variant() == DDOT)
                identifierList.insert((SgSymbol*)exp); // BAD solution
        }
        
        fillVars(exp->lhs(), types, identifierList, funcCalls);
        fillVars(exp->rhs(), types, identifierList, funcCalls);
    }
}

static bool isPrivateVar(SgStatement *st, SgSymbol *symbol)
{
    bool retVal = false;
    
    for (auto &data : getAttributes<SgStatement*, SgStatement*>(st, set<int>{ SPF_ANALYSIS_DIR }))
    {
        set<string> privates;
        fillPrivatesFromComment(new Statement(data), privates);

        retVal = retVal || privates.find(symbol->identifier()) != privates.end();
        if (retVal)
            break;
    }

    return retVal;
}

#define BAD_POSITION_FULL(ERR_TYPE, PLACE_E, PLACE_R, BEFORE_VAR_E, BEFORE_VAR_R, BEFORE_DO_E, BEFORE_DO_R, LINE) do { \
   __spf_print(1, "bad directive position on line %d, it can be placed only %s %s %s\n", LINE, PLACE_E, BEFORE_VAR_E, BEFORE_DO_E); \
   wstring messageE, messageR;\
   __spf_printToLongBuf(messageE, L"bad directive position, it can be placed only %s %s %s", to_wstring(PLACE_E).c_str(), to_wstring(BEFORE_VAR_E).c_str(), to_wstring(BEFORE_DO_E).c_str()); \
   __spf_printToLongBuf(messageR, R1, PLACE_R, BEFORE_VAR_R, BEFORE_DO_R); \
   messagesForFile.push_back(Messages(ERR_TYPE, LINE, messageR, messageE, 1001)); \
} while(0)

static SgStatement* skipDvmDirs(SgStatement *st)
{
    while (isDVM_stat(st))
        st = st->lexNext();
    return st;
}

static void fillVarsSets(SgStatement *iterator, SgStatement *end, set<string> &varDef, set<string> &varUse)
{
    for ( ;iterator != end; iterator = iterator->lexNext())
    {
        vector<SgExpression*> funcCalls;
        if (iterator->variant() == PROC_STAT)
        {// TODO: procedures may have IN, INOUT, OUT parameters
            fillVars(iterator->expr(0), { ARRAY_REF, VAR_REF }, varDef, funcCalls);
            fillVars(iterator->expr(0), { ARRAY_REF, VAR_REF }, varUse, funcCalls);
        }
        else
        {
            if (iterator->variant() == ASSIGN_STAT || isSgExecutableStatement(iterator) == NULL)
                fillVars(iterator->expr(0), { ARRAY_REF, VAR_REF }, varDef, funcCalls);
            else
                fillVars(iterator->expr(0), { ARRAY_REF, VAR_REF }, varUse, funcCalls);

            fillVars(iterator->expr(1), { ARRAY_REF, VAR_REF }, varUse, funcCalls);
            fillVars(iterator->expr(2), { ARRAY_REF, VAR_REF }, varUse, funcCalls);

            if (iterator->variant() == FOR_NODE)
            {
                auto loop = isSgForStmt(iterator);
                varDef.insert(loop->doName()->identifier());
                varUse.insert(loop->doName()->identifier());
            }

            vector<SgExpression*> dummy;
            // TODO: functions may have IN, INOUT, OUT parameters
            for (auto &func : funcCalls)
                fillVars(func, { ARRAY_REF, VAR_REF }, varDef, dummy);
        }        
    }
}

static bool checkCover(SgStatement* st,
                       SgStatement* attributeStatement,
                       const int coverLoops,
                       vector<Messages>& messagesForFile)
{
    // COVER(VALUE)
    const int var = st->variant();
    bool retVal = true;

    SgForStmt* forSt = (SgForStmt*)st;
    const int nestedCount = countPerfectLoopNest(forSt);
    if (coverLoops > nestedCount || coverLoops == 0)
    {
        __spf_print(1, "bad directive expression: expected %d nested loops on line %d but got %d on line %d\n",
                    coverLoops, attributeStatement->lineNumber(), nestedCount, st->lineNumber());

        wstring messageE, messageR;
        __spf_printToLongBuf(messageE, L"bad directive expression: expected %d nested loops on line %d but got %d",
                             coverLoops, attributeStatement->lineNumber(), nestedCount);

        __spf_printToLongBuf(messageR, R77, coverLoops, attributeStatement->lineNumber(), nestedCount);

        messagesForFile.push_back(Messages(ERROR, st->lineNumber(), messageR, messageE, 1043));
        retVal = false;
    }

    return retVal;
}

static bool checkDeclaration(SgStatement* st,
                             SgStatement* attributeStatement,
                             const set<Symbol*>& variables,
                             vector<Messages>& messagesForFile)
{
    // PROCESS_PRIVATE(VAR)
    // MERGE_ARRAYS(ARR1, ARR2)
    const int var = st->variant();
    bool retVal = true;
    
    if (!isSgExecutableStatement(st))
    {
        st = skipDvmDirs(st);
        SgStatement* iterator = st;
        SgStatement* end = st->lexNext();
        set<string> varDef, varUse;

        fillVarsSets(iterator, end, varDef, varUse);
        for (auto& var : variables)
        {
            const string varElem = var->GetOriginal()->identifier();
            bool defCond = true;

            if (varDef.find(varElem) == varDef.end())
                defCond = false;

            if (!defCond)
            {
                BAD_POSITION_FULL(ERROR, "before", RR1_1, "variable declaration", RR1_2, "", L" ", attributeStatement->lineNumber());
                retVal = false;
            }
        }
    }
    else
    {
        BAD_POSITION_FULL(ERROR, "before", RR1_1, "variable declaration", RR1_2, "", L" ", attributeStatement->lineNumber());
        retVal = false;
    }

    return retVal;
}

static bool checkPrivate(SgStatement *st,
                         SgStatement *attributeStatement,
                         const set<Symbol*> &privates,
                         vector<Messages> &messagesForFile)
{    
    // PRIVATE(VAR)
    const int var = st->variant();
    bool retVal = true;

    if (var == FOR_NODE)
    {
        st = skipDvmDirs(st);
        SgStatement *iterator = st;
        SgStatement *end = st->lastNodeOfStmt();
        set<string> varDef, varUse;
        set<string> wrongPrivFromOmpParallel;

        fillVarsSets(iterator, end, varDef, varUse);
        for (auto& privElemS : privates)
        {
            const string privElem = privElemS->GetOriginal()->identifier();
            bool defCond = true;
            bool useCond = true;

            if (varDef.find(privElem) == varDef.end())
                defCond = false;
            if (varUse.find(privElem) == varUse.end())
                useCond = false;

            if (!defCond && !useCond)
            {
                if (attributeStatement->localLineNumber() != SPF_OMP_DIR)
                {
                    __spf_print(1, "variable '%s' is not used in loop on line %d\n", privElem.c_str(), attributeStatement->lineNumber());
                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"variable '%s' is not used in loop", to_wstring(privElem.c_str()).c_str());

                    __spf_printToLongBuf(messageR, R21, to_wstring(privElem.c_str()).c_str());

                    messagesForFile.push_back(Messages(WARR, attributeStatement->lineNumber(), messageR, messageE, 1002));
                }
                else
                    wrongPrivFromOmpParallel.insert(privElem);
            }
            else if (!defCond && useCond)
            {
                if (attributeStatement->localLineNumber() != SPF_OMP_DIR)
                {
                    __spf_print(1, "variable '%s' is not changed in loop on line %d\n", privElem.c_str(), attributeStatement->lineNumber());
                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"variable '%s' is not changed in loop", to_wstring(privElem.c_str()).c_str());

                    __spf_printToLongBuf(messageR, R23, to_wstring(privElem.c_str()).c_str());

                    messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1003));
                    retVal = false;
                }
                else
                    wrongPrivFromOmpParallel.insert(privElem);
            }
        }

        if (wrongPrivFromOmpParallel.size()) // remove unnecessary
        {
            if (wrongPrivFromOmpParallel.size() == privates.size()) // remove all
                attributeStatement->expr(0)->lhs()->setLhs(NULL);
            else
            {
                SgExpression* list = attributeStatement->expr(0)->lhs()->lhs();
                vector<SgExpression*> newList;
                for (auto ex = list; ex; ex = ex->rhs())
                {
                    string currV = ex->lhs()->symbol()->identifier();
                    if (wrongPrivFromOmpParallel.find(currV) == wrongPrivFromOmpParallel.end())
                        newList.push_back(ex->lhs());
                }
                attributeStatement->expr(0)->lhs()->setLhs(makeExprList(newList));
            }
        }
    }
    else
    {
        BAD_POSITION_FULL(ERROR, "before", RR1_1, "", L"", "DO statement", RR1_3, attributeStatement->lineNumber());
        retVal = false;
    }

    return retVal;
}

static bool checkReduction(SgStatement *st,
                           SgStatement *attributeStatement,
                           const map<string, set<Symbol*>> &reduction,
                           vector<Messages> &messagesForFile)
{
    // REDUCTION(OP(VAR))
    const int var = st->variant();
    bool retVal = true;

    if (var == FOR_NODE)
    {
        SgStatement *iterator = st->lexNext();
        SgStatement *end = st->lastNodeOfStmt();
        set<string> varDef;
        set<string> varUse;

        fillVarsSets(iterator, end, varDef, varUse);

        for (auto &redElem : reduction)
        {
            for (auto &setElemS : redElem.second)
            {
                const string setElem = setElemS->GetOriginal()->identifier();
                bool defCond = true;
                bool useCond = true;

                if (varDef.find(setElem) == varDef.end())
                    defCond = false;
                if (varUse.find(setElem) == varUse.end())
                    useCond = false;

                if (var == FOR_NODE && !defCond && !useCond)
                {
                    __spf_print(1, "variable '%s' is not used in loop on line %d\n", setElem.c_str(), attributeStatement->lineNumber());
                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"variable '%s' is not used in loop", to_wstring(setElem.c_str()).c_str());

                    __spf_printToLongBuf(messageR, R22, to_wstring(setElem.c_str()).c_str());

                    messagesForFile.push_back(Messages(WARR, attributeStatement->lineNumber(), messageR, messageE, 1002));
                }
                if (var == FOR_NODE && !defCond && useCond)
                {
                    __spf_print(1, "variable '%s' is not changed in loop on line %d\n", setElem.c_str(), attributeStatement->lineNumber());
                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"variable '%s' is not changed in loop", to_wstring(setElem.c_str()).c_str());

                    __spf_printToLongBuf(messageR, R24, to_wstring(setElem.c_str()).c_str());

                    messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1003));
                    retVal = false;
                }
            }
        }
    }
    else
    {
        BAD_POSITION_FULL(ERROR, "before", RR1_1, "", L"", "DO statement", RR1_3, attributeStatement->lineNumber());
        retVal = false;
    }

    return retVal;
}

static bool checkReduction(SgStatement *st,
                           SgStatement *attributeStatement,
                           const map<string, set<tuple<Symbol*, Symbol*, int>>> &reduction,
                           vector<Messages> &messagesForFile)
{
    // REDUCTION(MIN/MAXLOC(VAR, ARRAY, CONST))
    bool retVal = true;    

    for (auto &redElem : reduction)
    {
        set<Symbol*> vars;
        set<Symbol*> arrs;

        set<string> varsS;
        set<string> arrsS;

        for (auto &setElem : redElem.second)
        {
            vars.insert(std::get<0>(setElem));
            varsS.insert(std::get<0>(setElem)->GetOriginal()->identifier());

            arrs.insert(std::get<1>(setElem));
            arrsS.insert(std::get<1>(setElem)->GetOriginal()->identifier());

            // CHECK ARRAY DECLARATION && DIMENTION
            SgSymbol *arraySymbol = std::get<1>(setElem)->GetOriginal();
            SgStatement *declStatement = declaratedInStmt(arraySymbol);
            SgArrayType *arrayType = NULL;
            int count = std::get<2>(setElem);

            if (arraySymbol->type())
                arrayType = isSgArrayType(arraySymbol->type());

            if (arrayType)
            {
                const int dim = arrayType->dimension();

                if (dim != 1)
                {
                    __spf_print(1, "dimention of array '%s' is %d, but must be 1 on line %d\n", arraySymbol->identifier(), dim, attributeStatement->lineNumber());

                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"dimention of array '%s' is %d, but must be 1", to_wstring(arraySymbol->identifier()).c_str(), dim);

                    __spf_printToLongBuf(messageR, R25, to_wstring(arraySymbol->identifier()).c_str(), dim);

                    messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1004));

                    retVal = false;
                }

                if (!arrayType->baseType()->equivalentToType(SgTypeInt()))
                {
                    __spf_print(1, "type of array '%s' must be INTEGER on line %d\n", arraySymbol->identifier(), attributeStatement->lineNumber());

                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"type of array '%s' but must be INTEGER", to_wstring(arraySymbol->identifier()).c_str());

                    __spf_printToLongBuf(messageR, R26, to_wstring(arraySymbol->identifier()).c_str());

                    messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1005));

                    retVal = false;
                }
            }
            else
            {
                __spf_print(1, "type of variable '%s' must be array on line %d\n", arraySymbol->identifier(), attributeStatement->lineNumber());

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"type of variable '%s' must be array", to_wstring(arraySymbol->identifier()).c_str());

                __spf_printToLongBuf(messageR, R27, to_wstring(arraySymbol->identifier()).c_str());

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1006));

                retVal = false;
            }

            SgStatement *iterator = st;
            SgStatement *end = st;
            vector<SgExpression*> dimentions;
            
            while (iterator->variant() != PROG_HEDR && iterator->variant() != PROC_HEDR && iterator->variant() != FUNC_HEDR)
                iterator = iterator->controlParent();

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

                if (!isSgExecutableStatement(iterator))
                {
                    for (SgExpression *exp = iterator->expr(0); exp; exp = exp->rhs())
                        if (exp->lhs()->symbol() == arraySymbol)
                            for (SgExpression *list = exp->lhs()->lhs(); list; list = list->rhs())
                                dimentions.push_back(list->lhs());

                    if (dimentions.size())
                    {
                        if (dimentions.size() != 1)
                            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                        int size;
                        int err = CalculateInteger(dimentions[0], size);

                        if (err != 0)
                        {
                            // Expression can not be computed
                            __spf_print(1, "array size can't be computed on line %d\n", attributeStatement->lineNumber());

                            wstring messageE, messageR;
                            __spf_printToLongBuf(messageE, L"array size can't be computed");
                            __spf_printToLongBuf(messageR, R30);

                            messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1007));

                            retVal = false;
                        }
                        else if (size != count)
                        {
                            __spf_print(1, "size of array '%s' is %d, but you enter %d on line %d\n",
                                        arraySymbol->identifier(), size, count, attributeStatement->lineNumber());

                            wstring messageE, messageR;
                            __spf_printToLongBuf(messageE, L"size of array '%s' is %d, but you enter %d", to_wstring(arraySymbol->identifier()).c_str(), size, count);
                            __spf_printToLongBuf(messageR, R31, to_wstring(arraySymbol->identifier()).c_str(), size, count);

                            messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1008));

                            retVal = false;
                        }
                        break;
                    }
                }
                else
                    break;

                iterator = iterator->lexNext();
            }
        }

        map<string, set<Symbol*>> reductionVar;
        map<string, set<Symbol*>> reductionArr;

        reductionVar[redElem.first] = vars;
        reductionArr[redElem.first] = arrs;

        retVal = checkReduction(st, attributeStatement, reductionVar, messagesForFile) && checkReduction(st, attributeStatement, reductionArr, messagesForFile);
    }

    return retVal;
}

static bool findSymbolInExpressionRec(SgExpression *exp, SgSymbol *s)
{
    if (exp)
    {
        //if (exp->symbol() && string(exp->symbol()->identifier()) == s->identifier())
        if (exp->symbol() && exp->symbol() == s)
            return true;

        return findSymbolInExpressionRec(exp->lhs(), s) || findSymbolInExpressionRec(exp->rhs(), s);
    }

    return false;
}

static bool findSymbolInStatement(SgStatement *st, SgSymbol *s)
{
    if (!st || !s)
        return false;
    
    bool found = false;
    for (int i = 0; !found && i < 3; ++i)
        found = found || findSymbolInExpressionRec(st->expr(i), s);

    return found;
}

static bool checkParametersExpressionRec(SgStatement *st, SgStatement *attributeStatement, SgExpression *exp, vector<Messages> &messagesForFile)
{
    bool retVal = true;

    if (exp && exp->variant() == VAR_REF && exp->symbol())
    {
        auto declStatement = declaratedInStmt(OriginalSymbol(exp->symbol()), NULL, false);

        // check all used modules in function
        auto moduleVar = false;
        vector<SgStatement*> useStats;
        fillUsedModulesInFunction(st, useStats);
        for (auto& useSt : useStats)
        {
            map<string, SgSymbol*> visibleVars;
            fillVisibleInUseVariables(useSt, visibleVars);
            if (visibleVars.find(exp->symbol()->identifier()) != visibleVars.end())
                moduleVar = true;
        }

        if (!declStatement && !moduleVar)
        {
            __spf_print(1, "Variable '%s' in %s clause must be declared at the same module in file '%s' on line %d.\n",
                exp->symbol()->identifier(), "PARAMETER", st->fileName(), attributeStatement->lineNumber());

            wstring messageE, messageR;
            __spf_printToLongBuf(messageE, L"Variable '%s' in %s clause must be declared at the same module.",
                to_wstring(exp->symbol()->identifier()).c_str(), to_wstring("PARAMETER").c_str());

            __spf_printToLongBuf(messageR, R168, to_wstring(exp->symbol()->identifier()).c_str(), to_wstring("PARAMETER").c_str());

            messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 5004));
            retVal = false;
        }
    }

    bool leftResult, rightResult;
    leftResult = rightResult = true;
    if (exp->lhs())
        leftResult = checkParametersExpressionRec(st, attributeStatement, exp->lhs(), messagesForFile);
    if (exp->rhs())
        rightResult = checkParametersExpressionRec(st, attributeStatement, exp->rhs(), messagesForFile);
    retVal = retVal && leftResult && rightResult;

    return retVal;
}

static bool checkParameter(SgStatement *st, SgStatement *attributeStatement, const vector<pair<Expression*, Expression*>> &assigns, vector<Messages> &messagesForFile)
{
    bool retVal = true;

    if (!isSgExecutableStatement(st))
    {
        wstring messageE, messageR;

        __spf_print(1, "ANALYSIS directive with PARAMETER clause can be only at executable code section in file '%s' on line %d.\n",
                    st->fileName(), attributeStatement->lineNumber());
                
        __spf_printToLongBuf(messageE, L"ANALYSIS directive with PARAMETER clause can be only at executable code section.");
        __spf_printToLongBuf(messageR, R166, L"ANALYSIS", L"PARAMETER");

        messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 5002));
        retVal = false;
    }

    for (auto &assign : assigns)
    {
        auto assignVarExp = assign.first->GetOriginal();
        auto assignVarVar = assignVarExp->variant();

        if (!findSymbolInStatement(st, assignVarExp->symbol()))
        {
            __spf_print(1, "Variable '%s' in %s clause must be used in next statement in file '%s' on line %d.\n",
                        assignVarExp->symbol()->identifier(), "PARAMETER", st->fileName(), attributeStatement->lineNumber());

            wstring messageE, messageR;
            __spf_printToLongBuf(messageE, L"Variable '%s' in %s clause must be used in next statement.",
                                            to_wstring(assignVarExp->symbol()->identifier()).c_str(), to_wstring("PARAMETER").c_str());

            __spf_printToLongBuf(messageR, R175, to_wstring(assignVarExp->symbol()->identifier()).c_str());

            messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1057));
            retVal = false;
        }

        if (assignVarVar != VAR_REF)
        {
            __spf_print(1, "Left part of PARAMETER clause must be a variable in file '%s' on line %d.\n",
                        st->fileName(), attributeStatement->lineNumber());

            wstring messageE, messageR;
            __spf_printToLongBuf(messageE, L"Left part of PARAMETER clause must be a variable.");
            __spf_printToLongBuf(messageR, R176);

            messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1058));
            retVal = false;
        }

        auto assignExp = assign.second->GetOriginal();
        auto result = checkParametersExpressionRec(st, attributeStatement, assignExp, messagesForFile);
        retVal = retVal && result;
    }

    return retVal;
}

static bool checkShadowAcross(SgStatement *st,
                              SgStatement *attributeStatement,
                              const vector<pair<pair<Symbol*, string>, vector<pair<int, int>>>> &data,
                              vector<Messages> &messagesForFile)
{
    // SHADOW (VAR(list of  shadows)) / ACROSS (VAR(list of  shadows))
    const int var = st->variant();
    bool retVal = true;

    if (var == FOR_NODE)
    {
        for (int i = 0; i < data.size(); ++i)
        {
            SgSymbol *arraySymbol = data[i].first.first->GetOriginal();
            SgStatement *declStatement = declaratedInStmt(arraySymbol);
            const vector<pair<int, int>> &arrayDisc = data[i].second;
            bool notPrivCond = true;

            if (arraySymbol->type()->variant() != T_ARRAY)
            {
                __spf_print(1, "variable '%s' is not array on line %d\n", arraySymbol->identifier(), attributeStatement->lineNumber());

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"variable '%s' is not array", to_wstring(arraySymbol->identifier()).c_str());
                __spf_printToLongBuf(messageR, R28, to_wstring(arraySymbol->identifier()).c_str());

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1006));
                retVal = false;
            }

            notPrivCond = !isPrivateVar(st, arraySymbol) && !isPrivateVar(declStatement, arraySymbol);

            if (!notPrivCond)
            {
                __spf_print(1, "array '%s' on line %d is private\n", arraySymbol->identifier(), attributeStatement->lineNumber());

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"array '%s' is private", to_wstring(arraySymbol->identifier()).c_str());

                __spf_printToLongBuf(messageR, R33, to_wstring(arraySymbol->identifier()).c_str());

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1009));
                retVal = false;
            }
            
            SgArrayType *arrayType = NULL; 
            if (arraySymbol->type())
                arrayType = isSgArrayType(arraySymbol->type());

            if (arrayType)
            {
                int dim = arrayType->dimension();

                if (dim != arrayDisc.size())
                {
                    __spf_print(1, "dimention of array '%s' is %d, but you enter %d on line %d\n",
                                arraySymbol->identifier(), arrayType->dimension(), (int)arrayDisc.size(), attributeStatement->lineNumber());

                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"dimention of array '%s' is %d, but you enter %d",
                                         to_wstring(arraySymbol->identifier()).c_str(), arrayType->dimension(), (int)arrayDisc.size());

                    __spf_printToLongBuf(messageR, R32,
                                         to_wstring(arraySymbol->identifier()).c_str(), arrayType->dimension(), (int)arrayDisc.size());

                    messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1008));
                    retVal = false;
                }
            }
            else
            {
                __spf_print(1, "variable '%s' is not array on line %d\n", arraySymbol->identifier(), attributeStatement->lineNumber());

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"variable '%s' is not array", to_wstring(arraySymbol->identifier()).c_str());

                __spf_printToLongBuf(messageR, R29, to_wstring(arraySymbol->identifier()).c_str());

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1006));
                retVal = false;
            }

            for (auto &array : arrayDisc)
            {
                if (array.first < 0 || array.second < 0)
                {
                    __spf_print(1, "only positive numbers are supported on line %d\n", attributeStatement->lineNumber());

                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"only positive numbers are supported");

                    __spf_printToLongBuf(messageR, R35);

                    messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1010));
                    retVal = false;
                }
            }
        }
    }
    else
    {
        BAD_POSITION_FULL(ERROR, "before", RR1_1, "", L"", "DO statement", RR1_3, attributeStatement->lineNumber());
        retVal = false;
    }

    return retVal;
}

static int hasName(SgExpression *exp, const string &varName)
{
    if (exp)
    {
        SgSymbol *symb = exp->symbol();
        SgExpression *lhs = exp->lhs();
        SgExpression *rhs = exp->rhs();

        if (symb)
            if (string(symb->identifier()) == varName)
                return 1;

        return hasName(lhs, varName) + hasName(rhs, varName);
    }

    return 0;
}

static bool isRemoteExpressions(SgExpression *exp, SgExpression *remoteExp, map<SgExpression*, string> &collection)
{
    if (exp == remoteExp)
        return true;

    bool retVal = true;

    while (retVal && exp != NULL && remoteExp != NULL)
    {
        if (remoteExp->lhs())
            if (remoteExp->lhs()->variant() != DDOT)
                retVal = retVal && isEqExpressions(exp->lhs(), remoteExp->lhs(), collection);

        exp = exp->rhs();
        remoteExp = remoteExp->rhs();
    }

    return retVal;
}

static bool hasRemoteExpressions(SgExpression *exp, SgExpression *remoteExp, map<SgExpression*, string> &collection)
{
    if (exp)
    {
        SgExpression *lhs = exp->lhs();
        SgExpression *rhs = exp->rhs();

        if (exp->variant() == ARRAY_REF && string(exp->symbol()->identifier()) == remoteExp->symbol()->identifier())
            if (isRemoteExpressions(exp->lhs(), remoteExp->lhs(), collection))
                return true;

        return hasRemoteExpressions(lhs, remoteExp, collection) || hasRemoteExpressions(rhs, remoteExp, collection);
    }

    return false;
}

static bool checkRemote(SgStatement *st,
                        SgStatement *attributeStatement,
                        const map<pair<Symbol*, string>, Expression*> &remote,
                        vector<Messages> &messagesForFile)
{
    // REMOTE_ACCESS (EXPR)
    const int var = st->variant();
    bool retVal = true;

    if (var == FOR_NODE || isSgExecutableStatement(st))
    {
        for (auto &remElem : remote)
        {
            bool cond = false;
            SgStatement *declStatement = declaratedInStmt(remElem.first.first->GetOriginal());
            set<SgSymbol*> arraySymbols;

            vector<SgExpression*> dummy;
            fillVars(remElem.second, { ARRAY_REF }, arraySymbols, dummy);
            for (auto &arraySymbol : arraySymbols)
            {
                declStatement = declaratedInStmt(arraySymbol);
                bool notPrivCond = !isPrivateVar(st, arraySymbol) && !isPrivateVar(declStatement, arraySymbol);

                if (!notPrivCond)
                {
                    __spf_print(1, "array '%s' is private on line %d\n", arraySymbol->identifier(), attributeStatement->lineNumber());

                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"array '%s' is private", to_wstring(arraySymbol->identifier()).c_str());

                    __spf_printToLongBuf(messageR, R34, to_wstring(arraySymbol->identifier()).c_str());

                    messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1009));
                    retVal = false;
                }
            }

            if (var == FOR_NODE)
            {
                SgStatement *iterator = st;
                SgStatement *end = st->lastNodeOfStmt();
                map<string, int> forVars;

                while (iterator != end)
                {
                    SgForStmt *forSt = isSgForStmt(iterator);

                    if (forSt)
                    {
                        SgSymbol *forName = forSt->doName();
                        forVars.insert(make_pair(string(forName->identifier()), 0));
                    }

                    iterator = iterator->lexNext();
                }

                // CHECK: i AND a * i + b
                // remElem.second links to b(i,j,k)
                // remElem.second->lhs() links to i,j,k
                SgExpression *remoteExp = remElem.second->lhs();
                while (remoteExp)
                {
                    int forVarsCount = 0;

                    for (auto &forVar : forVars)
                    {
                        if (hasName(remoteExp->lhs(), forVar.first))
                        {
                            ++forVarsCount;
                            ++forVar.second;

                            if (retVal && forVarsCount > 1 || forVar.second > 1)
                            {
                                __spf_print(1, "bad directive expression: too many DO variables on line %d\n", attributeStatement->lineNumber());

                                wstring messageE, messageR;
                                __spf_printToLongBuf(messageE, L"bad directive expression: too many DO variables");

                                __spf_printToLongBuf(messageR, R2);

                                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1001));
                                retVal = false;
                            }

                            // CHECK TREE: i OR a * i OR a * i + b
                            SgExpression *list = remoteExp->lhs();
                            bool isRemoteSubTreeCond = false;

                            if (list->variant() == ADD_OP)
                            {
                                if (list->lhs()->variant() == MULT_OP && !hasName(list->rhs(), forVar.first))
                                {
                                    if (hasName(list->lhs(), forVar.first) == 1)
                                        isRemoteSubTreeCond = true;
                                }
                                else if (list->rhs()->variant() == MULT_OP && !hasName(list->lhs(), forVar.first))
                                {
                                    if (hasName(list->rhs(), forVar.first) == 1)
                                        isRemoteSubTreeCond = true;
                                }
                                else if (hasName(list, forVar.first) == 1)
                                    isRemoteSubTreeCond = true;
                            }
                            else if (list->variant() == MULT_OP)
                            {
                                if (!hasName(list->lhs(), forVar.first) && hasName(list->rhs(), forVar.first) == 1 ||
                                    !hasName(list->rhs(), forVar.first) && hasName(list->lhs(), forVar.first) == 1)
                                    isRemoteSubTreeCond = true;
                            }
                            else if (hasName(list, forVar.first) == 1)
                                isRemoteSubTreeCond = true;

                            if (!isRemoteSubTreeCond)
                            {
                                __spf_print(1, "bad directive expression: only a * i + b on line %d\n", attributeStatement->lineNumber()); 

                                wstring messageE, messageR;
                                __spf_printToLongBuf(messageE, L"bad directive expression: only a * i + b");

                                __spf_printToLongBuf(messageR, R3);

                                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1001));
                                retVal = false;
                            }
                        }
                    }

                    remoteExp = remoteExp->rhs();
                }
            }
                        
            st = skipDvmDirs(st);
            SgStatement *iterator = st;
            SgStatement *end = (var == FOR_NODE) ? st->lastNodeOfStmt() : st->lexNext();

            while (iterator != end)
            {
                map<SgExpression*, string> collection;
                for (int i = 0; i < 3; ++i)
                    if (hasRemoteExpressions(iterator->expr(i), remElem.second, collection))
                        cond = true;                

                iterator = iterator->lexNext();
            }

            if (!cond)
            {
                __spf_print(1, "no such expression '%s' on line %d\n", remElem.second->unparse(), attributeStatement->lineNumber());

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"no such expression '%s' on loop", to_wstring(remElem.second->unparse()).c_str());

                __spf_printToLongBuf(messageR, R36, to_wstring(remElem.second->unparse()).c_str());

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1011));
                retVal = false;
            }
        }
    }
    else
    {
        BAD_POSITION_FULL(ERROR, "before", RR1_1, "", L"", "DO statement", RR1_3, attributeStatement->lineNumber());
        retVal = false;
    }

    return retVal;
}

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

        findAllRefs(ex->lhs(), names);
        findAllRefs(ex->rhs(), names);
    }
}

static bool checkParallelRegions(SgStatement *st,
                                 const map<string, CommonBlock*> *commonBlocks,
                                 vector<Messages> &messagesForFile)
{
    bool retVal = true;

    bool execNext = isSgExecutableStatement(st->lexNext());

    if (execNext || st->lexNext()->variant() == ENTRY_STAT)
    {
        if (st->variant() == SPF_PARALLEL_REG_DIR)
        {
            SgSymbol *identSymbol = st->symbol();

            // declaration checking
            SgStatement *iterator = st;
            SgStatement *end = st;

            while (iterator->variant() != PROG_HEDR && iterator->variant() != PROC_HEDR && iterator->variant() != FUNC_HEDR)
                iterator = iterator->controlParent();

            for (; iterator != end && retVal; iterator = iterator->lexNext())
            {
                if (isSPF_stat(iterator) || isDVM_stat(iterator))
                    continue;
                
                if (!isSgExecutableStatement(iterator))
                {
                    if (iterator->variant() == VAR_DECL || iterator->variant() == VAR_DECL_90)
                    {
                        set<string> names;
                        findAllRefs(iterator->expr(0), names);
                        if (names.find(identSymbol->identifier()) != names.end())
                        {
                            __spf_print(1, "wrong parallel region identifier: variable '%s' was declared on line %d on line %d\n",
                                            identSymbol->identifier(), iterator->lineNumber(), st->lineNumber());

                            wstring messageE, messageR;
                            __spf_printToLongBuf(messageE, L"wrong parallel region identifier: variable '%s' was declared on line %d",
                                                           to_wstring(identSymbol->identifier()).c_str(), iterator->lineNumber());

                            __spf_printToLongBuf(messageR, R63,
                                                           to_wstring(identSymbol->identifier()).c_str(), iterator->lineNumber());

                            messagesForFile.push_back(Messages(ERROR, st->lineNumber(), messageR, messageE, 1031));

                            retVal = false;
                        }
                    }
                }
                else
                    break;
            }

            if (commonBlocks)
            {
                // common blocks checking
                for (auto &commonBlockPair : *commonBlocks)
                {
                    for (auto &variable : commonBlockPair.second->getVariables())
                    {
                        if (variable->getName() == identSymbol->identifier())
                        {
                            __spf_print(1, "wrong parallel region identifier: variable '%s' was declared in common block '%s' on line %d\n",
                                        identSymbol->identifier(), commonBlockPair.first.c_str(), st->lineNumber());

                            wstring messageE, messageR;
                            __spf_printToLongBuf(messageE, L"wrong parallel region identifier: variable '%s' was declared in common block '%s'",
                                                 to_wstring(identSymbol->identifier()).c_str(), to_wstring(commonBlockPair.first).c_str());

                            __spf_printToLongBuf(messageR, R64,
                                                 to_wstring(identSymbol->identifier()).c_str(), to_wstring(commonBlockPair.first).c_str());

                            messagesForFile.push_back(Messages(ERROR, st->lineNumber(), messageR, messageE, 1032));

                            retVal = false;
                        }
                    }
                }
            }

            // try to find SPF_END_PARALLEL_REG_DIR
            iterator = st->lexNext();
            bool found = false;

            for (; iterator && !found; iterator = iterator->lexNext())
            {
                const int var = iterator->variant();
                if (var == SPF_PARALLEL_REG_DIR)
                {
                    // intersection
                    __spf_print(1, "bad directive position: expected 'SPF END PARALLEL_REG_DIR' for identificator '%s', but got 'SPF PARALLEL_REG_DIR' on line %d\n",
                                identSymbol->identifier(), st->lineNumber());

                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"bad directive position: expected 'SPF END PARALLEL_REG_DIR' for identificator '%s', but got 'SPF PARALLEL_REG_DIR'",
                                         to_wstring(identSymbol->identifier()).c_str());

                    __spf_printToLongBuf(messageR, R4,
                                         to_wstring(identSymbol->identifier()).c_str());

                    messagesForFile.push_back(Messages(ERROR, st->lineNumber(), messageR, messageE, 1001));

                    retVal = false;
                    break;
                }
                else if (var == SPF_END_PARALLEL_REG_DIR)
                {
                    found = true;

                    if (iterator->controlParent() != st->controlParent())
                    {
                        __spf_print(1, "bad directive position: expected 'SPF END PARALLEL_REG_DIR' for identificator '%s' at the same scope on line %d\n",
                                    identSymbol->identifier(), st->lineNumber());

                        wstring messageE, messageR;
                        __spf_printToLongBuf(messageE, L"bad directive position: expected 'SPF END PARALLEL_REG_DIR' for identificator '%s' at the same scope",
                                             to_wstring(identSymbol->identifier()).c_str());

                        __spf_printToLongBuf(messageR, R5,
                                             to_wstring(identSymbol->identifier()).c_str());

                        messagesForFile.push_back(Messages(ERROR, st->lineNumber(), messageR, messageE, 1001));

                        retVal = false;
                    }

                    break;
                }
            }

            if (!found && retVal)
            {
                __spf_print(1, "bad directive position: expected 'SPF END PARALLEL_REG_DIR' for identificator '%s' on line %d\n", identSymbol->identifier(), st->lineNumber());

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"bad directive position: expected 'SPF END PARALLEL_REG_DIR' for identificator '%s'",
                                     to_wstring(identSymbol->identifier()).c_str());

                __spf_printToLongBuf(messageR, R6,
                                     to_wstring(identSymbol->identifier()).c_str());

                messagesForFile.push_back(Messages(ERROR, st->lineNumber(), messageR, messageE, 1001));

                retVal = false;
            }
        }
        else
        {
            // type == SPF_END_PARALLEL_REG_DIR
            // try to find SPF_PARALLEL_REG_DIR
            SgStatement *iterator = st->lexPrev();
            SgStatement *end = st->controlParent();
            bool found = false;
            
            for (; iterator != end && retVal && !found; iterator = iterator->lexPrev())
            {
                const int var = iterator->variant();
                if (var == SPF_END_PARALLEL_REG_DIR)
                {
                    // intersection
                    __spf_print(1, "bad directive position: expected 'SPF PARALLEL_REG_DIR', but got 'SPF END PARALLEL_REG_DIR' on line %d\n", st->lineNumber());

                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"bad directive position: expected 'SPF PARALLEL_REG_DIR', but got 'SPF END PARALLEL_REG_DIR'");

                    __spf_printToLongBuf(messageR, R7);

                    messagesForFile.push_back(Messages(ERROR, st->lineNumber(), messageR, messageE, 1001));

                    retVal = false;
                    break;
                }
                else if (var == SPF_PARALLEL_REG_DIR)
                {
                    found = true;

                    if (iterator->controlParent() != st->controlParent())
                    {
                        __spf_print(1, "bad directive position: expected 'SPF PARALLEL_REG_DIR' at the same scope on line %d\n", st->lineNumber());
  
                        wstring messageE, messageR;
                        __spf_printToLongBuf(messageE, L"bad directive position: expected 'SPF PARALLEL_REG_DIR' at the same scope");

                        __spf_printToLongBuf(messageR, R8);

                        messagesForFile.push_back(Messages(ERROR, st->lineNumber(), messageR, messageE, 1001));

                        retVal = false;
                    }

                    break;
                }
            }

            if (!found && retVal)
            {
                __spf_print(1, "bad directive position: expected 'SPF PARALLEL_REG_DIR' on line %d\n", st->lineNumber());
   
                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"bad directive position: expected 'SPF PARALLEL_REG_DIR'");

                __spf_printToLongBuf(messageR, R9);

                messagesForFile.push_back(Messages(ERROR, st->lineNumber(), messageR, messageE, 1001));

                retVal = false;
            }
        }
    }
    else
    {
        BAD_POSITION_FULL(ERROR, "after", RR1_4, "", L"", "all declaration statements", RR1_5, st->lineNumber());
        retVal = false;
    }

    return retVal;
}

static inline void addSPFtoAttr(SgStatement *st, const string &currFile, map<pair<string, int>, set<SgStatement*>>& usersDirectives)
{
    bool cond = false;
    SgStatement *iterator = skipDvmDirs(st);
    vector<pair<SgStatement*, bool>> attributesToProcess;
    do
    {
        SgStatement *prev = iterator->lexPrev();
        const int prevVar = prev->variant();
        cond = (isSPF_stat(prev) && prevVar != SPF_END_PARALLEL_REG_DIR);
        if (cond)
        {
            if ((prev->fileName() == currFile) && prevVar != SPF_PARALLEL_REG_DIR)
                attributesToProcess.push_back(std::make_pair(prev, true));
            else
                attributesToProcess.push_back(std::make_pair(prev, false));
            iterator = prev;
        }
    } while (cond);

    //save direct order of SPF from source code
    std::reverse(attributesToProcess.begin(), attributesToProcess.end());
    for (auto& attr : attributesToProcess)
    {
        addToattribute(attr.first, st, attr.first->variant());

        auto copy = attr.first->copyPtr();
        copy->setlineNumber(attr.first->lineNumber());
        copy->setLocalLineNumber(attr.first->localLineNumber());
        usersDirectives[std::make_pair(st->fileName(), st->lineNumber())].insert(copy);

        if (attr.second)
            attr.first->deleteStmt();
    }
}

static bool checkFissionPrivatesExpansion(SgStatement *st,
                                          SgStatement *attributeStatement,
                                          const string &currFile,
                                          vector<Messages> &messagesForFile,
                                          map<pair<string, int>, set<SgStatement*>>& usersDirectives,
                                          bool checkVars = false)
{
    bool retVal = true;
    if (attributeStatement)
    {
        vector<string> vars;
        fillFissionPrivatesExpansionFromComment(new Statement(attributeStatement), vars);

        if (checkVars && !vars.size())
        {
            __spf_print(1, "bad directive expression: expected list of variables on line %d\n", attributeStatement->lineNumber());

            wstring messageE, messageR;
            __spf_printToLongBuf(messageE, L"bad directive expression: expected list of variables");

            __spf_printToLongBuf(messageR, R76);

            messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1043));

            retVal = false;
        }

        if (vars.size())
        {
            // move next SPF directives to atributes
            for (auto s = st; s != st->lastNodeOfStmt()->lexNext(); s = s->lexNext())
                addSPFtoAttr(s, currFile, usersDirectives);

            SgForStmt *forSt = (SgForStmt*)st;
            const int nestedCount = countPerfectLoopNest(forSt);
            if (vars.size() > nestedCount)
            {
                __spf_print(1, "bad directive expression: expected %d nested loops on line %d but got %d on line %d\n",
                            (int)vars.size(), attributeStatement->lineNumber(), nestedCount, st->lineNumber());

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"bad directive expression: expected %d nested loops on line %d but got %d",
                                     (int)vars.size(), attributeStatement->lineNumber(), nestedCount);

                __spf_printToLongBuf(messageR, R77, (int)vars.size(), attributeStatement->lineNumber(), nestedCount);

                messagesForFile.push_back(Messages(ERROR, st->lineNumber(), messageR, messageE, 1043));

                retVal = false;
            }
        }

        for (int i = 0; retVal && i < vars.size(); ++i)
        {
            SgForStmt *forSt = (SgForStmt*)st;
            if (forSt->doName()->identifier() != vars[i])
            {
                __spf_print(1, "bad directive expression: expected variable '%s' at %d position on line %d\n",
                            forSt->doName()->identifier(), i + 1, attributeStatement->lineNumber());

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"bad directive expression: expected variable '%s' at %d position",
                                     to_wstring(forSt->doName()->identifier()).c_str(), i + 1);

                __spf_printToLongBuf(messageR, R78,
                                     to_wstring(forSt->doName()->identifier()).c_str(), i + 1);

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1043));

                retVal = false;
            }
            st = st->lexNext();
        }
    }
    return retVal;
}

static bool checkShrink(SgStatement *st,
                        SgStatement *attributeStatement,
                        const string &currFile,
                        vector<Messages> &messagesForFile)
{
    bool retVal = true;
    
    vector<pair<Symbol *, vector<int>>> varDims;
    fillShrinkFromComment(new Statement(attributeStatement), varDims);

    for (auto &p : varDims)
    {
        auto var = p.first->GetOriginal();
        auto dims = p.second;

        // check variable type
        SgArrayType *arrType = isSgArrayType(var->type());
        if (!arrType)
        {
            __spf_print(1, "variable in shrink clause must be array in file '%s' on line %d\n", st->fileName(), attributeStatement->lineNumber());
            wstring messageE, messageR;
            __spf_printToLongBuf(messageE, L"variable in shrink clause must be array in file '%s'", to_wstring(st->fileName()).c_str());

            __spf_printToLongBuf(messageR, R154, to_wstring(st->fileName()).c_str());

            messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1053));
            retVal = false;
        }
        else
        {
            // check private directives
            set<Symbol*> privatesS;
            set<SgSymbol*> privates;
            for (int i = 0; i < st->numberOfAttributes(); ++i)
            {
                SgAttribute *attr = st->getAttribute(i);
                SgStatement *attributeStatement = (SgStatement *)(attr->getAttributeData());
                fillPrivatesFromComment(new Statement(attributeStatement), privatesS);
            }
            for (auto &elem : privatesS)
                privates.insert(elem->GetOriginal());

            auto it = privates.find(var);
            if (it == privates.end())
            {
                __spf_print(1, "array '%s' in shrink clause must be also declared in private clause in file '%s' on line %d\n",
                            var->identifier(), st->fileName(), attributeStatement->lineNumber());
                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"array '%s' in shrink clause must be also declared in private clause in file '%s'",
                                     to_wstring(var->identifier()).c_str(), to_wstring(st->fileName()).c_str());

                __spf_printToLongBuf(messageR, R157, to_wstring(var->identifier()).c_str(), to_wstring(st->fileName()).c_str());

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1056));
                retVal = false;
            }
            
            // check mask dimensions
            if (dims.size() != arrType->dimension())
            {
                __spf_print(1, "length of mask for array '%s' must be %d, but you enter only %d dimenions in file '%s' on line %d\n",
                            var->identifier(), arrType->dimension(), dims.size(), st->fileName(), attributeStatement->lineNumber());
                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"length of mask for array '%s' must be %d, but you enter only %d dimenions in file '%s'",
                                     to_wstring(var->identifier()).c_str(), arrType->dimension(), dims.size(),
                                     to_wstring(st->fileName()).c_str());

                __spf_printToLongBuf(messageR, R155, to_wstring(var->identifier()).c_str(), arrType->dimension(), dims.size(), to_wstring(st->fileName()).c_str());

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1054));
                retVal = false;
            }

            for (auto i = 0; i < dims.size(); ++i)
            {
                auto dimVal = dims[i];
                if (dimVal != 0 && dimVal != 1)
                {
                    __spf_print(1, "wrong mask value in %d position for array '%s': it can be only 0 or 1 in file '%s' on line %d\n",
                                i + 1, var->identifier(), st->fileName(), attributeStatement->lineNumber());
                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"wrong mask value in %d position of array '%s': it can be only 0 or 1 in file '%s'",
                                         i + 1, to_wstring(var->identifier()).c_str(), to_wstring(st->fileName()).c_str());

                    __spf_printToLongBuf(messageR, R156, i + 1, to_wstring(var->identifier()).c_str(), to_wstring(st->fileName()).c_str());

                    messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 1055));
                    retVal = false;
                }
            }
        }
    }

    return retVal;
}

static bool recIsVarUsed(SgStatement *st, SgExpression *exp, const string &varName)
{
    if (exp)
    {
        if (exp->symbol() && exp->symbol()->identifier() == varName)
            return true;
        return recIsVarUsed(st, exp->lhs(), varName) || recIsVarUsed(st, exp->rhs(), varName);
    }
    return false;
}

static bool isVarUsed(SgStatement *st, const string &varName, bool doNotGetFuncStat = false)
{
    if (st)
    {
        auto funcSt = doNotGetFuncStat ? st : getFuncStat(st);
        checkNull(funcSt, convertFileName(__FILE__).c_str(), __LINE__);
        for (auto st = funcSt; st != funcSt->lastNodeOfStmt(); st = st->lexNext())
        {
            for (auto i = 0; i < 3; ++i)
            {
                bool found = recIsVarUsed(st, st->expr(i), varName);
                if (found)
                    return true;
            }
        }
    }
    return false;
}

static bool isModuleVar(SgStatement *st,
                        SgStatement *attributeStatement, 
                        const string &varName)
{
    auto moduleSymbols = moduleRefsByUseInFunction(st);
    // check renamed vars
    for (auto &elem : moduleSymbols)
        for (auto &var : elem.second)
            if (varName == var->identifier())
                return true;
    // check all used modules in function
    vector<SgStatement*> useStats;
    fillUsedModulesInFunction(st, useStats);
    for (auto &useSt : useStats)
    {
        map<string, SgSymbol*> visibleVars;
        fillVisibleInUseVariables(useSt, visibleVars);
        if (visibleVars.find(varName) != visibleVars.end())
            return true;
    }
    return false;
}

static bool checkCheckpointVarsDecl(SgStatement *st,
                                    SgStatement *attributeStatement,
                                    const set<Symbol*> &vars,
                                    const string &op,
                                    vector<Messages> &messagesForFile)
{
    bool retVal = true;
    for (auto &varS : vars)
    {
        auto var = varS->GetOriginal();
        bool module = isModuleVar(st, attributeStatement, var->identifier());
        if (!module)
        {
            bool local = isVarUsed(st, var->identifier());
            if (!local)
            {
                __spf_print(1, "Variable '%s' in %s clause must be declared at the same module in file '%s' on line %d.\n",
                            var->identifier(), op.c_str(), st->fileName(), attributeStatement->lineNumber());

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"Variable '%s' in %s clause must be declared at the same module.",
                                               to_wstring(var->identifier()).c_str(), to_wstring(op).c_str());

                __spf_printToLongBuf(messageR, R168, to_wstring(var->identifier()).c_str(), to_wstring(op).c_str());

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 5004));
                retVal = false;
            }
        }
    }
    return retVal;
}

static bool checkCheckpoint(SgStatement *st,
                            SgStatement *attributeStatement,
                            const map<int, Expression*> &clauses,
                            const set<Symbol*> &vars,
                            const set<Symbol*> &expt,
                            vector<Messages> &messagesForFile)
{
    bool retVal = true;
    bool hasInterval = clauses.find(SPF_INTERVAL_OP) == clauses.end();
    bool isExecutable = isSgExecutableStatement(st);

    for (auto &p : clauses)
    {
        auto op = p.first;
        SgExpression *exprList = p.second->GetOriginal();
        switch (op)
        {
        case SPF_INTERVAL_OP:
        {
            int count = 0;
            while (exprList)
            {
                if (exprList->lhs())
                    ++count;
                exprList = exprList->rhs();
            }
            if (count != 1)
            {
                wstring messageE, messageR;
                __spf_print(1, "INTERVAL clause can be used only once in file '%s' on line %d.\n", st->fileName(), attributeStatement->lineNumber());
                
                __spf_printToLongBuf(messageE, L"INTERVAL clause can be used only once.");
                __spf_printToLongBuf(messageR, R170, L"INTERVAL");

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 5006));
                retVal = false;
            }
            exprList = p.second->lhs();
            if (!exprList || exprList->rhs()->variant() != INT_VAL ||
                exprList->lhs()->variant() != SPF_TIME_OP && exprList->lhs()->variant() != SPF_ITER_OP)
            {
                wstring messageE, messageR;

                __spf_print(1, "The first argument must be TIME or ITER and the second must be integer in INTERVAL clause in file '%s' on line %d.\n",
                            st->fileName(), attributeStatement->lineNumber());
                
                __spf_printToLongBuf(messageE, L"The first argument must be TIME or ITER and the second must be integer in INTERVAL clause.");
                __spf_printToLongBuf(messageR, R165);

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 5001));
                retVal = false;
            }

            if (!isExecutable)
            {
                wstring messageE, messageR;

                __spf_print(1, "CHECKPOINT directive with INTERVAL clause can be only at executable code section in file '%s' on line %d.\n",
                            st->fileName(), attributeStatement->lineNumber());
                
                __spf_printToLongBuf(messageE, L"CHECKPOINT directive with INTERVAL clause can be only at executable code section.");
                __spf_printToLongBuf(messageR, R166, L"INTERVAL");

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 5002));
                retVal = false;
            }
            break;
        }
        case SPF_FILES_COUNT_OP:
        {
            if (!isExecutable)
            {
                wstring messageE, messageR;

                __spf_print(1, "CHECKPOINT directive with FILES clause can be only at executable code section in file '%s' on line %d.\n",
                            st->fileName(), attributeStatement->lineNumber());
                
                __spf_printToLongBuf(messageE, L"CHECKPOINT directive with FILES clause can be only at executable code section.");
                __spf_printToLongBuf(messageR, R166, L"CHECKPOINT", L"FILES");

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 5002));
                retVal = false;
            }

            int count = 0;
            while (exprList)
            {
                if (exprList->lhs())
                    ++count;
                exprList = exprList->rhs();
            }
            exprList = p.second;

            if (count != 1)
            {
                wstring messageE, messageR;
                __spf_print(1, "FILES clause can be used only once in file '%s' on line %d.\n",
                            st->fileName(), attributeStatement->lineNumber());
                
                __spf_printToLongBuf(messageE, L"FILES clause can be used only once.");
                __spf_printToLongBuf(messageR, R170, L"FILES");

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 5006));
                retVal = false;
            }
            if (exprList && exprList->lhs()->variant() != INT_VAL)
            {
                wstring messageE, messageR;

                __spf_print(1, "CHECKPOINT directive with FILES clause must contain integer value in file '%s' on line %d.\n",
                            st->fileName(), attributeStatement->lineNumber());
                
                __spf_printToLongBuf(messageE, L"CHECKPOINT directive with FILES clause must contain integer value.");
                __spf_printToLongBuf(messageR, R167);

                messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 5003));
                retVal = false;
            }
            break;
        }
        case SPF_VARLIST_OP:
        {
            retVal = retVal && checkCheckpointVarsDecl(st, attributeStatement, vars, string("VARLIST"), messagesForFile);
            for (auto &varS : vars)
            {
                if (expt.find(varS) != expt.end())
                {
                    wstring messageE, messageR;

                    __spf_print(1, "Variable '%s' can't be used in FILES and EXCEPT clauses at the same time in file '%s' on line %d.\n",
                                varS->GetOriginal()->identifier(), st->fileName(), attributeStatement->lineNumber());
                    
                    __spf_printToLongBuf(messageE, L"Variable '%s' can't be used in FILES and EXCEPT clauses at the same time.",
                                                   to_wstring(varS->GetOriginal()->identifier()).c_str());
                    __spf_printToLongBuf(messageR, R172, to_wstring(varS->GetOriginal()->identifier()).c_str());

                    messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 5007));
                    retVal = false;
                }
            }
            break;
        }
        case SPF_EXCEPT_OP:
        {
            retVal = retVal && checkCheckpointVarsDecl(st, attributeStatement, vars, string("EXEPT"), messagesForFile);
            break;
        }
        case SPF_TYPE_OP:
        {
            int count = 0;
            exprList = exprList->rhs();
            while (exprList)
            {
                if (exprList->lhs() && exprList->lhs()->variant() != ACC_ASYNC_OP && exprList->lhs()->variant() != SPF_FLEXIBLE_OP)
                {
                    wstring messageE, messageR;

                    __spf_print(1, "Illegal option in TYPE clause in file '%s' on line %d.\n", st->fileName(), attributeStatement->lineNumber());
                    
                    __spf_printToLongBuf(messageE, L"Illegal option in TYPE clause.");
                    __spf_printToLongBuf(messageR, R169);

                    messagesForFile.push_back(Messages(ERROR, attributeStatement->lineNumber(), messageR, messageE, 5005));
                    retVal = false;
                }
                ++count;
                exprList = exprList->rhs();
            }
            break;
        }
        default:
            retVal = false;
            break;
        }
    }

    return retVal;
}

static int countSPF_OP(SgStatement *st, const int type, const int op)
{
    int count = 0;
    if (st)
    {
        for (auto &data : getAttributes<SgStatement*, SgStatement*>(st, set<int>{ type }))
        {
            SgExpression *exprList = data->expr(0);
            while (exprList)
            {
                if (exprList->lhs()->variant() == op)
                    ++count;

                exprList = exprList->rhs();
            }
        }
    }
    return count;
}

static bool isSPF_OP(SgStatement *st, const int op)
{
    if (st)
    {
        SgExpression *exprList = st->expr(0);
        while (exprList)
        {
            if (exprList->lhs() && exprList->lhs()->variant() == op)
                return true;
            exprList = exprList->rhs();
        }
    }
    return false;
}

static inline bool processStat(SgStatement *st, const string &currFile,
                               const map<string, CommonBlock*> *commonBlocks,
                               vector<Messages> &messagesForFile,
                               map<pair<string, int>, set<SgStatement*>>& usersDirectives)
{
    bool retVal = true;
    if (st->variant() == SPF_PARALLEL_REG_DIR || st->variant() == SPF_END_PARALLEL_REG_DIR)
    {
        bool result = checkParallelRegions(st, commonBlocks, messagesForFile);
        retVal = retVal && result;
    }
    
    // ignore SPF statements
    if (isSPF_stat(st))
        return retVal;

    addSPFtoAttr(st, currFile, usersDirectives);

    for (int i = 0; i < st->numberOfAttributes(); ++i)
    {
        SgAttribute *attr = st->getAttribute(i);
        SgStatement *attributeStatement = (SgStatement *)(attr->getAttributeData());
        int type = st->attributeType(i);
        int count;

        if (type == SPF_ANALYSIS_DIR)
        {
            // !$SPF ANALYSIS
            // PRIVATE(VAR)
            set<Symbol*> privates;
            fillPrivatesFromComment(new Statement(attributeStatement), privates, ACC_PRIVATE_OP);
            if (privates.size())
            {
                bool result = checkPrivate(st, attributeStatement, privates, messagesForFile);
                retVal = retVal && result;
            }

            // PROCESS_PRIVATE(VAR)
            privates.clear();
            fillPrivatesFromComment(new Statement(attributeStatement), privates, SPF_PROCESS_PRIVATE_OP);
            if (privates.size())
            {
                bool result = checkDeclaration(st, attributeStatement, privates, messagesForFile);
                retVal = retVal && result;
            }

            // REDUCTION(OP(VAR), MIN/MAXLOC(VAR, ARRAY, CONST))
            map<string, set<Symbol*>> reduction;
            map<string, set<tuple<Symbol*, Symbol*, int>>> reductionLoc;
            fillReductionsFromComment(new Statement(attributeStatement), reduction);
            fillReductionsFromComment(new Statement(attributeStatement), reductionLoc);
            if (reduction.size())
            {
                bool result = checkReduction(st, attributeStatement, reduction, messagesForFile);
                bool resultLoc = checkReduction(st, attributeStatement, reductionLoc, messagesForFile);
                retVal = retVal && result && resultLoc;
            }

            // PARAMETER(ident=expr)
            vector<pair<Expression*, Expression*>> assigns;
            fillParameterFromComment(new Statement(attributeStatement), assigns);
            if (assigns.size())
            {
                bool result = checkParameter(st, attributeStatement, assigns, messagesForFile);
                retVal = retVal && result;
            }

            // COVER
            if (isSPF_OP(attributeStatement, SPF_COVER_OP) && (count = countSPF_OP(st, SPF_ANALYSIS_DIR, SPF_COVER_OP)))
            {
                attributeStatement->setLocalLineNumber(-1);
                if (count > 1 || st->variant() != FOR_NODE)
                {
                    BAD_POSITION_FULL(ERROR, "once", RR1_7, "before", RR1_1, "DO statement", RR1_3, attributeStatement->lineNumber());
                    retVal = false;
                }
                else
                {
                    bool result = checkCover(st, attributeStatement, getCoverPropertyFromComment(new Statement(attributeStatement)), messagesForFile);
                    retVal = retVal && result;
                }
            }
        }
        else if (type == SPF_PARALLEL_DIR)
        {
            // !$SPF PARALLEL

            // SHADOW (VAR(list of  shadows)) / ACROSS (VAR(list of  shadows))
            vector<pair<pair<Symbol*, string>, vector<pair<int, int>>>> data;
            fillShadowAcrossFromComment(SHADOW_OP, new Statement(attributeStatement), data);
            fillShadowAcrossFromComment(ACROSS_OP, new Statement(attributeStatement), data);
            if (data.size())
            {
                bool result = checkShadowAcross(st, attributeStatement, data, messagesForFile);
                retVal = retVal && result;
            }

            // REMOTE_ACCESS (EXPR)
            map<pair<Symbol*, string>, Expression*> remote;
            fillRemoteFromComment(new Statement(attributeStatement), remote, true);
            if (remote.size())
            {
                bool result = checkRemote(st, attributeStatement, remote, messagesForFile);
                retVal = retVal && result;
            }
        }
        else if (type == SPF_TRANSFORM_DIR)
        {
            // !$SPF TRANSFORM

            // NOINLINE
            if (isSPF_NoInline(new Statement(st)))
            {
                SgStatement *prev = st->lexPrev();
                const int prevVar = prev->variant();
                if (prevVar != PROC_HEDR && prevVar != FUNC_HEDR)
                {
                    BAD_POSITION_FULL(ERROR, "after", RR1_4, "", L"", "function statements", RR1_6, attributeStatement->lineNumber());
                    retVal = false;
                }
            }

            // FISSION
            if (isSPF_OP(attributeStatement, SPF_FISSION_OP) && (count = countSPF_OP(st, SPF_TRANSFORM_DIR, SPF_FISSION_OP)))
            {
                attributeStatement->setLocalLineNumber(-1);
                if (count > 1 || st->variant() != FOR_NODE)
                {
                    BAD_POSITION_FULL(ERROR, "once", RR1_7, "before", RR1_1, "DO statement", RR1_3, attributeStatement->lineNumber());
                    retVal = false;
                }
                else
                {
                    bool result = checkFissionPrivatesExpansion(st, attributeStatement, currFile, messagesForFile, usersDirectives, true);
                    retVal = retVal && result;
                }
            }

            // PRIVATES_EXPANSION
            if (isSPF_OP(attributeStatement, SPF_EXPAND_OP) && (count = countSPF_OP(st, SPF_TRANSFORM_DIR, SPF_EXPAND_OP)))
            {
                attributeStatement->setLocalLineNumber(-1);
                if (count > 1 || st->variant() != FOR_NODE)
                {
                    BAD_POSITION_FULL(ERROR, "once", RR1_7, "before", RR1_1, "DO statement", RR1_3, attributeStatement->lineNumber());
                    retVal = false;
                }
                else
                {
                    bool result = checkFissionPrivatesExpansion(st, attributeStatement, currFile, messagesForFile, usersDirectives);
                    retVal = retVal && result;
                }
            }

            // SHRINK
            if (isSPF_OP(attributeStatement, SPF_SHRINK_OP))
            {
                attributeStatement->setLocalLineNumber(-1);
                if (st->variant() != FOR_NODE)
                {
                    BAD_POSITION_FULL(ERROR, "", "", "before", RR1_1, "DO statement", RR1_3, attributeStatement->lineNumber());
                    retVal = false;
                }
                else
                {
                    bool result = checkShrink(st, attributeStatement, currFile, messagesForFile);
                    retVal = retVal && result;
                }
            }

            // UNROLL
            if (isSPF_OP(attributeStatement, SPF_UNROLL_OP))
            {
                attributeStatement->setLocalLineNumber(-1);
                if (st->variant() != FOR_NODE)
                {
                    BAD_POSITION_FULL(ERROR, "", "", "before", RR1_1, "DO statement", RR1_3, attributeStatement->lineNumber());
                    retVal = false;
                }
            }

            // MERGE
            if (isSPF_OP(attributeStatement, SPF_MERGE_OP))
            {
                attributeStatement->setLocalLineNumber(-1);
                if (st->variant() != FOR_NODE)
                {
                    BAD_POSITION_FULL(ERROR, "", "", "before", RR1_1, "DO statement", RR1_3, attributeStatement->lineNumber());
                    retVal = false;
                }
            }

            //MERGE_ARRAYS
            if (isSPF_OP(attributeStatement, SPF_MERGE_ARRAYS_OP))
            {
                attributeStatement->setLocalLineNumber(-1);
                if (!isSgDeclarationStatement(st))
                {
                    BAD_POSITION_FULL(ERROR, "", "", "before", RR1_1, "declataion statement", RR1_9, attributeStatement->lineNumber());
                    retVal = false;
                }
                else
                {                    
                    pair<Symbol*, Symbol*> toReplacePair;
                    fillMergeArraysFromComment(new Statement(attributeStatement), toReplacePair);

                    set<Symbol*> toCheckDecl = { toReplacePair.first };
                    bool result = checkDeclaration(st, attributeStatement, toCheckDecl, messagesForFile);
                    retVal = retVal && result;
                }
            }
        }
        else if (type == SPF_CHECKPOINT_DIR)
        {
            map<int, Expression*> clauses;
            set<Symbol*> vars, expt;
            fillCheckpointFromComment(new Statement(attributeStatement), clauses, vars, expt);
            if (clauses.size())
            {
                bool result = checkCheckpoint(st, attributeStatement, clauses, vars, expt, messagesForFile);
                retVal = retVal && result;
            }
        }
    }

    return retVal;
}

static bool checkContainsRegion(SgStatement* st, const string& currFile,
                                const map<string, CommonBlock*>* commonBlocks, vector<Messages>& messagesForFile, 
                                map<pair<string, int>, set<SgStatement*>>& usersDirectives)
{
    bool retVal = true;
    if (st->variant() != CONTAINS_STMT)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    
    do 
    {
        st = st->lexNext();
        bool result = processStat(st, currFile, commonBlocks, messagesForFile, usersDirectives);
        retVal = retVal && result;
    } while (!isSgProgHedrStmt(st) && st->variant() != CONTROL_END);

    return retVal;
}

static bool processModules(vector<SgStatement*> &modules, const string &currFile, const map<string, CommonBlock*> *commonBlocks, 
                           vector<Messages> &messagesForFile, const set<string>& allFileNames,
                           map<pair<string, int>, set<SgStatement*>>& usersDirectives)
{
    bool retVal = true;

    for (int i = 0; i < modules.size(); ++i)
    {
        SgStatement *modIterator = modules[i];
        SgStatement *modEnd = modules[i]->lastNodeOfStmt();
        
        // skip included file
        const string modFile = modIterator->fileName();
        if (modFile != currFile && allFileNames.find(modFile) != allFileNames.end())
            continue;

        do
        {
            modIterator = modIterator->lexNext();
            if (modIterator->variant() == CONTAINS_STMT)
            {
                bool result = checkContainsRegion(modIterator, currFile, commonBlocks, messagesForFile, usersDirectives);
                retVal = retVal && result;
                break;
            }

            bool result = processStat(modIterator, currFile, commonBlocks, messagesForFile, usersDirectives);
            retVal = retVal && result;

            SgStatement *next = modIterator->lexNext();
            if (next)
                if (next->variant() == SPF_END_PARALLEL_REG_DIR)
                    addToattribute(next, modIterator, SPF_END_PARALLEL_REG_DIR);


        } while (modIterator != modEnd);
    }

    return retVal;
}

bool check_par_reg_dirs(SgFile *file, vector<Messages> &messagesForFile)
{
    int funcNum = file->numberOfFunctions();
    const string currFile = file->filename();

    bool noError = true;

    for (int i = 0; i < funcNum; ++i)
    {
        SgStatement *st = file->functions(i);
        SgStatement *lastNode = st->lastNodeOfStmt();
        while (st != lastNode)
        {            
            if (st == NULL)
            {
                __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 (st->variant() == SPF_PARALLEL_REG_DIR || st->variant() == SPF_END_PARALLEL_REG_DIR)
            {
                bool result = checkParallelRegions(st, NULL, messagesForFile);
                noError = noError && result;
            }                       
            st = st->lexNext();
        }
    }

    return noError;
}

static void distributeAnalysisWithCover(SgFile* file)
{
    int funcNum = file->numberOfFunctions();
    const string currFile = file->filename();
    
    for (int i = 0; i < funcNum; ++i)
    {
        SgStatement* st = file->functions(i);
        SgStatement* lastNode = st->lastNodeOfStmt();

        map<SgStatement*, pair<set<SgStatement*>, int>> spfAnalysis;
        do
        {
            st = st->lexNext();
            if (st == NULL)
            {
                __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 (st->variant() == FOR_NODE)
            {
                pair<set<SgStatement*>, int> newData = { set<SgStatement*>(), 0 };
                for (auto& data : getAttributes<SgStatement*, SgStatement*>(st, set<int>{ SPF_ANALYSIS_DIR }))
                {
                    newData.first.insert(data);
                    int cover = getCoverPropertyFromComment(new Statement(data));
                    if (cover != 0)
                        newData.second = cover;
                }
                if (newData.first.size())
                    spfAnalysis[st] = newData;
            }
        } while (st != lastNode);

        for (auto& data : spfAnalysis)
        {
            SgForStmt* st = isSgForStmt(data.first);
            checkNull(st, convertFileName(__FILE__).c_str(), __LINE__);

            int level = st->isPerfectLoopNest();
            if (data.second.second < level && data.second.second != 0)
                level = data.second.second;

            for (int z = 0; z < level - 1; ++z)
            {
                st = isSgForStmt(st->lexNext());
                checkNull(st, convertFileName(__FILE__).c_str(), __LINE__);

                for (auto& dirs : data.second.first)
                {
                    auto copy = dirs->copyPtr();
                    copy->setLocalLineNumber(SPF_USER_DIR_COPY);
                    st->addAttribute(copy->variant(), copy, sizeof(SgStatement));
                }
            }
        }
    }
}

bool preprocess_spf_dirs(SgFile *file, const map<string, CommonBlock*> &commonBlocks, vector<Messages> &messagesForFile, const set<string>& allFileNames,
                         map<pair<string, int>, set<SgStatement*>>& usersDirectives)
{
    int funcNum = file->numberOfFunctions();
    const string currFile = file->filename();
    bool noError = true;

    for (int i = 0; i < funcNum; ++i)
    {
        SgStatement *st = file->functions(i);
        SgStatement *lastNode = st->lastNodeOfStmt();

        do
        {
            st = st->lexNext();
            if (st == NULL)
            {
                __spf_print(1, "internal error in analysis, parallel directives will not be generated for this file!\n");
                break;
            }

            if (st->variant() == CONTAINS_STMT)
            {
                bool result = checkContainsRegion(st, currFile, &commonBlocks, messagesForFile, usersDirectives);
                noError = noError && result;
                break;
            }

            bool result = processStat(st, currFile, &commonBlocks, messagesForFile, usersDirectives);
            noError = noError && result;

            SgStatement *next = st->lexNext();
            if (next)
                if (next->variant() == SPF_END_PARALLEL_REG_DIR)
                    addToattribute(next, st, SPF_END_PARALLEL_REG_DIR);
        } while (st != lastNode);
    }

    vector<SgStatement*> modules;
    findModulesInFile(file, modules);
    bool result = processModules(modules, currFile, &commonBlocks, messagesForFile, allFileNames, usersDirectives);
    noError = noError && result;

    if (noError)
        distributeAnalysisWithCover(file);

    return noError;
}

static void LinkTree(SgExpression *mainExp, SgExpression *exp) 
{
    SgExpression *copyExp = &(exp->copy());
    SgExpression *rhs = mainExp->rhs();
    while (rhs) 
    {
        SgExpression *a = rhs;
        rhs = mainExp->rhs();
        mainExp = a;
    }
    mainExp->setRhs(copyExp);
}       

static void OptimizeTree(SgExpression *exp) 
{
    while (exp) 
    {
        SgExpression *checkExp = exp->lhs();
        SgExpression *currExp = exp->rhs();

        SgExpression *prevExp = exp;
        int var = checkExp->variant();

        while (currExp) 
        {
            SgExpression *lhs = currExp->lhs();
            SgExpression *rhs = currExp->rhs();
            if (lhs) 
            {
                if (lhs->variant() == var) 
                {
                    prevExp->setRhs(rhs);
                    LinkTree(checkExp->lhs(), lhs->lhs());
                }
                else
                    prevExp = currExp;
                currExp = rhs;
            }
        }
        exp = exp->rhs();
    }
}

static SgStatement* UniteAttributes(const vector<SgStatement*> &sameAtt) 
{
    set<string> uniqAttrs;
    SgStatement *toAddExp = NULL;
    for (auto &elem : sameAtt) 
    {
        if (elem->expr(0) == NULL)
            continue;

        const string currAtr(elem->unparse());
        auto it = uniqAttrs.find(currAtr);
        if (it == uniqAttrs.end())
            uniqAttrs.insert(it, currAtr);
        else
            continue;

        if (toAddExp) 
        {
            SgExpression *exp = elem->expr(0);
            LinkTree(toAddExp->expr(0), exp);
        }
        else
            toAddExp = &(elem->copy());
    }
    
    if (toAddExp)
        OptimizeTree(toAddExp->expr(0));

    //check null of expressions
    if (toAddExp)
    {
        vector<SgExpression*> list;
        SgExpression* ex = toAddExp->expr(0);

        bool deleted = false;
        while (ex)
        {
            if (ex->lhs()->lhs())
                list.push_back(ex->lhs());
            else
                deleted = true;
            ex = ex->rhs();
        }
        if (deleted)
            toAddExp->setExpression(0, makeExprList(list));
    }

    return toAddExp;
}

vector<SgStatement*> filterUserSpf(const vector<SgStatement*> &toFilter, bool with_omp)
{
    vector<SgStatement*> ret;
    for (auto &elem : toFilter)
        if (elem->localLineNumber() == SPF_USER_DIR || (elem->localLineNumber() == SPF_OMP_DIR && with_omp)) // user and omp
            ret.push_back(elem);

    return ret;
}

//move PARAMETER to operator after a(1:N) = 0 to loop convertation
static bool moveSpfParameterForImplicitLoops(SgStatement* st, SgStatement* toAdd)
{
    bool moveNext = false;
    if (st->lineNumber() < 0 && st->variant() == FOR_NODE)
    {
        for (auto ex = toAdd->expr(0); ex; ex = ex->rhs())
            if (ex->lhs() && ex->lhs()->variant() == SPF_PARAMETER_OP)
                moveNext = true;

        if (moveNext)
        {
            int countOp = 0;
            for (auto t = st->lexNext(); t != st->lastNodeOfStmt(); t = t->lexNext())
                countOp++;

            if (countOp == 1)
                st->lexNext()->insertStmtBefore(*toAdd, *st->lexNext()->controlParent());
            else
                moveNext = false;
        }
    }
    return moveNext;
}

static void insertBefore(SgStatement* st, SgStatement* toAdd)
{
    if (toAdd == NULL)
        return;

    st->insertStmtBefore(*toAdd, *st->controlParent());
    if (st->variant() == FOR_NODE)
    {
        auto com = st->comments();
        if (com)
        {
            st->lexPrev()->addComment(com);
            st->delComments();
        }
    }
}

void revertion_spf_dirs(SgFile *file,
                        map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>> declaredArrays,
                        map<SgStatement*, set<tuple<int, string, string>>> declaratedArraysSt)
{
    const string fileName(file->filename());

    vector<SgStatement*> units;
    getModulesAndFunctions(file, units);

    for (int i = 0; i < units.size(); ++i)
    {
        SgStatement *st = units[i];
        SgStatement *lastNode = st->lastNodeOfStmt();

        for ( ; st != lastNode; st = st->lexNext()) 
        {
            if (st == NULL) 
            {
                __spf_print(1, "internal error in analysis, spf directives will not be returned for this file!\n");
                break;
            }

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

            if (st->variant() < 0)
                continue;

            //analyze attributes
            SgAttribute *atrib = st->getAttribute(0);
            SgStatement *toAdd = NULL;

            if (atrib && st->fileName() == fileName)
            {
                //check previosly directives SPF_ANALYSIS
                vector<SgStatement*> sameAtt = filterUserSpf(getAttributes<SgStatement*, SgStatement*>(st, set<int>{SPF_ANALYSIS_DIR}));

                if (sameAtt.size())
                {
                    toAdd = UniteAttributes(sameAtt);
                    if (toAdd)
                        if (!moveSpfParameterForImplicitLoops(st, toAdd))
                            insertBefore(st, toAdd);
                }

                //check previosly directives SPF_PARALLEL
                if (sameAtt.size())
                {
                    sameAtt = filterUserSpf(getAttributes<SgStatement*, SgStatement*>(st, set<int>{SPF_PARALLEL_DIR}));
                    for (auto &elem : sameAtt)
                    {
                        if (toAdd)
                            toAdd = UniteAttributes(sameAtt);
                        insertBefore(st, toAdd);
                    }
                } 

                //remaining directives
                sameAtt = filterUserSpf(getAttributes<SgStatement*, SgStatement*>(st, set<int>{SPF_CHECKPOINT_DIR, SPF_TRANSFORM_DIR, SPF_NOINLINE_OP, SPF_REGION_NAME}));
                if (sameAtt.size())
                {
                    for (auto &elem : sameAtt)
                    {
                        SgStatement *data = (SgStatement *)atrib->getAttributeData();
                        SgStatement *toAdd = &(data->copy());

                        if (toAdd)
                            insertBefore(st, toAdd);
                    }
                }
            }
        }
    }
}

void addAcrossToLoops(LoopGraph *topLoop,
                      const map<SgSymbol*, tuple<int, int, int>> &acrossToAdd,
                      const map<int, SgForStmt*> &allLoops,
                      vector<Messages> &currMessages)
{
    if (acrossToAdd.size() != 0)
    {
        SgStatement *acrSpf = new SgStatement(SPF_PARALLEL_DIR, NULL, NULL, NULL, NULL, NULL);
        SgExpression *exprList = new SgExpression(EXPR_LIST, new SgExpression(ACROSS_OP), NULL, NULL);
        acrSpf->setExpression(0, *exprList);
        exprList = exprList->lhs();
        SgExpression *newList = new SgExpression(EXPR_LIST);
        exprList->setLhs(newList);
        exprList = exprList->lhs();
        
        int k = 0;
        for (auto it = acrossToAdd.begin(); it != acrossToAdd.end(); ++it, ++k)
        {
            SgSymbol *arrayS = it->first;
            int pos = std::get<2>(it->second);
            pair<int, int> ref = std::make_pair(std::get<0>(it->second), std::get<1>(it->second));

            SgArrayType *arrType = isSgArrayType(arrayS->type());
            if (!arrType)
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            if (arrType->dimension() <= pos)
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            SgArrayRefExp *toAdd = new SgArrayRefExp(*arrayS);
            for (int z = 0; z < arrType->dimension(); ++z)
            {
                if (z == pos)
                {
                    SgValueExp *tmp = new SgValueExp(ref.first);
                    SgValueExp *tmp1 = new SgValueExp(ref.second);
                    SgExpression *tmp2 = new SgExpression(DDOT, tmp, tmp1, NULL);
                    toAdd->addSubscript(*tmp2);
                }
                else
                {
                    SgValueExp *tmp = new SgValueExp(0);
                    SgValueExp *tmp1 = new SgValueExp(0);
                    SgExpression *tmp2 = new SgExpression(DDOT, tmp, tmp1, NULL);
                    toAdd->addSubscript(*tmp2);
                }
            }

            exprList->setLhs(toAdd);

            if (consoleMode)
                printf("  add across dependencies by array '%s' for loop on line %d\n", arrayS->identifier(), topLoop->lineNum);

            wstring messageE, messageR;
            __spf_printToLongBuf(messageE, L"add across dependencies by array '%s' to loop", to_wstring(arrayS->identifier()).c_str());

            __spf_printToLongBuf(messageR, R108, to_wstring(arrayS->identifier()).c_str());

            currMessages.push_back(Messages(NOTE, topLoop->lineNum, messageR, messageE, 3001));

            if (k != acrossToAdd.size() - 1)
            {
                SgExpression *tmp = new SgExpression(EXPR_LIST);
                exprList->setRhs(tmp);
                exprList = exprList->rhs();
            }
        }
        
        auto it = allLoops.find(topLoop->lineNum);
        if (it == allLoops.end())
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        it->second->addAttribute(SPF_PARALLEL_DIR, acrSpf, sizeof(SgStatement));
    }
}

void addPrivatesToLoops(LoopGraph *topLoop, 
                        const vector<const depNode*> &privatesToAdd,
                        const map<int, SgForStmt*> &allLoops,
                        vector<Messages> &currMessages)
{
    if (privatesToAdd.size() != 0)
    {
        vector<const depNode*> addForCurrLoop;
        for (int i = 0; i < privatesToAdd.size(); ++i)
        {
            const int privLine = privatesToAdd[i]->stmtin->lineNumber();
            if (topLoop->lineNum <= privLine && topLoop->lineNumAfterLoop > privLine)
                addForCurrLoop.push_back(privatesToAdd[i]);
        }

        if (addForCurrLoop.size())
        {
            SgStatement *privSpf = new SgStatement(SPF_ANALYSIS_DIR, NULL, NULL, NULL, NULL, NULL);

            SgExpression *tmp = new SgExpression(ACC_PRIVATE_OP);
            SgExpression *exprList = new SgExpression(EXPR_LIST, tmp, NULL, NULL);
            privSpf->setExpression(0, *exprList);
            exprList = exprList->lhs();

            SgExpression *tmp1 = new SgExpression(EXPR_LIST);
            exprList->setLhs(tmp1);
            exprList = exprList->lhs();

            auto itLoop = allLoops.find(topLoop->lineNum);
            if (itLoop == allLoops.end())
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            set<string> added;
            for (auto &data : getAttributes<SgStatement*, SgStatement*>(itLoop->second, set<int>{ SPF_ANALYSIS_DIR }))
                fillPrivatesFromComment(new Statement(data), added);

            int uniq = 0;
            int k = 0;
            while (k != addForCurrLoop.size())
            {
                string identifier = addForCurrLoop[k]->varin->symbol()->identifier();
                auto itF = added.find(identifier);
                if (itF == added.end())
                    itF = added.insert(itF, identifier);
                else
                {
                    addForCurrLoop.erase(addForCurrLoop.begin() + k);
                    continue;
                }
                k++;
                uniq++;
            }

            for (int k = 0; k < addForCurrLoop.size(); ++k)
            {
                string identifier = addForCurrLoop[k]->varin->symbol()->identifier();
                exprList->setLhs(addForCurrLoop[k]->varin);
                if (consoleMode)
                    printf("  add private scalar '%s' for loop on line %d\n", identifier.c_str(), topLoop->lineNum);

                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"add private scalar '%s' to loop on line %d", to_wstring(identifier).c_str(), topLoop->lineNum);
                __spf_printToLongBuf(messageR, R109, to_wstring(identifier).c_str(), topLoop->lineNum);

                currMessages.push_back(Messages(NOTE, addForCurrLoop[k]->stmtin->lineNumber(), messageR, messageE, 3002));

                if (k != addForCurrLoop.size() - 1)
                {
                    SgExpression *tmp = new SgExpression(EXPR_LIST);
                    exprList->setRhs(tmp);
                    exprList = exprList->rhs();
                }
            }

            if (uniq > 0)
                itLoop->second->addAttribute(SPF_ANALYSIS_DIR, privSpf, sizeof(SgStatement));
        }
    }
}

static bool addReductionToList(const char *oper, SgExpression *exprList, SgExpression *varin)
{
    SgExpression *tmp3 = new SgKeywordValExp(oper);
    SgExpression *tmp4 = new SgExpression(ARRAY_OP, tmp3, varin, NULL);
    exprList->setLhs(tmp4);
    return true;
}

void addReductionsToLoops(LoopGraph *topLoop,
                          const vector<const depNode*> &reductionsToAdd,
                          const map<int, SgForStmt*> &allLoops,
                          vector<Messages> &currMessages)
{
    if (reductionsToAdd.size() != 0)
    {
        vector<const depNode*> addForCurrLoop;
        for (int i = 0; i < reductionsToAdd.size(); ++i)
        {
            const int privLine = reductionsToAdd[i]->stmtin->lineNumber();
            if (topLoop->lineNum < privLine && topLoop->lineNumAfterLoop > privLine)
                addForCurrLoop.push_back(reductionsToAdd[i]);
        }

        if (addForCurrLoop.size())
        {
            SgStatement *redSpf = new SgStatement(SPF_ANALYSIS_DIR, NULL, NULL, NULL, NULL, NULL);

            SgExpression *tmp = new SgExpression(REDUCTION_OP);
            SgExpression *exprList = new SgExpression(EXPR_LIST, tmp, NULL, NULL);
            redSpf->setExpression(0, *exprList);
            exprList = exprList->lhs();

            SgExpression *tmp1 = new SgExpression(EXPR_LIST);
            exprList->setLhs(tmp1);
            exprList = exprList->lhs();

            bool wasAdd = false;
            for (int k = 0; k < addForCurrLoop.size(); ++k)
            {
                if (k != 0 && wasAdd)
                {
                    SgExpression *tmp = new SgExpression(EXPR_LIST);
                    exprList->setRhs(tmp);
                    exprList = exprList->rhs();
                }

                wasAdd = false;
                const char *oper = NULL;
                switch (addForCurrLoop[k]->kinddep)
                {
                case SADDREDUCTION:
                case DADDREDUCTION:
                case IADDREDUCTION:
                    oper = "sum";
                    wasAdd = addReductionToList(oper, exprList, addForCurrLoop[k]->varin);
                    break;
                case SMULREDUCTION:
                case DMULREDUCTION:
                case IMULREDUCTION:
                    oper = "product";
                    wasAdd = addReductionToList(oper, exprList, addForCurrLoop[k]->varin);
                    break;
                case SDIVREDUCTION:
                case DDIVREDUCTION:
                case IDIVREDUCTION:
                    break;
                case SMAXREDUCTION:
                case DMAXREDUCTION:
                case IMAXREDUCTION:
                    oper = "max";
                    wasAdd = addReductionToList(oper, exprList, addForCurrLoop[k]->varin);
                    break;
                case SMINREDUCTION:
                case DMINREDUCTION:
                case IMINREDUCTION:
                    oper = "min";
                    wasAdd = addReductionToList(oper, exprList, addForCurrLoop[k]->varin);
                    break;
                case ANDREDUCTION:
                    oper = "and";
                    wasAdd = addReductionToList(oper, exprList, addForCurrLoop[k]->varin);
                    break;
                case ORREDUCTION:
                    oper = "or";
                    wasAdd = addReductionToList(oper, exprList, addForCurrLoop[k]->varin);
                    break;
                case EQVREDUCTION:
                    oper = "eqv";
                    wasAdd = addReductionToList(oper, exprList, addForCurrLoop[k]->varin);
                    break;
                case NEQVREDUCTION:
                    oper = "neqv";
                    wasAdd = addReductionToList(oper, exprList, addForCurrLoop[k]->varin);
                    break;
                default:
                    break;
                }

                if (wasAdd)
                {
                    if (consoleMode)
                        printf("  add reduction scalar '%s' with operation '%s' to loop on line %d\n", addForCurrLoop[k]->varin->symbol()->identifier(), oper, topLoop->lineNum);

                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"add reduction scalar '%s' with operation '%s' to loop on line %d", 
                                         to_wstring(addForCurrLoop[k]->varin->symbol()->identifier()).c_str(), to_wstring(oper).c_str(), topLoop->lineNum);

                    __spf_printToLongBuf(messageR, R110,
                                         to_wstring(addForCurrLoop[k]->varin->symbol()->identifier()).c_str(), to_wstring(oper).c_str(), topLoop->lineNum);

                    currMessages.push_back(Messages(NOTE, addForCurrLoop[k]->stmtin->lineNumber(), messageR, messageE, 3003));
                }
                else
                {
                    if (consoleMode)
                        printf("  unknown type of reduction scalar '%s' on line %d\n", addForCurrLoop[k]->varin->symbol()->identifier(), addForCurrLoop[k]->stmtin->lineNumber());

                    wstring messageE, messageR;
                    __spf_printToLongBuf(messageE, L"unknown type of reduction scalar '%s'", to_wstring(addForCurrLoop[k]->varin->symbol()->identifier()).c_str());

                    __spf_printToLongBuf(messageR, R111, to_wstring(addForCurrLoop[k]->varin->symbol()->identifier()).c_str());

                    currMessages.push_back(Messages(WARR, addForCurrLoop[k]->stmtin->lineNumber(), messageR, messageE, 3004));

                    topLoop->hasUnknownScalarDep = true;
                    topLoop->linesOfScalarDep.push_back(addForCurrLoop[k]->stmtin->lineNumber());
                }
            }

            auto it = allLoops.find(topLoop->lineNum);
            if (it == allLoops.end())
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            it->second->addAttribute(SPF_ANALYSIS_DIR, redSpf, sizeof(SgStatement));
        }
    }
}