#include "leak_detector.h"

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

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

#include <vector>
#include <map>
#include <queue>
#include <set>
#include <utility>
#include <string>
#include <assert.h>
#include <locale>
#include <algorithm>
#include <fcntl.h>
#include <mutex>
#include <thread>
#include <stack>

#ifdef _WIN32
#include <direct.h>
#else
#include <unistd.h> 
#endif

#include "SgUtils.h"
#include "errors.h"
#include "../Sapfor.h"

#include "../DirectiveProcessing/directive_parser.h"
#include "../Distribution/Distribution.h"

#include "graph_calls.h"
#include "graph_calls_func.h"
#include "../CreateInterTree/CreateInterTree.h"
#include "../Predictor/PredictScheme.h"
#include "../VisualizerCalls/get_information.h"
#include "../VisualizerCalls/SendMessage.h"
#include "../VerificationCode/verifications.h"
#include "../LoopAnalyzer/loop_analyzer.h"

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

static bool ifIntervalExists(const vector<pair<int, int>> &intervals, const pair<int, int> &toFind, pair<int, int>& inInterval)
{
    bool retVal = false;

    for (auto &elem : intervals)
    {
        if (toFind.first <= elem.first && toFind.second >= elem.second)
        {
            inInterval = elem;
            retVal = true;
            break;
        }
    }

    return retVal;
}

static bool ifDir(const string &line)
{
    if (line.size() >= 10 && (line[0] == '!' || line[0] == 'c'))
    {
        string dir(line.begin() + 1, line.begin() + 1 + 4);
        if (dir == "$spf" || dir == "dvm$")
            return true;
    }
    return false;
}
 
static string replaceTabsToSpaces(const string &in)
{
    string ret = ""; 
    for (auto &elem : in)
    {
        if (elem == '\t')
            ret += "    ";
        else
            ret += elem;
    }
    return ret;
}

static map<string, pair<string, vector<pair<int, int>> > > findIncludes(FILE *currFile)
{
    map<string, pair<string, vector<pair<int, int>> > > includeFiles;
    bool notClosed = false;
    // TODO: extend buff size in dynamic
    char buf[8192];
    int lineBefore = 0;
    while (!feof(currFile))
    {
        char *read = fgets(buf, 8192, currFile);
        if (read)
        {
            const string orig(replaceTabsToSpaces(read));
            string line(read);
            convertToLower(line);
            line = replaceTabsToSpaces(line);

            size_t posF = line.find("include");
            if (posF != string::npos)
            {
                //check if 'inclide' - operator
                for (size_t k = 0; k < posF; ++k)
                {
                    if (line[k] != ' ')
                    {
                        posF = -1;
                        break;
                    }
                }
                if (posF == -1)
                {
                    ++lineBefore;
                    continue;
                }

                posF += sizeof("include") - 1;
                int tok = 0;
                size_t st = string::npos, en = string::npos;

                for (size_t k = posF; k < line.size(); ++k)
                {
                    bool isQuote = line[k] == '\'' || line[k] == '\"';
                    if (isQuote && tok == 1)
                        break;
                    else if (isQuote)
                        tok++;
                    else if (tok == 1 && st == string::npos)
                        st = k;
                    else
                        en = k;
                }
                if (st == string::npos || st >= line.size() || en == string::npos || en >= line.size())
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                const string inclName(orig.begin() + st, orig.begin() + en + 1);

                auto toInsert = includeFiles.find(inclName);
                if (toInsert == includeFiles.end())
                {
                    toInsert = includeFiles.insert(toInsert, make_pair(inclName, make_pair(orig, vector<pair<int, int>>())));
                    toInsert->second.second.push_back(make_pair(lineBefore, -1));
                    notClosed = true;
                }
                else
                {
                    toInsert->second.second.push_back(make_pair(lineBefore, -1));
                    notClosed = true;
                }
                //printf("insert %s -> %s\n", inclName.c_str(), line.c_str());
            }
            else if ((line[0] != 'c' && line[0] != '!' && line != "" && line[0] != '\n') || ifDir(line))
            {
                if (notClosed)
                {
                    for (auto &elem : includeFiles)
                        for (auto &pair : elem.second.second)
                            if (pair.second == -1)
                                pair.second = lineBefore + 1;
                    notClosed = false;
                }
            }
        }
        ++lineBefore;
    }

    return includeFiles;
}

static vector<string> findCommentsInIncludes(map<string, pair<string, vector<pair<int, int>> > >& includeFiles)
{
    vector<string> foundComments;

    for (auto& elem : includeFiles)
    {
        FILE* inclFile = fopen(elem.first.c_str(), "r");
        if (inclFile == NULL)
        {
            char buf[256];
            sprintf(buf, "ERROR: Can't open inlcude file %s for read with err %d\n", elem.first.c_str(), errno);
            addToGlobalBufferAndPrint(buf);
            continue;
        }

        char buf[8192];
        int lineBefore = 0;
        string newComment = "";
        while (!feof(inclFile))
        {
            char* read = fgets(buf, 8192, inclFile);
            if (read)
            {
                string orig(replaceTabsToSpaces(read));
                if (orig.size() && (orig[0] == 'C' || orig[0] == 'c'))
                {
                    orig[0] = '!';
                    newComment += orig;
                    continue;
                }

                int k = 0;
                while (k < orig.size())
                {
                    if (orig[k] == ' ')
                        ++k;
                    else
                        break;
                }

                // remove first spaces
                if (k > 0)
                    orig.erase(orig.begin(), orig.begin() + k);

                if (orig.size())
                {
                    if (orig[0] == '!')
                        newComment += read;
                    else if (newComment != "")
                    {
                        foundComments.push_back(newComment);
                        newComment = "";
                    }
                }
                else if (newComment != "")
                {
                    foundComments.push_back(newComment);
                    newComment = "";
                }
            }
        }

        if (newComment != "")
        {
            foundComments.push_back(newComment);
            newComment = "";
        }

        fclose(inclFile);
    }

    return foundComments;
}

static int reverseVar(SgStatement* st)
{
    st->setVariant(-1 * st->variant());
    return st->id();
}

static map<SgStatement*, map<int, set<string>>> insertedIncludes;
static set<SgFile*> genVersionDone;

static set<int> hideUnnecessary(SgFile* file, const string& fileN, const set<string>& moduleDeclsInFiles, bool dontReplaceIncludes)
{
    set<int> changedVars;
    for (SgStatement* st = file->firstStatement(); st; st = st->lexNext())
    {
        if (st->variant() == GLOBAL)
            continue;

        if (dontReplaceIncludes == false)
        {
            if (st->fileName() != fileN || st->getUnparseIgnore())
                if (st->variant() > 0)
                    changedVars.insert(reverseVar(st));
        }
        else
        {
            if (st->getUnparseIgnore())
            {
                if (st->variant() > 0)
                    changedVars.insert(reverseVar(st));
            }
            else if (st->fileName() != fileN)
            {
                if (st->variant() == MODULE_STMT && moduleDeclsInFiles.find(st->fileName()) != moduleDeclsInFiles.end())
                {
                    for (auto mSt = st; mSt != st->lastNodeOfStmt(); mSt = mSt->lexNext())
                        changedVars.insert(reverseVar(mSt));

                    st = st->lastNodeOfStmt();
                    changedVars.insert(reverseVar(st));
                }
            }
        }
    }

    return changedVars;
}

//TODO: read includes and find last lines, all included files
string removeIncludeStatsAndUnparse(SgFile *file, const char *fileName, const char *fout, 
                                    set<string> &allIncludeFiles, bool outFree, 
                                    const map<string, set<string>>& moduleUsesByFile, const map<string, string>& moduleDelcs, 
                                    const map<SgStatement*, vector<SgStatement*>>& exctactedModuleStats,
                                    bool toString, bool renameIncludes, bool dontReplaceIncludes)
{
    removeSpecialCommentsFromProject(file);

    if (genVersionDone.find(file) == genVersionDone.end())
    {
        auto firstStat = file->firstStatement();
        auto com = printVersionAsFortranComm();
        if (com != "")
            firstStat->addComment(com.c_str());
        genVersionDone.insert(file);
    }

    fflush(NULL);

    const set<string> modulesExclude = getExcludedModules();

    set<string> moduleIncudeUses;
    auto itM = moduleUsesByFile.find(fileName);
    if (itM != moduleUsesByFile.end())
    {
        for (auto& elem : itM->second)
        {
            auto it2 = moduleDelcs.find(elem);
            if (it2 == moduleDelcs.end())
            {
                if (modulesExclude.find(elem) != modulesExclude.end())
                    continue;

                __spf_print(1, "can not find module '%s'\n", elem.c_str());
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            }
            string lowerInclude = it2->second;
            convertToLower(lowerInclude);

            moduleIncudeUses.insert(lowerInclude);
        }
    }

    int funcNum = file->numberOfFunctions();
    FILE *currFile = fopen(fileName, "r");
    if (currFile == NULL)
    {
        char buf[256];
        sprintf(buf, "ERROR: Can't open file %s for read with err %d\n", fileName, errno);
        addToGlobalBufferAndPrint(buf);
        char buff[1024];
        if (getcwd(buff, 1024) == NULL)
             ;//
        else
             __spf_print(1, "curr path %s\n", buff);
        throw(-1);
    }

    // name -> unparse comment and position
    map<string, pair<string, vector<pair<int, int>> > > includeFiles = findIncludes(currFile);
    fclose(currFile);

    const vector<string> commentsFromIncl = findCommentsInIncludes(includeFiles);

    //add spaces if needed
    if (!outFree)
    {
        for (auto &elem : includeFiles)
        {
            int countSpaces = 0;
            for (int z = 0; z < elem.second.first.size() && elem.second.first[z] == ' '; ++z, ++countSpaces) { }

            if (countSpaces < 6)
            {
                while (countSpaces != 6)
                {
                    elem.second.first.insert(elem.second.first.begin(), ' ');
                    countSpaces++;
                }
            }
        }
    }

    const string fileN = fileName;
    //insert comment
    int lineBefore = -1;

    map<string, set<SgStatement*>> insertedIncludeFiles;
    map<int, vector<pair<int, int>>> placesForInsert;

    set<string> foundForIncludes;
    map<int, int> stIdByLine;

    for (SgStatement* st = file->firstStatement(); st; st = st->lexNext())
    {
        if (st->variant() > 0 && st->lineNumber() > 0 && st->fileName() == string(file->filename()))
        {
            stIdByLine[st->lineNumber()] = st->id();
            vector<SgStatement*> attr = getAttributes<SgStatement*, SgStatement*>(st, set<int>{SPF_ANALYSIS_DIR, SPF_NOINLINE_OP});
            for (auto& elem : attr)
                if (stIdByLine.find(elem->lineNumber()) == stIdByLine.end())
                    stIdByLine[elem->lineNumber()] = st->id();
        }
    }

    auto fileInProject = getAllFilesInProject();

    for (SgStatement *st = file->firstStatement(); st; st = st->lexNext())
    {
        if (st->lineNumber() <= 0 || st->variant() < 0)
            continue;
        string currFileName = st->fileName();

        // included for module use
        if (currFileName != fileN && fileInProject.find(currFileName) != fileInProject.end())
        {
            st = st->lastNodeOfStmt();
            continue;
        }

        if (currFileName != fileN)
        {
            allIncludeFiles.insert(currFileName);
            auto it = includeFiles.find(currFileName);

            if (it != includeFiles.end())
            {
                foundForIncludes.insert(currFileName);
                pair<int, int> lines = make_pair(lineBefore, -1);

                SgStatement *locSt = st->lexNext();
                set<string> nearIncludes;

                while (locSt && (locSt->fileName() != fileN || locSt->lineNumber() <= 0))
                {
                    const string locName = locSt->fileName();

                    if (locName != fileN)
                    {
                        allIncludeFiles.insert(locName);
                        if (locName != currFileName)
                        {
                            auto itNear = includeFiles.find(locName);
                            if (itNear != includeFiles.end())
                            {
                                nearIncludes.insert(locName);
                                foundForIncludes.insert(locName);
                            }
                        }
                    }
                    locSt = locSt->lexNext();
                }

                if (locSt)
                {
                    lines.second = locSt->lineNumber();
                    st = locSt;

                    bool change = true;
                    while (change)
                    {
                        change = false;
                        SgStatement *prev = locSt->lexPrev();
                        if (prev && 
                            (prev->variant() == DVM_PARALLEL_ON_DIR || 
                             prev->variant() == SPF_ANALYSIS_DIR || 
                             prev->variant() == SPF_TRANSFORM_DIR ||
                             prev->variant() == DVM_INTERVAL_DIR || 
                             prev->variant() == ACC_REGION_DIR || 
                             prev->variant() == DVM_REALIGN_DIR ||
                             prev->variant() == DVM_REDISTRIBUTE_DIR))
                        {
                            locSt = prev;
                            change = true;
                        }
                    }
                    placesForInsert[locSt->id()].push_back(lines);
                }
            }           
        }
        else
            lineBefore = st->lineNumber();
    }

    //if not found, it indicates that file contains of other include files
    for (auto& incl : includeFiles)
    {
        if (foundForIncludes.find(incl.first) == foundForIncludes.end())
        {
            for (auto& interval : incl.second.second)
            {
                bool added = false;
                for (int z = interval.second; z > interval.first; z--)
                {
                    auto it = stIdByLine.find(z);
                    if (it != stIdByLine.end())
                    {
                        placesForInsert[it->second].push_back(interval);
                        added = true;
                        break;
                    }
                }

                if (added == false)
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            }
            
        }
    }

    for (SgStatement *st = file->firstStatement(); st; st = st->lexNext())
    {
        if (st->variant() < 0)
            continue;

        auto itGlobal = insertedIncludes.find(st);
        if (itGlobal == insertedIncludes.end())
            itGlobal = insertedIncludes.insert(itGlobal, make_pair(st, map<int, set<string>>()));
        map<int, set<string>>& toInsertIncludeComment = itGlobal->second;
        bool needUpdated = false;

        for (auto &it : includeFiles)
        {
            auto foundV = placesForInsert.find(st->id());
            if (foundV != placesForInsert.end())
            {                
                SgStatement* parent = getFuncStat(st, { BLOCK_DATA, MODULE_STMT });
                if(!parent)
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                for (auto& interval : foundV->second)
                {
                    pair<int, int> inInterval;
                    if (ifIntervalExists(it.second.second, interval, inInterval) && insertedIncludeFiles[it.first].find(parent) == insertedIncludeFiles[it.first].end())
                    {
                        allIncludeFiles.insert(it.first);
                        insertedIncludeFiles[it.first].insert(parent);

                        string lowerInclude = it.first;
                        convertToLower(lowerInclude);

                        if (moduleIncudeUses.find(lowerInclude) == moduleIncudeUses.end())
                        {
                            if (dontReplaceIncludes == false)
                            {
                                auto itI = toInsertIncludeComment.find(inInterval.first);
                                if (itI == toInsertIncludeComment.end() ||
                                    (itI != toInsertIncludeComment.end() && itI->second.find(it.second.first) == itI->second.end()))
                                {
                                    toInsertIncludeComment[inInterval.first].insert(it.second.first);
                                    needUpdated = true;
                                    __spf_print(1, "  --> restore include '%s' before statement on line %d\n", it.first.c_str(), st->lineNumber());
                                }
                            }
                        }
                    }
                }
            }
        }

        if (needUpdated)
        {
            string inlcude = "";
            for (auto& inclByPos : toInsertIncludeComment)
                for (auto& incl : inclByPos.second)
                    inlcude += (renameIncludes ? renameInclude(incl) : incl);

            //check inserted operators
            SgStatement* toInsert = st;
            do {
                auto lexPrev = toInsert->lexPrev();
                if (lexPrev && lexPrev->variant() > 0 && lexPrev->lineNumber() < 0)
                    toInsert = lexPrev;
                else
                    break;                
            } while (true);

            if (toInsert->comments())
                toInsert->setComments((inlcude + toInsert->comments()).c_str());
            else
                toInsert->addComment(inlcude.c_str());
        }
    }

    for (auto &inserted : insertedIncludeFiles)
    {
        auto it = includeFiles.find(inserted.first);
        if (it == includeFiles.end())
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        //TODO: dont work after subroutine copying 
        /*if (inserted.second.size() != it->second.second.size())
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);*/
    }

    set<string> moduleDeclsInFiles;
    for (auto& elem : moduleDelcs)
        moduleDeclsInFiles.insert(elem.second);

    //restore hidden modules
    vector<SgStatement*> toExtract;
    for (auto& mod : exctactedModuleStats)
    {
        auto cp = mod.first->controlParent();
        for (auto& toInsert : mod.second)
        {
            mod.first->insertStmtBefore(*toInsert, *cp);
            toExtract.push_back(toInsert);
        }
    }

    //remove unnecessary
    //XXX: use Sage hack!!
    set<int> changedVars = hideUnnecessary(file, fileN, moduleDeclsInFiles, dontReplaceIncludes);

    set<int> checkPositions;
    for (auto& elem : includeFiles)
        for (auto& interval : elem.second.second)
            checkPositions.insert(interval.second);
    
    // remove comments that were substituted from includes
    for (SgStatement* st = file->firstStatement(); st; st = st->lexNext())
    {
        const char* comments = st->comments();
        if (st->fileName() == fileN && st->variant() > 0 && comments && !dontReplaceIncludes)
        {
            if (checkPositions.find(st->lineNumber()) != checkPositions.end())
            {
                //printf("%s\n", comments);
                string str(comments);
                bool wasDeleted = false;
                for (auto& com : commentsFromIncl)
                {
                    auto it = str.find(com);
                    while (it != string::npos)
                    {
                        wasDeleted = true;
                        str.erase(it, com.size());

                        it = str.find(com);
                    }

                    if (str.size() == 0)
                        break;
                }

                if (wasDeleted)
                {
                    if (str.size() && str.back() != '\n')
                        str += '\n';
                    st->setComments(str.c_str());
                }
            }
        }
    }

    //check for empty
    string comm = "";
    if (file->firstStatement()->comments())
    {
        comm = file->firstStatement()->comments();
        file->firstStatement()->delComments();
    }
    const string tmp = file->firstStatement()->unparse();
    bool isEmpty = (tmp.size() == 0);
    if (comm.size())
        file->firstStatement()->addComment(comm.c_str());

    string strUnparse = "";
    if (toString)
    {
        if (!isEmpty)
            strUnparse = string(file->firstStatement()->unparse());
    }
    else
    {        
        if (!isEmpty)
        {
#ifdef _WIN32
            FILE* fOut;
            errno_t err = fopen_s(&fOut, fout, "w");
#else
            int err = 0;
            FILE* fOut = fopen(fout, "w");
#endif
            if (fOut == NULL)
            {
                if (fout)
                    __spf_print(1, "can not open file to write with name '%s' with error %d\n", fout, err);
                else
                    __spf_print(1, "can not open file to write with name 'NULL'\n");
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            }

            file->unparse(fOut);
            fclose(fOut);
        }
    }

    //restore back
    //XXX: use Sage hack!!
    for (SgStatement *st = file->firstStatement(); st; st = st->lexNext())
        if (changedVars.find(st->id()) != changedVars.end())
            st->setVariant(-1 * st->variant());

    for (auto& elem : toExtract)
        elem->extractStmt();

    return strUnparse;
}

static map<SgFile*, map<string, vector<SgSymbol*>>> allCreatedSymbols;

SgSymbol* findSymbolOrCreate(SgFile *file, const string toFind, SgType *type, SgStatement *scope)
{
    auto createdSymbols = allCreatedSymbols.find(file);
    if (createdSymbols == allCreatedSymbols.end())
        createdSymbols = allCreatedSymbols.insert(createdSymbols, make_pair(file, map<string, vector<SgSymbol*>>()));    

    SgSymbol *symb = file->firstSymbol();

    while (symb)
    {
        if (symb->identifier() == toFind)
        {
            bool scopeC = false;
            bool typeC = false;
            if (scope)
                scopeC = (symb->scope() == scope);
            else
                scopeC = true;

            if (type)
                typeC = (symb->type() && symb->type()->equivalentToType(type));
            else
                typeC = true;
            if (scopeC && typeC)
                return symb;
        }
        symb = symb->next();
    }

    auto result = createdSymbols->second.find(toFind);

    if (result == createdSymbols->second.end())
        result = createdSymbols->second.insert(result, make_pair(toFind, vector<SgSymbol*>()));

    SgSymbol *newS = NULL;
    for (auto &symbs : result->second)
    {
        if ((symbs->scope() == scope && scope) || (!symbs->scope() && !scope))
        {
            if (symbs->type() && type)
            {
                if (symbs->type()->equivalentToType(type))
                {
                    newS = symbs;
                    break;
                }
            }
            else if (!symbs->type() && !type)
            {
                newS = symbs;
                break;
            }
        }
    }

    if (newS == NULL)
    {
        newS = new SgSymbol(VARIABLE_NAME, toFind.c_str(), type, scope);
        result->second.push_back(newS);
    }    
    return newS;
}

static string getValue(SgExpression *exp)
{
    if (exp == NULL || exp->variant() == STMT_STR)
        return "";

    string ret = "";
    if (exp->variant() == CONST_REF)
    {
        if (exp->symbol()->identifier())
        {
            ret = "(" + string(exp->symbol()->identifier());
            auto sc = isSgConstantSymb(exp->symbol());
            if (sc && sc->constantValue())
                ret += string("=") + sc->constantValue()->unparse();
            ret += ")";
        }
    }
    else if (exp->symbol())
    {
        if (exp->symbol()->identifier())
            ret = "(" + string(exp->symbol()->identifier()) + ")";
    }
    else if (exp->variant() == INT_VAL)
        ret = "(" + std::to_string(exp->valueInteger()) + ")";
    else if (exp->variant() == DOUBLE_VAL)
        ret = string("(") + (((SgValueExp*)exp)->doubleValue()) + ")";
    else if (exp->variant() == FLOAT_VAL)
        ret = string("(") + (((SgValueExp*)exp)->floatValue()) + ")";
    else if (exp->variant() == ADD_OP)
        ret = "(+)";
    else if (exp->variant() == SUBT_OP)
        ret = "(-)";
    else if (exp->variant() == MULT_OP)
        ret = "(*)";
    else if (exp->variant() == DIV_OP)
        ret = "(/)";
    else if (exp->variant() == MOD_OP)
        ret = "(mod)";
    else if (exp->variant() == EXP_OP)
        ret = "(**)";
    else if (exp->variant() == KEYWORD_VAL)
        ret = "(" + string(((SgKeywordValExp*)exp)->value()) + ")";
    return ret;
}

static void recExpressionPrint(SgExpression *exp, const int lvl, const char *LR, const int currNum, int &allNum)
{
    if (exp)
    {
        SgExpression *lhs = exp->lhs();
        SgExpression *rhs = exp->rhs();
        int lNum, rNum;

        string vCurr = getValue(exp);
        string vL = getValue(lhs);
        string vR = getValue(rhs);

        if (lhs && rhs)
        {
            lNum = allNum + 1;
            rNum = allNum + 2;
            allNum += 2;
            printf("\"%d_%d_%s_%s_%s\" -> \"%d_%d_L_%s_%s\";\n", currNum, lvl, LR, tag[exp->variant()], vCurr.c_str(), lNum, lvl + 1, tag[lhs->variant()], vL.c_str());
            printf("\"%d_%d_%s_%s_%s\" -> \"%d_%d_R_%s_%s\";\n", currNum, lvl, LR, tag[exp->variant()], vCurr.c_str(), rNum, lvl + 1, tag[rhs->variant()], vR.c_str());
        }
        else if (lhs)
        {
            lNum = allNum + 1;
            allNum++;
            printf("\"%d_%d_%s_%s_%s\" -> \"%d_%d_L_%s_%s\";\n", currNum, lvl, LR, tag[exp->variant()], vCurr.c_str(), lNum, lvl + 1, tag[lhs->variant()], vL.c_str());
        }
        else if (rhs)
        {
            rNum = allNum + 1;
            allNum++;
            printf("\"%d_%d_%s_%s_%s\" -> \"%d_%d_R_%s_%s\";\n", currNum, lvl, LR, tag[exp->variant()], vCurr.c_str(), rNum, lvl + 1, tag[rhs->variant()], vR.c_str());
        }
        if (lhs)
            recExpressionPrint(lhs, lvl + 1, "L", lNum, allNum);
        if (rhs)
            recExpressionPrint(rhs, lvl + 1, "R", rNum, allNum);
    }
}

void recExpressionPrint(SgExpression *exp)
{
    printf("digraph G{\n");
    int allNum = 0;
    recExpressionPrint(exp, 0, "L", allNum, allNum);
    if (allNum == 0 && exp)
        printf("\"%d_%d_%s_%s_%s\";\n", allNum, 0, "L", tag[exp->variant()], getValue(exp).c_str());    
    printf("}\n");
    fflush(NULL);
}

void initTags()
{
    for (int i = 0; i < MAXTAGS; i++)
        tag[i] = NULL;

#include "tag.h"
}

void tryToFindPrivateInAttributes(SgStatement *st, set<string> &privates, bool onlyReduction, bool onlyUsers)
{
    set<Symbol*> privatesVars;
    auto foundAttrs = getAttributes<SgStatement*, SgStatement*>(st, set<int>{ SPF_ANALYSIS_DIR, SPF_PARALLEL_DIR });
    if (onlyUsers)
        foundAttrs = filterUserSpf(foundAttrs);

    for (auto &data : foundAttrs)
    {
        if (data)
        {
            Statement *sData = new Statement(data);
            if (!onlyReduction)
                fillPrivatesFromComment(sData, privatesVars);

            // try to find reductions and set its symbols as private in 
            // current loop analysis for correct scalar detection in 
            // tryToFindDependencies() function
            map<string, set<Symbol*>> reductions;
            map<string, set<tuple<Symbol*, Symbol*, int>>> reductionsLoc;

            fillReductionsFromComment(sData, reductions);
            fillReductionsFromComment(sData, reductionsLoc);

            for (auto &redList : reductions)
                privatesVars.insert(redList.second.begin(), redList.second.end());

            for (auto &redLocList : reductionsLoc)
            {
                for (auto &groupList : redLocList.second)
                {
                    privatesVars.insert(std::get<0>(groupList));
                    privatesVars.insert(std::get<1>(groupList));
                }
            }
        }
    }

    for (auto& elem : privatesVars)
        privates.insert(elem->GetOriginal()->identifier());
}

void fillNonDistrArraysAsPrivate(SgStatement *st,
                                 const map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>> &declaredArrays,
                                 const map<SgStatement*, set<tuple<int, string, string>>> &declaratedArraysSt,
                                 set<string> &privatesVars)
{
    // fill as private all non distributed arrays
    auto it = declaratedArraysSt.find(st);
    if (it != declaratedArraysSt.end())
    {
        for (auto itSet = it->second.begin(); itSet != it->second.end(); ++itSet)
        {
            auto itD = declaredArrays.find(*itSet);
            if (itD != declaredArrays.end())
            {
                const auto array = itD->second.first;
                if (array->IsNotDistribute())
                {
                    auto symb = array->GetDeclSymbol(make_pair(st->fileName(), st->lineNumber()));
                    privatesVars.insert(symb->identifier());
                }
            }
        }
    }
}

extern  map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>> declaredArrays;
extern  map<SgStatement*, set<tuple<int, string, string>>> declaratedArraysSt;

DIST::Array* getArrayFromDeclarated(SgStatement *st, const string &arrayName)
{
    DIST::Array *found = NULL;

    auto it = declaratedArraysSt.find(st);
    if (it != declaratedArraysSt.end())
    {
        for (auto itSet = it->second.begin(); itSet != it->second.end() && !found; ++itSet)
        {
            auto itD = declaredArrays.find(*itSet);
            if (itD != declaredArrays.end()) 
            {
                DIST::Array* array = itD->second.first;
                if (array->GetLocation().first == DIST::l_COMMON)
                {
                    auto symb = array->GetDeclSymbol(make_pair(st->fileName(), st->lineNumber()));
                    if (symb && symb->identifier() == arrayName)
                        found = array;
                }
                else
                {
                    if (array->GetShortName() == arrayName)
                        found = array;
                }
            }
        }
    }
    return found;
}

static bool findSymbol(SgExpression *declLst, const string &toFind)
{
    if (declLst == NULL)
        return false;

    stack<SgExpression*> exs;
    exs.push(declLst);

    while (!exs.empty())
    {
        SgExpression* ex = exs.top();
        exs.pop();

        if (ex->variant() == ARRAY_REF || ex->variant() == VAR_REF || ex->variant() == CONST_REF)
        {
            if (ex->symbol()->identifier() == toFind)
                return true;
        }
        else
        {
            if (ex->lhs())
                exs.push(ex->lhs());

            if (ex->rhs())
                exs.push(ex->rhs());
        }
    }
    return false;
}

extern map<string, vector<Messages>> SPF_messages;
SgStatement* declaratedInStmt(SgSymbol *toFind, vector<SgStatement*> *allDecls, bool printInternal, SgStatement* scope)
{
    //need to call this function for MODULE symbols!
    toFind = OriginalSymbol(toFind);
    vector<SgStatement*> inDecl;
    if (toFind->variant() == FUNCTION_NAME && scope == NULL)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    SgStatement *start = scope ? scope : toFind->scope();

    if (start)
    {
        SgStatement *end = start->lastNodeOfStmt();
        const set<int> possibleVars = { VAR_DECL, VAR_DECL_90, ALLOCATABLE_STMT, DIM_STAT, EXTERN_STAT,
                                        COMM_STAT, HPF_TEMPLATE_STAT, DVM_VAR_DECL, STRUCT_DECL };

        while (start != end)
        {
            //error ?
            if (start == NULL)
                break;
            if (start->variant() == INTERFACE_STMT)
                start = start->lastNodeOfStmt();

            //printf("%d on line %d\n", start->variant(), start->lineNumber());
            if (possibleVars.find(start->variant()) != possibleVars.end())
            {
                for (int i = 0; i < 3; ++i)
                {
                    SgExpression *declLst = start->expr(i);
                    if (findSymbol(declLst, toFind->identifier()))
                        inDecl.push_back(start);
                }
            }

            if (!isDVM_stat(start) && !isSPF_stat(start) && isSgExecutableStatement(start))
                break;
            start = start->lexNext();
        }
    }

    /*if (inDecl.size() == 0)
    {
        SgStatement *lowLevelDecl = toFind->declaredInStmt();
        if (lowLevelDecl)
            inDecl.push_back(lowLevelDecl);
    }*/

    if (inDecl.size() == 0)
    {
        if (printInternal)
        {
            __spf_print(1, "can not find declaration for symbol '%s'\n", toFind->identifier());

            auto itM = SPF_messages.find(start->fileName());
            if (itM == SPF_messages.end())
                itM = SPF_messages.insert(itM, make_pair(start->fileName(), vector<Messages>()));

            wstring bufE, bufR;
            __spf_printToLongBuf(bufE, L"Can not find declaration for symbol '%s' in current scope", to_wstring(toFind->identifier()).c_str());
            __spf_printToLongBuf(bufR, R49, to_wstring(toFind->identifier()).c_str());

            itM->second.push_back(Messages(ERROR, toFind->scope()->lineNumber(), bufR, bufE, 1017));
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        }
        return NULL;
    }

    if (allDecls)
        *allDecls = inDecl;

    //return statement by priority: VAR_DECL, VAR_DECL_90, ALLOCATABLE_STMT, DIM_STAT, other
    if (inDecl.size() > 1)
    {
        for (int i = 0; i < inDecl.size(); ++i)
            if (inDecl[i]->variant() == VAR_DECL || inDecl[i]->variant() == VAR_DECL_90)
                return inDecl[i];

        for (int i = 0; i < inDecl.size(); ++i)
            if (inDecl[i]->variant() == ALLOCATABLE_STMT)
                return inDecl[i];

        for (int i = 0; i < inDecl.size(); ++i)
            if (inDecl[i]->variant() == DIM_STAT)
                return inDecl[i];

        for (int i = 0; i < inDecl.size(); ++i)
            if (inDecl[i]->variant() == COMM_STAT)
                return inDecl[i];

        for (int i = 0; i < inDecl.size(); ++i)
            if (inDecl[i]->variant() == HPF_TEMPLATE_STAT)
                return inDecl[i];

        for (int i = 0; i < inDecl.size(); ++i)
            if (inDecl[i]->variant() == DVM_VAR_DECL)
                return inDecl[i];

        for (int i = 0; i < inDecl.size(); ++i)
            return inDecl[i];

        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        return NULL;
    }
    else
        return inDecl[0];
}

bool isDVM_stat(SgStatement *st)
{
    if (st == NULL)
        return false;

    bool ret = false;

    const int var = st->variant();

    //for details see dvm_tag.h
    if ((var >= DVM_INTERVAL_DIR && var <= DVM_ENDINTERVAL_DIR) ||
        (var >= DVM_DEBUG_DIR && var <= DVM_TRACEOFF_DIR) ||
        (var >= DVM_PARALLEL_ON_DIR && var <= DVM_SHADOW_DIR) ||
        (var >= DVM_NEW_VALUE_DIR && var <= DVM_POINTER_DIR) ||
        (var >= DVM_TASK_REGION_DIR && var < FORALL_STAT) ||
        (var > FORALL_STAT && var <= DVM_TEMPLATE_DELETE_DIR) ||
        (var >= ACC_REGION_DIR && var <= ACC_ASYNC_OP) ||
        (var == DVM_DISTRIBUTE_DIR) ||
        (var >= HPF_TEMPLATE_STAT && var <= DVM_REDISTRIBUTE_DIR) ||
        (var >= BLOCK_OP && var <= STAGE_OP) || 
        (var >= INDIRECT_OP && var <= SHADOW_NAMES_OP) ||
        (var >= ACC_REGION_DIR && var <= ACC_ASYNC_OP))
            ret = true;
    return ret;
}

bool isSPF_stat(SgStatement *st)
{
    if (st == NULL)
        return false;

    bool ret = false;

    const int var = st->variant();
    //for details see dvm_tag.h
    if (var >= SPF_ANALYSIS_DIR && var <= SPF_PROCESS_PRIVATE_OP)
        ret = true;
    return ret;
}

static map<SgExpression*, string>::const_iterator getStringExpr(SgExpression *ex, map<SgExpression*, string> &collection)
{
    auto it = collection.find(ex);
    if (it == collection.end())
        it = collection.insert(it, make_pair(ex, ex->unparse()));
    return it;
}

bool isEqExpressions(SgExpression *left, SgExpression *right, map<SgExpression*, string> &collection)
{
    if (left == right)
        return true;
    else if (left == NULL || right == NULL)
        return false;
    else
        return getStringExpr(left, collection)->second == getStringExpr(right, collection)->second;
}

static string getCommonName(SgExpression *common)
{
    if (common->symbol())
        return string(common->symbol()->identifier());
    else
        return string("spf_unnamed");
}

void getCommonBlocksRef(map<string, vector<SgExpression*>> &commonBlocks, SgStatement *start, SgStatement *end, const string *nameToSkip)
{
    while (start != end)
    {
        if (start->variant() == CONTAINS_STMT)
            break;

        if (start->variant() == COMM_STAT)
        {
            // fill all common blocks
            for (SgExpression *exp = start->expr(0); exp; exp = exp->rhs())
            {
                const string commonName = getCommonName(exp);

                if (nameToSkip && *nameToSkip == commonName)
                    continue;

                auto it = commonBlocks.find(commonName);
                if (it == commonBlocks.end())
                    it = commonBlocks.insert(it, make_pair(commonName, vector<SgExpression*>()));
                it->second.push_back(exp);
            }
        }
        start = start->lexNext();
    }
}

int checkCommonInMainUnit(SgProject& project, map<string, vector<Messages>>& SPF_messages,
                          const set<DIST::Array*>& arrays, bool forDistrbuted)
{
    int internalExit = 0;

    SgStatement* mainUnit = findMainUnit(&project, SPF_messages);
    checkNull(mainUnit, convertFileName(__FILE__).c_str(), __LINE__);

    map<string, vector<SgExpression*>> commonBlocks;
    getCommonBlocksRef(commonBlocks, mainUnit, mainUnit->lastNodeOfStmt());

    // check array declaration
    for (auto& array : arrays)
    {
        if (array->IsLoopArray() || array->IsTemplate())
            continue;
        if (array->GetLocation().first == DIST::l_COMMON)
        {
            auto nameOfCommon = array->GetLocation().second;
            if (commonBlocks.find(nameOfCommon) == commonBlocks.end())
            {
                auto declPlaces = array->GetDeclInfo();
                for (auto& place : declPlaces)
                {
                    vector<Messages>& currMessages = getObjectForFileFromMap(place.first.c_str(), SPF_messages);
                    if (forDistrbuted)
                    {
                        __spf_print(1, "  ERROR: distributed array '%s' in common block '%s' must have declaration in main unit\n", array->GetShortName().c_str(), nameOfCommon.c_str());

                        wstring messageE, messageR;
                        __spf_printToLongBuf(messageE, L"distributed array '%s' in common block '%s' must have declaration in main unit",
                            to_wstring(array->GetShortName()).c_str(), to_wstring(nameOfCommon).c_str());
                        __spf_printToLongBuf(messageR, R75,
                            to_wstring(array->GetShortName()).c_str(), to_wstring(nameOfCommon).c_str());
                        currMessages.push_back(Messages(ERROR, place.second, messageR, messageE, 1042));
                    }
                    else
                    {
                        __spf_print(1, "  ERROR: array '%s' in common block '%s' must have declaration in main unit for DECLARE insertion\n", array->GetShortName().c_str(), nameOfCommon.c_str());

                        wstring messageE, messageR;
                        __spf_printToLongBuf(messageE, L"array '%s' in common block '%s' must have declaration in main unit for DECLARE insertion",
                            to_wstring(array->GetShortName()).c_str(), to_wstring(nameOfCommon).c_str());
                        __spf_printToLongBuf(messageR, R205,
                            to_wstring(array->GetShortName()).c_str(), to_wstring(nameOfCommon).c_str());
                        currMessages.push_back(Messages(ERROR, place.second, messageR, messageE, 1062));
                    }
                }
                internalExit = 1;
            }
        }
    }

    return internalExit;
}

static SgExpression* isInCommon(const vector<SgExpression*> &commonBlocks, const char *arrayName, int &commonPos)
{
    commonPos = 0;
    for (auto &common : commonBlocks)
    {
        for (SgExpression *currCommon = common->lhs(); currCommon; currCommon = currCommon->rhs())
        {
            if (!strcmp(currCommon->lhs()->symbol()->identifier(), arrayName))
                return common;
            commonPos++;
        }
    }
    return NULL;
}

//all common block in project
extern map<string, CommonBlock*> commonBlocks;
static string getCommonNameOnPos(const string& name, const int commonPos)
{
    if (commonBlocks.size() == 0)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    auto it = commonBlocks.find(name);
    if (it == commonBlocks.end())
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    auto inPos = it->second->getGroupedVars().find(commonPos);
    if (inPos == it->second->getGroupedVars().end())
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    set<string> namesOnPos;

    for (auto& var : inPos->second)
        namesOnPos.insert(var->getName());

    if (namesOnPos.size() == 0)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    return *namesOnPos.begin();
}

static map<tuple<string, string, int>, tuple<int, string, string>> tableOfUniqNames;
tuple<int, string, string> getUniqName(const map<string, vector<SgExpression*>> &commonBlocks, SgStatement *decl, SgSymbol *symb)
{
    bool inCommon = false;
    bool needtoCheck = true;
    int commonPos = 0;

    SgExpression *foundCommon = NULL;
    string symbCommn = "";

    SgStatement *declCP = decl->controlParent();
    // find symbol in parameter list of functions
    if (declCP->variant() == PROC_HEDR || declCP->variant() == FUNC_HEDR)
    {
        if (declCP->variant() == PROC_HEDR)
        {
            const int num = ((SgProcHedrStmt*)declCP)->numberOfParameters();
            for (int i = 0; i < num && needtoCheck; ++i)
                if (!strcmp(((SgProcHedrStmt*)declCP)->parameter(i)->identifier(), symb->identifier()))
                    needtoCheck = false;
        }
        else
        {
            const int num = ((SgFuncHedrStmt*)declCP)->numberOfParameters();
            for (int i = 0; i < num && needtoCheck; ++i)
                if (!strcmp(((SgFuncHedrStmt*)declCP)->parameter(i)->identifier(), symb->identifier()))
                    needtoCheck = false;
        }

        auto declCpCp = declCP->controlParent();
        if (declCpCp && declCpCp->variant() == INTERFACE_STMT && needtoCheck)
            needtoCheck = false;
    }
    else if (declCP->variant() == INTERFACE_STMT)
        needtoCheck = false;

    if (needtoCheck)
    {
        for (auto &common : commonBlocks)
        {
            foundCommon = isInCommon(common.second, symb->identifier(), commonPos);
            if (foundCommon)
            {
                inCommon = true;
                symbCommn = getCommonNameOnPos(common.first, commonPos);
                break;
            }
        }
    }

    tuple<int, string, string> retVal;
    if (inCommon)
        retVal = make_tuple(commonPos, string("common_") + getCommonName(foundCommon), symbCommn);
    else
        retVal = make_tuple(decl->lineNumber(), string(decl->fileName()), string(symb->identifier()));

    auto key = make_tuple(symb->identifier(), decl->fileName(), decl->lineNumber());
    auto it = tableOfUniqNames.find(key);
    if (it == tableOfUniqNames.end())
        tableOfUniqNames.insert(it, make_pair(key, retVal));
    else
    {
        if (it->first != key)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    }
    return retVal;
}

tuple<int, string, string> getFromUniqTable(SgSymbol *symb)
{
    auto place = declaratedInStmt(symb);
    auto localIt = tableOfUniqNames.find(std::make_tuple(symb->identifier(), place->fileName(), place->lineNumber()));
    if (localIt == tableOfUniqNames.end())
    {
        auto place = declaratedInStmt(symb);
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    }
    
    return localIt->second;
}

SgStatement* findMainUnit(SgProject *proj, map<string, vector<Messages>> &SPF_messages)
{
    SgStatement *mainUnit = NULL;

    vector<SgStatement*> mainUnits;
    for (int i = proj->numberOfFiles() - 1; i >= 0; --i)
    {
        SgFile *file = &(proj->file(i));
        const string file_name = file->filename();

        for (int k = 0; k < file->numberOfFunctions(); ++k)
        {
            SgStatement *func = file->functions(k);
            if (func->variant() == PROG_HEDR && file_name == func->fileName())
            {
                mainUnit = func;
                mainUnits.push_back(func);
            }
        }
    }

    if (mainUnits.size() != 1)
    {
        for (auto& elem : mainUnits)
        {
            vector<Messages>& currMessages = getObjectForFileFromMap(elem->fileName(), SPF_messages);

            wstring messageE, messageR;
            __spf_printToLongBuf(messageE, L"more than one main unit was found");
            __spf_printToLongBuf(messageR, R146);

            currMessages.push_back(Messages(ERROR, elem->lineNumber(), messageR, messageE, 1050));
        }

        if (mainUnits.size() == 0)
        {
            for (int i = proj->numberOfFiles() - 1; i >= 0; --i)
            {
                SgFile* file = &(proj->file(i));
                const char* file_name = file->filename();

                vector<Messages> &currMessages = getObjectForFileFromMap(file_name, SPF_messages);
                wstring messageE, messageR;
                __spf_printToLongBuf(messageE, L"main unit not found");
                __spf_printToLongBuf(messageR, R147);

                currMessages.push_back(Messages(ERROR, file->firstStatement()->lexNext()->lineNumber(), messageR, messageE, 1050));
            }            
        }
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    }
    
    if (!mainUnit->switchToFile())
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    return mainUnit;
}

template<typename IN_TYPE, typename OUT_TYPE>
const vector<OUT_TYPE> getAttributes(IN_TYPE st, const set<int> dataType)
{
    vector<OUT_TYPE> outData;

    for (int i = 0; i < st->numberOfAttributes(); ++i)
    {
        SgAttribute *attr = st->getAttribute(i);
        const int type = st->attributeType(i);
        if (dataType.find(type) != dataType.end())
            if (attr->getAttributeData())
                outData.push_back((OUT_TYPE)(attr->getAttributeData()));
    }

    return outData;
}

template<typename IN_TYPE>
void deleteAttributes(IN_TYPE st, const set<int> dataType)
{
    bool changed = true;
    while (changed)
    {
        changed = false;
        int idxDel = -1;
        for (int i = 0; i < st->numberOfAttributes(); ++i)
        {
            SgAttribute* attr = st->getAttribute(i);
            const int type = st->attributeType(i);
            if (dataType.find(type) != dataType.end())
            {
                if (attr->getAttributeData())
                {
                    idxDel = i;
                    break;
                }
            }
        }

        if (idxDel != -1)
        {
            st->deleteAttribute(idxDel);
            changed = true;
        }
    }
}

template const vector<SgSymbol*> getAttributes(SgSymbol *st, const set<int> dataType);
template const vector<SgSymbol*> getAttributes(SgStatement* st, const set<int> dataType);
template const vector<SgSymbol*> getAttributes(SgExpression* st, const set<int> dataType);
template const vector<char*> getAttributes(SgSymbol *st, const set<int> dataType);
template const vector<int*> getAttributes(SgSymbol* st, const set<int> dataType);
template const vector<SgStatement*> getAttributes(SgStatement *st, const set<int> dataType);
template const vector<SgExpression*> getAttributes(SgExpression *st, const set<int> dataType);
template const vector<SgStatement*> getAttributes(SgExpression *st, const set<int> dataType);
template const vector<DIST::Array*> getAttributes(SgExpression *st, const set<int> dataType);
template const vector<int*> getAttributes(SgExpression *st, const set<int> dataType);
template const vector<FuncInfo*> getAttributes(SgStatement *st, const set<int> dataType);

template void deleteAttributes(SgStatement* st, const set<int> dataType);
template void deleteAttributes(SgExpression* st, const set<int> dataType);

static int isParameterOneOfThese(const string& name, const vector<string>& names)
{
    int parameterOfFunctionItself = -1;
    for (int i = 0; i < names.size(); ++i)
        if (name == names[i])
        {
            parameterOfFunctionItself = i;
            break;
        }
    return parameterOfFunctionItself;
}

#define DEF_T 0
#define USE_T 1
#define DEF_USE_T 2

static void processPartOfAssign(int type, SgExpression *exp, vector<DefUseList> &currentLists, SgFile *file, SgStatement *inStat, 
                                vector<string> &parameterNames, pair<SgSymbol*, string> underCall, int funcPos);

static void addSymbolsToDefUse(const int type, SgExpression *ex, vector<DefUseList> &currentList, 
                               pair<SgSymbol*, string> underCall, int funcPos,
                               SgFile *file, SgStatement *inStat, vector<string>& parameterNames)
{
    if (ex)
    {
        bool next = true;
        if (ex->variant() == VAR_REF || ex->variant() == ARRAY_REF)
        {
            if (ex->symbol())
            {
                string name = string(ex->symbol()->identifier());
                currentList.push_back(DefUseList(type, inStat, ex, file, underCall, make_pair(ex->symbol(), name), funcPos, isParameterOneOfThese(name, parameterNames)));
            }
        }
        else if (ex->variant() == FUNC_CALL)
        {
            underCall = make_pair(ex->symbol(), string(ex->symbol()->identifier()));
            funcPos = 0;

            bool isIntrinsic = isIntrinsicFunctionName(ex->symbol()->identifier());
            for (SgExpression *list = ex->lhs(); list; list = list->rhs(), funcPos++)
            {
                if (!isIntrinsic)
                    processPartOfAssign(DEF_USE_T, list->lhs(), currentList, file, inStat, parameterNames, underCall, funcPos);
                else
                    addSymbolsToDefUse(type, list->lhs(), currentList, underCall, funcPos, file, inStat, parameterNames);
            }
            next = false;
        }

        if (next)
        {
            addSymbolsToDefUse(type, ex->lhs(), currentList, underCall, funcPos, file, inStat, parameterNames);
            addSymbolsToDefUse(type, ex->rhs(), currentList, underCall, funcPos, file, inStat, parameterNames);
        }
    }    
}

static void processPartOfAssign(int type, SgExpression *exp, vector<DefUseList> &currentList, SgFile *file, SgStatement *inStat,
                                vector<string> &parameterNames, pair<SgSymbol*, string> underCall, int funcPos)
{
    if (exp->symbol() && exp->variant() == VAR_REF) // simple assign
    {
        SgSymbol *tmp = exp->symbol();        
        string name = string(tmp->identifier());

        if (type == DEF_USE_T)
        {
            currentList.push_back(DefUseList(DEF_T, inStat, exp, file, underCall, make_pair(tmp, name), funcPos, isParameterOneOfThese(name, parameterNames)));
            currentList.push_back(DefUseList(USE_T, inStat, exp, file, underCall, make_pair(tmp, name), funcPos, isParameterOneOfThese(name, parameterNames)));
        }
        else
            currentList.push_back(DefUseList(DEF_T, inStat, exp, file, underCall, make_pair(tmp, name), funcPos, isParameterOneOfThese(name, parameterNames)));
    }
    //TODO
    else // array ref and etc.
    {
        if (exp->variant() == ARRAY_OP)
        {            
            SgSymbol *tmp = exp->lhs()->symbol();
            string name = string(tmp->identifier());

            if (type == DEF_USE_T)
            {
                currentList.push_back(DefUseList(DEF_T, inStat, exp, file, underCall, make_pair(tmp, name), funcPos, isParameterOneOfThese(name, parameterNames)));
                currentList.push_back(DefUseList(USE_T, inStat, exp, file, underCall, make_pair(tmp, name), funcPos, isParameterOneOfThese(name, parameterNames)));
            }
            else
                currentList.push_back(DefUseList(type, inStat, exp, file, underCall, make_pair(tmp, name), funcPos, isParameterOneOfThese(name, parameterNames)));
            
            addSymbolsToDefUse(1, exp->lhs()->lhs(), currentList, underCall, funcPos, file, inStat, parameterNames);
            addSymbolsToDefUse(1, exp->lhs()->rhs(), currentList, underCall, funcPos, file, inStat, parameterNames);
        }
        else if (exp->variant() == ARRAY_REF)
        {
            SgSymbol *tmp = exp->symbol();            
            string name = string(tmp->identifier());

            if (type == DEF_USE_T)
            {
                currentList.push_back(DefUseList(DEF_T, inStat, exp, file, underCall, make_pair(tmp, name), funcPos, isParameterOneOfThese(name, parameterNames)));
                currentList.push_back(DefUseList(USE_T, inStat, exp, file, underCall, make_pair(tmp, name), funcPos, isParameterOneOfThese(name, parameterNames)));
            }
            else
                currentList.push_back(DefUseList(type, inStat, exp, file, underCall, make_pair(tmp, name), funcPos, isParameterOneOfThese(name, parameterNames)));
            
            addSymbolsToDefUse(1, exp->lhs(), currentList, underCall, funcPos, file, inStat, parameterNames);
            addSymbolsToDefUse(1, exp->rhs(), currentList, underCall, funcPos, file, inStat, parameterNames);
        }
        else
        {
            addSymbolsToDefUse(1, exp->lhs(), currentList, underCall, funcPos, file, inStat, parameterNames);
            addSymbolsToDefUse(1, exp->rhs(), currentList, underCall, funcPos, file, inStat, parameterNames);
        }
    }
}

static void inline addToLists(map<string, vector<DefUseList>> &currentLists, const vector<DefUseList> &tmpList)
{
    for (auto &list : currentLists)
        for (auto &elem : tmpList)
            list.second.push_back(elem);
}

void constructDefUseStep1(SgFile *file, map<string, vector<DefUseList>> &defUseByFunctions, map<string, vector<FuncInfo*>> &allFuncInfo, vector<Messages> &messages)
{
    map<string, vector<FuncInfo*>> curFileFuncInfo;
    vector<LoopGraph*> tmpL;
    map<FuncInfo*, vector<SAPFOR::BasicBlock*>> tmpIR;

    functionAnalyzer(file, curFileFuncInfo, tmpL, messages, tmpIR);

    //functions not found
    if (curFileFuncInfo.size() == 0)
        return;

    auto whereToCopy = allFuncInfo.insert(make_pair(file->filename(), vector<FuncInfo*>()));
    for(auto& it : curFileFuncInfo.begin()->second)
        whereToCopy.first->second.push_back(it);

    map<string, FuncInfo*> funcToFuncInfo = map<string, FuncInfo*>();
    for (int i = 0; i < curFileFuncInfo.begin()->second.size(); ++i)
        funcToFuncInfo.insert(make_pair(curFileFuncInfo.begin()->second[i]->funcName, curFileFuncInfo.begin()->second[i]));

    for (int f = 0; f < file->numberOfFunctions(); ++f)
    {
        //function header and entry points
        map<string, vector<DefUseList>> currentLists;
        vector<DefUseList> tmpList;
        vector<string> parameterNames = vector<string>();

        SgStatement *start = file->functions(f);
        SgStatement *end = start->lastNodeOfStmt();
        int pos;

        auto founded = funcToFuncInfo.find(((SgProgHedrStmt*)start)->nameWithContains());
        start->addAttribute(PROC_CALL, (void*)founded->second, sizeof(FuncInfo));

        SgProgHedrStmt *header = isSgProgHedrStmt(start);
        if (header && start->variant() != PROG_HEDR)
            for (int i = 0; i < header->numberOfParameters(); ++i)
                parameterNames.push_back(header->parameter(i)->identifier());

        currentLists[string(start->symbol()->identifier())] = vector<DefUseList>();
        for (SgStatement *st = start->lexNext(); st != end; st = st->lexNext())
        {            
            if (isSgExecutableStatement(st))
            {
                if (isDVM_stat(st))
                {
                    if (st->variant() != ACC_GET_ACTUAL_DIR && st->variant() != ACC_ACTUAL_DIR)
                        continue;
                }

                tmpList.clear();
                switch (st->variant())
                {
                case ASSIGN_STAT:
                    processPartOfAssign(DEF_T, st->expr(0), tmpList, file, st, parameterNames, make_pair((SgSymbol*)NULL, string("")), -1);
                    addSymbolsToDefUse(USE_T, st->expr(1), tmpList, make_pair((SgSymbol*)NULL, string("")), -1, file, st, parameterNames);

                    addToLists(currentLists, tmpList);                    
                    break;
                case FOR_NODE:
                    if (st->symbol())
                        for (auto &list : currentLists)
                        {
                            string name = string(st->symbol()->identifier());
                            auto vr = new SgVarRefExp(*(st->symbol()));

                            list.second.push_back(DefUseList(1, st, vr, file, make_pair((SgSymbol*)NULL, string("")), make_pair(st->symbol(), name), -1, isParameterOneOfThese(name, parameterNames)));
                            list.second.push_back(DefUseList(0, st, vr, file, make_pair((SgSymbol*)NULL, string("")), make_pair(st->symbol(), name), -1, isParameterOneOfThese(name, parameterNames)));
                        }

                    for (int i = 0; i < 3; ++i)
                        addSymbolsToDefUse(USE_T, st->expr(i), tmpList, make_pair((SgSymbol*)NULL, string("")), -1, file, st, parameterNames);
                    addToLists(currentLists, tmpList);
                    break;                
                case READ_STAT:
                    {
                        SgInputOutputStmt *io = isSgInputOutputStmt(st);
                        auto list = io->itemList();
                        int pos = 0;
                        while (list)
                        {
                            if (list->lhs())
                                processPartOfAssign(DEF_T, list->lhs(), tmpList, file, st, parameterNames, make_pair(io->symbol(), string("READ")), pos);
                            pos++;
                            list = list->rhs();
                        }
                    }
                    addToLists(currentLists, tmpList);
                    break;
                case PROC_STAT:
                    pos = 0;
                    for (SgExpression *ex = st->expr(0); ex; ex = ex->rhs(), pos++)
                        processPartOfAssign(DEF_USE_T, ex->lhs(), tmpList, file, st, parameterNames, make_pair(st->symbol(), string(st->symbol()->identifier())), pos);
                    addToLists(currentLists, tmpList);
                    break;
                case ENTRY_STAT:
                    currentLists[string(st->symbol()->identifier())] = vector<DefUseList>();
                    break;
                case LOGIF_NODE:
                case CONT_STAT:
                case IF_NODE:
                case WHILE_NODE:
                case DO_WHILE_NODE:
                case GOTO_NODE:
                case STOP_STAT:
                case RETURN_STAT:
                case RETURN_NODE:
                case ELSEIF_NODE:
                case ARITHIF_NODE:
                case WHERE_NODE:
                case WHERE_BLOCK_STMT:
                case SWITCH_NODE:
                case CASE_NODE:
                case BREAK_NODE:
                case EXIT_STMT:
                case ASSGOTO_NODE:
                case COMGOTO_NODE:
                case WRITE_STAT:
                case PRINT_STAT:
                default:
                    for (int i = 0; i < 3; ++i)
                        addSymbolsToDefUse(USE_T, st->expr(i), tmpList, make_pair((SgSymbol*)NULL, string("")), -1, file, st, parameterNames);
                    addToLists(currentLists, tmpList);
                    break;
                }
            }
        }

        for (auto &list : currentLists)
        {
            auto it = defUseByFunctions.find(list.first);
            if (it == defUseByFunctions.end())
                it = defUseByFunctions.insert(it, make_pair(list.first, vector<DefUseList>()));
        }

        for (auto &list : currentLists)
        {
            auto it = defUseByFunctions.find(list.first);
            for (auto &elem : list.second)
                it->second.push_back(elem);
        }
    }
}

#undef DEF_T
#undef USE_T
#undef DEF_USE_T

extern map<string, vector<DefUseList>> defUseByFunctions;
set<string> getAllDefVars(const string &funcName)
{
    set<string> retVal;
    for (auto &elem : defUseByFunctions[funcName])
        if (elem.isDef())
            retVal.insert(elem.getVar());
    return retVal;
}

set<string> getAllUseVars(const string &funcName)
{
    set<string> retVal;

    for (auto &elem : defUseByFunctions[funcName])
        if (elem.isUse())
            retVal.insert(elem.getVar());

    return retVal;
}

const vector<DefUseList>& getAllDefUseVarsList(const string &funcName) 
{
    return defUseByFunctions[funcName]; 
}

const vector<DefUseList> getAllDefUseVarsList(const string &funcName, const string varName) 
{
    vector<DefUseList> retVal;
    for (auto &elem : defUseByFunctions[funcName])
        if (elem.getVar() == varName)
            retVal.push_back(elem);    

    return retVal;
}

map<SgStatement*, vector<DefUseList>> createDefUseMapByPlace()
{
    map<SgStatement*, vector<DefUseList>> retVal;

    for (auto &byFunc : defUseByFunctions)
        for (auto &elem : byFunc.second)
            retVal[elem.getPlace()].push_back(elem);

    return retVal;
}

int printDefUseSets(const char *fileName, const map<string, vector<DefUseList>> &defUseLists)
{
    FILE *file = fopen(fileName, "w");
    if (file == NULL)
    {
        __spf_print(1, "can not open file %s\n", fileName);
        return -1;
    }
        
    for (auto &elem : defUseLists)
    {
        fprintf(file, "*** Function %s\n", elem.first.c_str());
        for (auto &inList : elem.second)
            inList.print(file, true);
        fprintf(file, "\n");
    }

    fclose(file);
    return 0;
}

static bool isDefUseVar(const int paramPosition, const string &funcName, map<string, vector<DefUseList>> &defUseByFunctions, bool defined)
{
    auto founded = defUseByFunctions.find(funcName);
    if(founded == defUseByFunctions.end())
        return true; //No information. Argument can be defined or used.

    vector<DefUseList>& curDefUse = founded->second;
    bool isDefUse = false;

    for (int i = 0; i < curDefUse.size(); ++i)
    {
        if (paramPosition == curDefUse[i].getParameterPositionInFunction())
        {
            if (defined && curDefUse[i].isDef())
            {
                isDefUse = true;
                break;
            }
            else if(!defined && curDefUse[i].isUse())
            {
                isDefUse = true;
                break;
            }
            else
            {
                const string calledFuncName = curDefUse[i].getParamOfFunction();
                if (!calledFuncName.empty())
                {
                    isDefUse = isDefUseVar(curDefUse[i].getParameterPosition(), calledFuncName, defUseByFunctions, defined);
                    if (isDefUse)
                        break;
                }
            }
        }
    }
    return isDefUse;
}

static SgExpression* makeList(const vector<SgExpression*> &list)
{
    if (list.size() == 0)
        return NULL;
    SgExpression *out = new SgExpression(EXPR_LIST);
    SgExpression *ret = out;
    for (int i = 0; i < list.size(); ++i)
    {
        out->setLhs(list[i]);
        if (i != list.size() - 1)
        {
            out->setRhs(new SgExpression(EXPR_LIST));
            out = out->rhs();
        }
        else
            out->setRhs(NULL);
    }

    return ret;
}

static map<SgStatement*, pair<SgExpression*, SgExpression*>> buildLists(const vector<DefUseList> &list)
{
    map<SgStatement*, pair<vector<SgExpression*>, vector<SgExpression*>>> out;
    for (auto &elem : list)
    {
        if (elem.isDef())
            out[elem.getPlace()].first.push_back(elem.getExpr());
        if (elem.isUse())
            out[elem.getPlace()].second.push_back(elem.getExpr());
    }

    map<SgStatement*, pair<SgExpression*, SgExpression*>> ret;
    for (auto &elem : out)
    {
        ret[elem.first].first = makeList(elem.second.first);
        ret[elem.first].second = makeList(elem.second.second);
    }

    return ret;
}

void constructDefUseStep2(SgFile *file, map<string, vector<DefUseList>> &defUseByFunctions)
{

    for (int f = 0; f < file->numberOfFunctions(); ++f)
    {
        SgProgHedrStmt *header = isSgProgHedrStmt(file->functions(f));
        if (header == NULL || header->variant() == PROG_HEDR)
            continue;

        for (int i = 0; i < header->numberOfParameters(); ++i)
        {
            int currAttr = header->parameter(i)->attributes();
            if ((currAttr & OUT_BIT) == 0 && (currAttr & INOUT_BIT) == 0 && (currAttr & IN_BIT) == 0)
            {
                bool isDef = isDefUseVar(i, header->symbol()->identifier(), defUseByFunctions, true);
                bool isUse = isDefUseVar(i, header->symbol()->identifier(), defUseByFunctions, false);
                if(isDef && isUse)
                    header->parameter(i)->setAttribute(INOUT_BIT);
                else if (isDef)
                    header->parameter(i)->setAttribute(OUT_BIT);
                else
                    header->parameter(i)->setAttribute(IN_BIT);
            }
            //TODO: test and replace from defUseVar() in defUse.cpp
            /*auto funcList = defUseByFunctions[header->symbol()->identifier()];
            auto toAdd = buildLists(funcList);
            for (auto &elem : toAdd)
            {                
                elem.first->addAttribute(DEFINEDLIST_ATTRIBUTE, (void*)elem.second.first, 0);
                elem.first->addAttribute(USEDLIST_ATTRIBUTE, (void*)elem.second.second, 0);
            }*/
        }
    }
}

int printCommonBlocks(const char *fileName, const map<string, CommonBlock*> &commonBlocks)
{
    FILE *file = fopen(fileName, "w");
    if (file == NULL)
    {
        __spf_print(1, "can not open file %s\n", fileName);
        return -1;
    }

    for (auto &commonBlock : commonBlocks)
    {
        fprintf(file, "*** COMMON BLOCK '%s'\n", commonBlock.first.c_str());
        commonBlock.second->print(file);
        fprintf(file, "\n");
    }

    fclose(file);
    return 0;
}

// CommonBlock::
Variable* CommonBlock::hasVariable(const string &name, const varType type, const int position)
{
    for (auto &variable : variables)
        if (variable->getName() == name && variable->getType() == type && variable->getPosition() == position)
            return variable;

    return NULL;
}

Variable* CommonBlock::hasVariable(SgSymbol *symbol, const varType type, const int position)
{
    return hasVariable(string(symbol->identifier()), type, position);
}

const vector<const Variable*> CommonBlock::getVariables(SgFile *file, SgStatement *function) const 
{
    return getVariables(string(file->filename()), string(function->symbol()->identifier()));
}

const vector<const Variable*> CommonBlock::getVariables(const string &file, const string &function) const
{
    vector<const Variable*> retVariables;
    
    for (auto &variable : variables)
        if (variable->hasUse(file, function))
            retVariables.push_back(variable);
    
    return retVariables;
}

const vector<const Variable*> CommonBlock::getVariables(int position) const
{
    vector <const Variable*> retVariables;

    auto it = groupedVars.find(position);
    if (it != groupedVars.end())
        for (auto &variable : it->second)
            retVariables.push_back(variable);

    return retVariables;
}

static void findDeclType(SgExpression *ex, varType &type, const string &toFind)
{
    if (ex)
    {
        if (ex->symbol() && ex->symbol()->identifier() == toFind)
        {
            switch (ex->variant())
            {
            case VAR_REF:
                if (type != ARRAY)
                    type = SCALAR;
                break;
            case ARRAY_REF:
                type = ARRAY;
                break;
            case INT_VAL:
            case FLOAT_VAL:
            case DOUBLE_VAL:
            case BOOL_VAL:
            case CHAR_VAL:
            case STRING_VAL:
            case CONST_REF:
                type = CONST;
                break;
            case COMM_LIST:
                break;
            default:
                type = ANOTHER;
                break;
            }
        }

        findDeclType(ex->lhs(), type, toFind);
        findDeclType(ex->rhs(), type, toFind);
    }
}

void CommonBlock::addVariables(SgFile *file, SgStatement *function, const vector<pair<SgSymbol*, int>> &newVariables)
{
    for (auto &varPair : newVariables)
    {
        vector<SgStatement*> allDecls;
        declaratedInStmt(varPair.first, &allDecls);
        varType type = ANOTHER;

        for (auto& decl : allDecls)
            for (int i = 0; i < 3; ++i)
                findDeclType(decl->expr(i), type, varPair.first->identifier());

        Variable *exist = hasVariable(varPair.first, type, varPair.second);
        if (exist)
            exist->addUse(file, function, varPair.first);
        else
        {
            variables.push_back(new Variable(file, function, varPair.first, string(varPair.first->identifier()), type, varPair.second));

            auto it = groupedVars.find(varPair.second);
            if (it == groupedVars.end())
                it = groupedVars.insert(it, make_pair(varPair.second, vector<Variable*>()));

            it->second.push_back(variables.back());
        }
    }
}

void CommonBlock::print(FILE *fileOut) const
{
    fprintf(fileOut, "  [POSITION], [VARIABLE NAME], [TYPE]: \n");
    for (auto &var: variables)
    {        
        var->print(fileOut);
        fprintf(fileOut, "      USE in [FILE, FUNC / BLOCK_DATA]:");

        for (auto &use : var->getAllUse())
            fprintf(fileOut, " [%s, %s]", use.getFileName().c_str(), 
                                          use.isBlockDataUse() ? ("BD_" + use.getFunctionName()).c_str() : ("F_" + use.getFunctionName()).c_str());
        fprintf(fileOut, "\n");
    }
}

DIST::Array* CommonBlock::getFirstSynonym(DIST::Array* array) const
{
    if (array->GetLocation().first != DIST::l_COMMON)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    for (auto& var : variables)
    {
        if (var->getName() == array->GetShortName())
        {
            int pos = var->getPosition();
            auto it = groupedVars.find(pos);
            if (it == groupedVars.end())
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            if (it->second.size() == 1)
                return array;
            else
            {
                const string currFile = current_file->filename();
                auto s = it->second[0]->getSymbol();
                if (SgFile::switchToFile(it->second[0]->getDeclarated()->fileName()) == -1)
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                string arrayName = string(s->identifier());
                DIST::Array* firstArray = getArrayFromDeclarated(declaratedInStmt(s), arrayName);

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

                if (SgFile::switchToFile(currFile) == -1)
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                return firstArray;
            }            
        }
    }

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

// END of CommonBlock::

void groupDeclarations(SgFile *file)
{
    for (int i = 0; i < file->numberOfFunctions(); ++i)
    {
        SgStatement *func = file->functions(i);
        SgStatement *lastNode = func->lastNodeOfStmt();

        map<SgType*, pair<vector<SgStatement*>, vector<SgExpression*>>> refs;
        
        vector<SgStatement*> toDel;
        for (auto st = func; st != lastNode; st = st->lexNext())
        {
            if (isSgExecutableStatement(st))
                break;
            
            if (st->variant() == VAR_DECL)
            {
                auto varDecl = isSgVarDeclStmt(st);
                SgExpression *varList = varDecl->varList();
                
                int count = 0;
                for (auto lst = varList; lst; lst = lst->rhs())
                    count++;

                vector<SgExpression*> notMoved;

                int countOfMoved = 0;
                for (auto lst = varList; lst; lst = lst->rhs())
                {
                    SgExpression *var = lst->lhs();
                    if (var->variant() == VAR_REF)
                    {
                        auto type = var->symbol()->type();
                        if (type && isSgArrayType(type) == NULL && type->variant() != T_STRING)
                        {
                            countOfMoved++;                            
                            refs[type].second.push_back(var);

                            SgStatement *prev = st->lexPrev();
                            while (prev && prev->variant() == SPF_ANALYSIS_DIR)
                            {
                                refs[type].first.push_back(prev->extractStmt());
                                prev = st->lexPrev();
                            }
                        }
                        else
                            notMoved.push_back(var);
                    }
                    else
                        notMoved.push_back(var);
                }
                                
                if (countOfMoved != count)
                {
                    if (countOfMoved != 0)
                    {
                        SgExpression *varList = new SgExpression(EXPR_LIST);
                        SgExpression *pVar = varList;

                        for (int z = 0; z < notMoved.size(); z++)
                        {                            
                            pVar->setLhs(notMoved[z]);
                            if (z != notMoved.size() - 1)
                            {
                                pVar->setRhs(new SgExpression(EXPR_LIST));
                                pVar = pVar->rhs();
                            }
                        }                        
                        st->setExpression(0, *varList);
                    }
                }
                else
                    toDel.push_back(st);
            }
        }

        for (auto st = func; st != lastNode; st = st->lexNext())
        {
            if (isSgExecutableStatement(st->lexNext()))
            {
                for (auto &newDecl : refs)
                {
                    SgVarDeclStmt *newDeclSt = newDecl.second.second[0]->symbol()->makeVarDeclStmt();
                    for (int z = 1; z < newDecl.second.second.size(); ++z)
                        newDeclSt->addVar(*newDecl.second.second[z]);
                    st->insertStmtAfter(*newDeclSt, *func);

                    for (auto &spf : newDecl.second.first)
                        st->insertStmtAfter(*spf, *func);                    
                }                
                break;
            }
        }
          
        //TODO: move comments
        for (auto &elem : toDel)
            elem->deleteStmt();
    }
}

bool ifSymbolExists(SgFile *file, const string &symbName)
{
    if (SgFile::switchToFile(file->filename()) != -1)
    {
        SgSymbol *symb = file->firstSymbol();

        while (symb)
        {
            if (symb->identifier() == symbName)
                return true;

            symb = symb->next();
        }
    }
    else
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    return false;
}

int checkSymbNameAndCorrect(const string& symbName, int complite)
{
    set<string> existedSymbols;
    //if (existedSymbols.size() == 0)
    {
        SgFile* oldFile = current_file;
        for (int i = 0; i < CurrentProject->numberOfFiles(); ++i)
        {
            SgFile* file = &(CurrentProject->file(i));
            SgSymbol* s = file->firstSymbol();
            while (s)
            {
                existedSymbols.insert(s->identifier());
                s = s->next();
            }
        }

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

    string base = symbName;
    int retNum = complite;
    while (existedSymbols.find(base + std::to_string(retNum)) != existedSymbols.end())
        retNum++;
    //existedSymbols.insert(retName);

    return retNum;
}

string checkSymbNameAndCorrect(const string& symbName, const string complite)
{
    set<string> existedSymbols;
    //if (existedSymbols.size() == 0)
    {
        string oldFileName = current_file->filename();
        for (int i = 0; i < CurrentProject->numberOfFiles(); ++i)
        {
            SgFile* file = &(CurrentProject->file(i));
            SgSymbol* s = file->firstSymbol();
            while (s)
            {
                existedSymbols.insert(s->identifier());
                s = s->next();
            }
        }

        if (SgFile::switchToFile(oldFileName) == -1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    }

    string retName = symbName;
    while (existedSymbols.find(retName) != existedSymbols.end())
        retName += complite;
    //existedSymbols.insert(retName);

    return retName;
}

const CommonBlock* isArrayInCommon(const map<string, CommonBlock*> &commonBlocks, const DIST::Array *array)
{
    for (auto &commonBlockPair : commonBlocks)
        for (auto &variable : commonBlockPair.second->getVariables())
            if (variable->getName() == array->GetShortName() && variable->getType() == ARRAY && array->GetLocation().first == DIST::l_COMMON)
                return commonBlockPair.second;

    return NULL;
}

static void fillArraysFromDirsRec(SgExpression *ex, vector<DIST::Array*> &toAdd)
{
    if (ex)
    {
        if (ex->variant() == ARRAY_REF)
        {
            auto attributes = getAttributes<SgExpression*, DIST::Array*>(ex, { ARRAY_REF });
            toAdd.insert(toAdd.end(), attributes.begin(), attributes.end());
        }

        if (ex->lhs())
            fillArraysFromDirsRec(ex->lhs(), toAdd);
        if (ex->rhs())
            fillArraysFromDirsRec(ex->rhs(), toAdd);
    }
}

vector<DIST::Array*> fillArraysFromDir(Statement *st)
{
    vector<DIST::Array*> retVal;
    for (int z = 0; z < 3; ++z)
        fillArraysFromDirsRec(st->GetOriginal()->lexPrev()->expr(z), retVal);
    return retVal;
}

template<typename objT>
objT& getObjectForFileFromMap(const char *fileName, map<string, objT> &mapObject)
{
    auto it = mapObject.find(fileName);
    if (it == mapObject.end())
        it = mapObject.insert(it, std::make_pair(fileName, objT()));
    return it->second;
}
template vector<SpfInterval*>& getObjectForFileFromMap(const char *fileName, map<string, vector<SpfInterval*>>&);
template PredictorStats& getObjectForFileFromMap(const char *fileName, map<string, PredictorStats>&);

void printSymbolTable(SgFile *file, string filter, const set<int>& vars)
{
    for (auto s = file->firstSymbol(); s; s = s->next())
    {
        if (vars.size() != 0)
            if (vars.find(s->variant()) == vars.end())
                continue;

        auto t = s->type();
        bool need = true;
        if (filter != "")
            if (filter != s->identifier())
                need = false;
        if (need)
        {
            int line = s->scope() ? s->scope()->lineNumber() : -1;
            printf("[%d] '%s' type %d ('%s'), location %d line, variant %d, addr %p\n", s->id(), s->identifier(), t ? t->variant() : -1, t ? tag[t->variant()] : "", line, s->variant(), s->thesymb);
        }
    }
}

static bool checkAdd(const int var, const set<int> &additional)
{
    if (additional.size())
    {
        if (additional.find(var) == additional.end())
            return true;
        else
            return false;
    }
    else
        return true;
}

SgStatement* getFuncStat(SgStatement *st, const set<int> additional)
{
    if (!st)
        return NULL;

    SgStatement *iterator = st;
    while (iterator && !isSgProgHedrStmt(iterator) && checkAdd(iterator->variant(), additional))
        iterator = iterator->controlParent();

    if (!iterator)
        checkNull(iterator, convertFileName(__FILE__).c_str(), __LINE__);
    return iterator;
}

SgStatement* duplicateProcedure(SgStatement *toDup, const string *newName, bool withAttributes, bool withComment, bool withSameLines, bool dontInsert)
{
    if (toDup == NULL)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    if (isSgProgHedrStmt(toDup) == NULL)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    SgStatement* global = current_file->firstStatement();
    if (global->variant() != GLOBAL)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    SgSymbol* orig = toDup->symbol();    
    SgSymbol* copied = &orig->copySubprogram(*global);

    //XXX: remove all extra functions
    if (global->lexNext()->symbol()->identifier() != string(copied->identifier()))
    {
        vector<SgStatement*> toDel;
        SgStatement* st = global->lexNext();
        if (!isSgProgHedrStmt(st))
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        while (st)
        {
            if (st->symbol() == copied)
                break;
            toDel.push_back(st);
            st = st->lastNodeOfStmt()->lexNext();
        }

        if (st == NULL)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        if (!isSgProgHedrStmt(st))
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        if (st->symbol()->identifier() != string(copied->identifier()))
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        for (auto& elem : toDel)
            elem->extractStmt();

        if (global->lexNext()->symbol()->identifier() != string(copied->identifier()))
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    }

    //move 
    SgStatement* toMove = NULL;
    if (dontInsert)
        toMove = global->lexNext()->extractStmt();
    else
    {
        if (toDup->controlParent())
        {
            toMove = global->lexNext()->extractStmt();
            toDup->insertStmtBefore(*toMove, *toDup->controlParent());
        }
        else
            toMove = global->lexNext();
    }

    //change name
    if (newName)
        copied->changeName(newName->c_str());
    
    // set line numbers, pointer to attributes, comments and unparseIgnore status
    for (auto origStat = toDup, copyStat = toMove;
         origStat != toDup->lastNodeOfStmt()->lexNext();
         origStat = origStat->lexNext(), copyStat = copyStat->lexNext())
    {
        if (copyStat->variant() != origStat->variant())
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

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

        if (withAttributes)
            if (origStat->numberOfAttributes() > 0)
                copyStat->addAttributeTree(origStat->getAttribute(0));

        if (withComment)
        {
            if (origStat->comments())
                copyStat->setComments(origStat->comments());
        }
        copyStat->setUnparseIgnore(origStat->getUnparseIgnore());
    }

    return toMove;
}

SgExpression* makeExprList(const vector<SgExpression*>& items, bool withSort)
{
    SgExpression* list = NULL;
    if (items.size() == 0)
        return list;
    list = new SgExpression(EXPR_LIST);

    vector<SgExpression*> newItems;
    if (withSort)
    {
         multimap<string, SgExpression*> sorted;
         int tmpVal = 0;
         for (auto& elem : items)
         {
             if (elem->variant() == VAR_REF || elem->variant() == ARRAY_REF || elem->variant() == CONST_REF)
                 sorted.insert(make_pair(elem->unparse(), elem));
             else if (elem->lhs() && elem->lhs()->variant() == VAR_REF || elem->lhs()->variant() == ARRAY_REF)
                 sorted.insert(make_pair(elem->lhs()->unparse(), elem));
             else
                 sorted.insert(make_pair(std::to_string(tmpVal++), elem));
         }

         for (auto& elem : sorted)
             newItems.push_back(elem.second);
         std::reverse(newItems.begin(), newItems.end());
    }
    else
        newItems = items;

    for (int z = 0; z < newItems.size(); ++z)
    {
        if (z == 0)
            list->setLhs(newItems[z]);
        else
        {
            SgExpression* tmp = new SgExpression(EXPR_LIST);
            tmp->setLhs(newItems[z]);
            tmp->setRhs(list);
            list = tmp;
        }
    }

    return list;
}

static bool sortByName(const pair<SgSymbol*, SgExpression*>& l, const pair<SgSymbol*, SgExpression*>& r)
{
    return string(l.first->identifier()) < r.first->identifier();
}

//if curr == NULL -> no insertion
//all vars in sIn must have the same type 
SgStatement* makeDeclaration(SgStatement* curr, const vector<SgSymbol*>& sIn, vector<SgExpression*>* inits)
{
    if (sIn.size() == 0)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    SgStatement* place = curr;
    SgStatement* scope = NULL;

    vector<pair<SgSymbol*, SgExpression*>> s;
    if (place)
    {
        //check same type
        map<string, map<string, vector<SgSymbol*>>> groups;
        for (auto& toDec : sIn)
        {
            string newDecl = makeDeclaration(NULL, { toDec })->unparse();
            auto it = newDecl.find("::");
            if (it == string::npos)
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            groups[newDecl.substr(0, it)][toDec->identifier()].push_back(toDec);
        }

        if (groups.size() > 1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        while (isSgProgHedrStmt(place) == NULL && place->variant() != BLOCK_DATA && place->variant() != MODULE_STMT)
            place = place->controlParent();
        scope = place;

        set<string> declared;
        while (isSgExecutableStatement(place) == NULL && place != scope->lastNodeOfStmt())
        {
            if (place->variant() == VAR_DECL || place->variant() == VAR_DECL_90)
            {
                SgExpression* ex = place->expr(0);
                while (ex)
                {
                    if (ex->lhs() && ex->lhs()->symbol())
                        declared.insert(ex->lhs()->symbol()->identifier());
                    ex = ex->rhs();
                }
            }
            place = place->lexNext();
        }

        for (int z = 0; z < sIn.size(); ++z)
            if (declared.find(sIn[z]->identifier()) == declared.end())
                s.push_back(make_pair(sIn[z], (inits ? (*inits)[z] : NULL)));
    }
    else
    {
        for (int z = 0; z < sIn.size(); ++z)
            s.push_back(make_pair(sIn[z], (inits ? (*inits)[z] : NULL)));
    }

    if (s.size() == 0)
        return NULL;

    sort(s.begin(), s.end(), sortByName);

    SgVarDeclStmt* decl = s[0].first->makeVarDeclStmt();
    if (s[0].second)
        decl->expr(0)->setLhs(new SgExpression(ASSGN_OP, new SgVarRefExp(s[0].first), s[0].second));

    for (int z = 1; z < s.size(); ++z)
    {
        auto tmpDecl = s[z].first->makeVarDeclStmt();
        if (s[z].second)
            decl->addVar(*new SgExpression(ASSGN_OP, new SgVarRefExp(s[z].first), s[z].second));
        
        else
            decl->addVar(*tmpDecl->expr(0)->lhs());
    }

    if (place)
    {
        decl->setFileName(place->fileName());
        decl->setFileId(place->getFileId());
        decl->setProject(place->getProject());
        decl->setlineNumber(getNextNegativeLineNumber());
        //decl->setlineNumber(place->lineNumber());

        place->insertStmtBefore(*decl, *scope);
    }

    decl->setVariant(VAR_DECL_90);
    return decl;
}

//vars in 'symbolsToDeclare' may be various type 
//TODO add inits parameter
vector<SgStatement*> makeDeclaration(const vector<SgSymbol*>& symbolsToDeclare, SgStatement* where, vector<SgExpression*>* inits)
{
    vector<SgStatement*> allDecls;
    map<string, map<string, vector<SgSymbol*>>> groups;
    for (auto& toDec : symbolsToDeclare)
    {
        string newDecl = makeDeclaration(NULL, vector<SgSymbol*> { toDec })->unparse();
        auto it = newDecl.find("::");

        if (it == string::npos)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        groups[newDecl.substr(0, it)][toDec->identifier()].push_back(toDec);
    }

    for (auto& toDecls : groups)
    {
        vector<SgSymbol*> unitedList;
        for (auto& elem : toDecls.second)
            unitedList.push_back(elem.second[0]);

        allDecls.push_back(makeDeclaration(where, unitedList));
    }
    return allDecls;
}

int getNextFreeLabel()
{
    PTR_LABEL lab;
    set<int> used;
    for (lab = PROJ_FIRST_LABEL(); lab; lab = LABEL_NEXT(lab))
        used.insert(LABEL_STMTNO(lab));

    for (int z = 1; z < 99999; ++z)
        if (used.find(z) == used.end())
            return z;
    return -1;
}

static void recFillUsedVars(SgExpression *exp, map<string, SgSymbol*> &vars)
{
    if (exp)
    {
        if (exp->symbol() && (exp->variant() == VAR_REF || exp->variant() == ARRAY_REF || exp->variant() == CONST_REF)
            && !(exp->symbol()->attributes() & PRIVATE_BIT))
        {
            const auto key = exp->symbol()->identifier();
            if (vars.find(key) == vars.end())
                vars.insert(make_pair(key, exp->symbol()));
        }
        recFillUsedVars(exp->lhs(), vars);
        recFillUsedVars(exp->rhs(), vars);
    }
}

static inline void fillUsedVars(SgStatement *st, map<string, SgSymbol*> &vars)
{
    if (st)
    {
        for (int i = 0; i < 3; ++i)
        {
            SgExpression *exp = st->expr(i);
            recFillUsedVars(exp, vars);
        }
    }
}

static inline void joinMaps(map<string, SgSymbol*> &map1, const map<string, SgSymbol*> &map2, const map<string, SgSymbol*> &exept = map<string, SgSymbol*>())
{
    for (auto &pair : map2)
        if (exept.find(pair.first) == exept.end() && map1.find(pair.first) == map1.end())
            map1.insert(pair);
}

void fillVisibleInUseVariables(SgStatement *useSt, map<string, SgSymbol*> &vars)
{
    if (useSt)
    {
        if (useSt->variant() == USE_STMT)
        {
            bool only = false;

            map<string, SgSymbol*> localVars;  // local module variables
            map<string, SgSymbol*> useVars;    // variables from other modules
            map<string, SgSymbol*> renamedVas; // renamed variables
            map<string, SgSymbol*> originVars; // origin variables names in USE_STAT

            // check USE_STAT
            // fill from ONLY_NODE and RENAME_NODE
            SgExpression *ex = useSt->expr(0);
            if (ex && ex->variant() == ONLY_NODE)
            {
                only = true;
                ex = ex->lhs();
            }

            for (auto exI = ex; exI; exI = exI->rhs())
            {
                if (exI->lhs()->variant() == RENAME_NODE)
                {
                    SgExpression *ren = exI->lhs();
                    if (ren->lhs()->symbol() && ren->rhs() && ren->rhs()->symbol())
                    {
                        if (renamedVas.find(ren->lhs()->symbol()->identifier()) == renamedVas.end())
                            renamedVas.insert(make_pair(ren->lhs()->symbol()->identifier(), ren->lhs()->symbol()));
                        if (originVars.find(ren->rhs()->symbol()->identifier()) == originVars.end())
                            originVars.insert(make_pair(ren->rhs()->symbol()->identifier(), ren->rhs()->symbol()));
                    }
                    else if (only && ren->lhs()->symbol())
                    {
                        if (renamedVas.find(ren->lhs()->symbol()->identifier()) == renamedVas.end())
                            renamedVas.insert(make_pair(ren->lhs()->symbol()->identifier(), ren->lhs()->symbol()));
                    }
                }
            }

            if (!only)
            {
                // check module
                const string modName(useSt->symbol()->identifier());
                vector<SgStatement*> modules;
                findModulesInFile(useSt->getFile(), modules);

                bool found = false;
                for (auto i = 0; i < modules.size(); ++i)
                {
                    if (modName == modules[i]->symbol()->identifier())
                    {
                        found = true;

                        vector<SgStatement*> useStats;
                        for (SgStatement *st = modules[i]->lexNext(); st != modules[i]->lastNodeOfStmt(); st = st->lexNext())
                        {
                            if (st->variant() == USE_STMT)
                                useStats.push_back(st);
                            else if (st->variant() == CONTAINS_STMT)
                                break;
                            else
                                fillUsedVars(st, localVars);
                        }

                        for (auto &useSt : useStats)
                        {
                            map<string, SgSymbol*> visibleVars;
                            fillVisibleInUseVariables(useSt, visibleVars);
                            joinMaps(useVars, visibleVars);
                        }

                        break;
                    }
                }

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

            // fill vars
            // TODO: check renaming
            joinMaps(vars, useVars, originVars);
            joinMaps(vars, localVars, originVars);
            joinMaps(vars, renamedVas);
        }
    }
}

Variable::Variable(SgFile* file, SgStatement* function, SgSymbol* symbol, const std::string& name, const varType type, const int position) :
                   symbol(symbol), name(name), type(type), position(position)
{
    declPace = declaratedInStmt(symbol);
    allUse.push_back(CommonVariableUse(file, function, symbol));
}

string preprocDataString(string data, bool full)
{
    string ret = "";


    for (int z = 0; z < data.size(); ++z)
    {
        if (data[z] == '\t')
            data[z] = ' ';
        else if (data[z] == '\r')
            data[z] = ' ';
    }

    auto it = data.find("DATA");
    if (it == string::npos)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    else
        it += 4;
    if (data[it] != ' ')
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    else
        it++;

    if (full)
        ret = "DATA ";

    int i = it;
    while (i != data.size())
    {
        if (data[i] != ' ')
            ret += data[i++];
        else
            i++;
    }
    return ret;
}

map<string, string> splitData(const set<SgValueExp*>& dataStats)
{
    map<string, string> result;
    for (auto& dataV : dataStats)
    {
        char* value = dataV->thellnd->entry.string_val;
        string dataS(value);
        convertToUpper(dataS);
        dataS = preprocDataString(dataS, false);

        convertToLower(dataS);
        string currS = "";
        string currV = "";
        int startV = -1;
        for (int z = 0; z < dataS.size(); ++z)
        {
            if (startV == -1)
            {
                if (dataS[z] == '/')
                    startV = 0;
                else if (dataS[z] != ' ' && dataS[z] != ',')
                    currS += dataS[z];
            }
            else if (startV == 0)
            {
                if (dataS[z] == '/')
                {
                    startV = -1;
                    result[currS] = currV;
                    currS = currV = "";
                }
                else if (dataS[z] != ' ')
                    currV += dataS[z];
            }            
        }
    }

    return result;
}

void extractComments(SgStatement* where, const string& what)
{
    if (BIF_CMNT(where->thebif) && CMNT_STRING(BIF_CMNT(where->thebif)))
    {
        char* str = CMNT_STRING(BIF_CMNT(where->thebif));
        string source(str);
        removeSubstrFromStr(source, what.c_str());
        sprintf(str, "%s", source.c_str());
    }
}

void getVariables(SgExpression* ex, set<string>& variables, const set<int> variants)
{
    if (ex)
    {
        if (variants.find(ex->variant()) != variants.end())
            variables.insert(OriginalSymbol(ex->symbol())->identifier());

        getVariables(ex->rhs(), variables, variants);
        getVariables(ex->lhs(), variables, variants);
    }
}

void getVariables(SgExpression* ex, set<SgSymbol*>& variables, const set<int> variants)
{
    if (ex)
    {
        if (variants.find(ex->variant()) != variants.end())
            variables.insert(OriginalSymbol(ex->symbol()));

        getVariables(ex->rhs(), variables, variants);
        getVariables(ex->lhs(), variables, variants);
    }
}

template<typename T>
set<T> getAllVariables(SgStatement* stFrom, SgStatement* stTo, const set<int>& variants)
{
    set<T> vars;
    for (auto stmt = stFrom; stmt != stTo; stmt = stmt->lexNext())
        for (int i = 0; i < 3; ++i)
            getVariables(stmt->expr(i), vars, variants);

    return vars;
}
template set<string>    getAllVariables(SgStatement* stFrom, SgStatement* stTo, const set<int>& variants);
template set<SgSymbol*> getAllVariables(SgStatement* stFrom, SgStatement* stTo, const set<int>& variants);

void shiftLines(SgProject* project, bool print)
{
    map<string, int> shifts;
    //shift lines if modules included
    const string shiftInfo = "!SPF NUM FILES";
    for (int z = 0; z < project->numberOfFiles(); ++z)
    {
        SgFile* file = &(project->file(z));
        const string fileN = file->filename();

        int shiftN = 0;
        for (auto st = file->firstStatement(); st; st = st->lexNext())
        {
            if (st->comments())
            {
                string comms = st->comments();
                auto it = comms.find(shiftInfo);
                if (it != string::npos)
                {
                    it += shiftInfo.size() + 1;

                    if (sscanf(comms.c_str() + it, "%d", &shiftN) == -1)
                    {
                        __spf_print(print, "comm %s, shifted %s\n", comms.c_str(), comms.c_str() + it);
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                    }
                    break;
                }
            }
        }

        if (shiftN == 0)
            continue;
        shifts[fileN] = shiftN + 1; // with comment !SPF NUM FILES
        __spf_print(print, "  shift by %d for %s\n", shiftN, fileN.c_str());
    }

    for (int z = 0; z < project->numberOfFiles(); ++z)
    {
        SgFile* file = &(project->file(z));
        for (SgStatement* st = file->firstStatement()->lexNext(); st; st = st->lexNext())
        {
            string currF = st->fileName();
            auto it = shifts.find(currF);
            if (it != shifts.end() && file->filename() == currF)
                st->setlineNumber(st->lineNumber() - it->second);
        }
    }
}

SgProject* createProject(const char* proj_name, 
                         vector<ParallelRegion*>& parallelRegions,
                         vector<ParallelRegion*>& subs_parallelRegions,
                         map<string, vector<SgStatement*>>& hiddenData, 
                         map<string, int>& filesNameWithoutExt,
                         map<string, set<string>>& moduleUsesByFile, 
                         map<string, string>& moduleDecls,
                         map<string, map<SgStatement*, vector<SgStatement*>>>& exctactedModuleStats,
                         bool printSymbTable)
{
    Statement::setSapforRegime();
    Statement::deactiveConsistentchecker();
    sortFilesBySize(proj_name);
    SgProject* project = new SgProject(proj_name);
    addNumberOfFileToAttribute(project);

    if (printSymbTable) {
        for (int z = 0; z < project->numberOfFiles(); ++z) {
            SgFile* file = &(project->file(z));
            printf("===== TABLE FOR FILE %s\n", file->filename());
            printSymbolTable(file, "");
        }
        exit(0);
    }

    parallelRegions.push_back(new ParallelRegion(0, "DEFAULT"));
    subs_parallelRegions.push_back(new ParallelRegion(0, "DEFAULT"));

    Statement::activeConsistentchecker();
    Statement::activeDeprecatedchecker();

    //hide duplicated functions
    for (int z = 0; z < project->numberOfFiles(); ++z)
    {
        SgFile* file = &(project->file(z));
        //file->unparsestdout();

        const string fileN = file->filename();
        auto first = file->firstStatement();
        SgStatement* lastValid = NULL;

        const string toFind = "!SPF SHADOW FILES";
        for (SgStatement* st = first->lexNext(), *stPrev = first;
            st;
            st = st->lexNext(), stPrev = stPrev->lexNext())
        {
            if (st->comments())
            {
                string comm(st->comments());
                if (comm.find(toFind) != string::npos)
                {
                    if (st->variant() == CONTROL_END)
                    {
                        extractComments(st, toFind);
                        lastValid = st;
                    }
                    else
                        lastValid = stPrev;
                    break;
                }
            }
        }
        
        if (lastValid)
        {
            vector<SgStatement*> toExtract;
            auto st = lastValid->lexNext();
            while (st)
            {
                toExtract.push_back(st);
                st = st->lastNodeOfStmt();
                st = st->lexNext();
            }

            __spf_print(1, "hidden data for file '%s'\n", file->filename());
            set<int> validVars = { PROG_HEDR, FUNC_HEDR, PROC_HEDR, BLOCK_DATA, MODULE_STMT };
            for (auto& elem : toExtract)
            {
                int var = elem->variant();
                if (validVars.find(var) == validVars.end())
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                __spf_print(1, "  [%s, %d]\n", elem->fileName(), elem->lineNumber());
                hiddenData[fileN].push_back(elem->extractStmt());
            }
            lastValid->setLexNext((SgStatement*)NULL);
        }
    }

    //hide excluded modules
    const set<string> excludedMods = getExcludedModules();

    for (int z = 0; z < project->numberOfFiles(); ++z)
    {
        SgFile* file = &(project->file(z));

        const string fileN = file->filename();
        auto first = file->firstStatement();
        SgStatement* lastValid = NULL;

        vector<SgStatement*> toExtract;
        for (SgStatement* st = first->lexNext(); st; st = st->lexNext())
        {
            if (st->variant() == USE_STMT)
            {
                string name = st->symbol()->identifier();
                if (excludedMods.find(name) != excludedMods.end())
                {
                    SgStatement* before = st->lexNext();
                    do
                    {
                        if (before->variant() == USE_STMT)
                        {
                            if (excludedMods.find(before->symbol()->identifier()) != excludedMods.end())
                            {
                                before = before->lexNext();
                                continue;
                            }
                        }
                        break;
                    } while (true);

                    exctactedModuleStats[fileN][before].push_back(st);
                    toExtract.push_back(st);
                }
            }
        }

        for (auto& elem : toExtract)
            elem->extractStmt();
    }

    //dump symbol table and symbol use
    /*printSymbolTable(current_file);
    for (auto st = current_file->firstStatement()->lexNext(); st; st = st->lexNext())
    {
        if (st->variant() != MODULE_STMT && isSgProgHedrStmt(st) == NULL)
        {
            set<SgSymbol*> refs;
            for (int z = 0; z < 3; ++z)
                if (st->expr(z))
                    fillAllSymbs(st->expr(z), refs);

            if (refs.size())
            {
                printf("FOR STATEMENT ON LINE %d:\n", st->lineNumber());
                st->unparsestdout();
                printf("refs ids:");
                for (auto& elem : refs)
                    printf(" %d (%s)", elem->id(), elem->identifier());
                printf(";\n\n");
            }
        }
    }*/

    set<string> filesInProj;
    //preprocess module includes
    for (int z = 0; z < project->numberOfFiles(); ++z)
        filesInProj.insert((project->file(z)).filename());

    //check main unit
    findMainUnit(project, SPF_messages);

    set<string> globalFunctions;
    for (int z = 0; z < project->numberOfFiles(); ++z)
    {
        SgFile* file = &(project->file(z));

        string name = OnlyName(file->filename());
        if (filesNameWithoutExt.find(name) == filesNameWithoutExt.end())
            filesNameWithoutExt[name] = 1;
        else
            filesNameWithoutExt[name]++;

        fillModuleUse(file, moduleUsesByFile, moduleDecls);

        int funcCount = file->numberOfFunctions();
        for (int k = 0; k < funcCount; ++k)
        {
            auto func = file->functions(k);
            if (func->controlParent()->variant() == GLOBAL)
                globalFunctions.insert(func->symbol()->identifier());
        }
    }
    filterModuleUse(moduleUsesByFile, moduleDecls);

    //shift lines if modules included
    shiftLines(project);

    map<string, vector<Function>> functions;
    for (int z = 0; z < project->numberOfFiles(); ++z)
        fillFunctionInfo(&(project->file(z)), functions);

    set<string> needToResolve;
    for (auto& func : functions)
        if (func.second.size() > 1)
            needToResolve.insert(func.first);

    for (int z = 0; z < project->numberOfFiles(); ++z)
        resolveFunctionCalls(&(project->file(z)), needToResolve, functions);

    for (int z = 0; z < project->numberOfFiles(); ++z)
        correctModuleProcNames(&(project->file(z)), globalFunctions);


    for (int z = 0; z < project->numberOfFiles(); ++z)
        removeExecutableFromModuleDeclaration(&(project->file(z)), filesInProj, hiddenData[project->file(z).filename()]);

    for (int z = 0; z < project->numberOfFiles(); ++z)
    {
        SgFile* file = &(project->file(z));
        correctModuleSymbols(file);
        //file->unparsestdout();
    }

    if (detectMpiCalls(project, SPF_messages))
    {
        sharedMemoryParallelization = 1;
        keepDvmDirectives = 0;
        ignoreIO = 1;
        parallizeFreeLoops = 0;
    }

    return project;
}

static bool isIntrinsic(SgType* base)
{
    switch (base->variant())
    {
    case T_INT:
    case T_FLOAT:
    case T_BOOL:
    case T_DOUBLE:
    case T_COMPLEX:
    case T_DCOMPLEX:
    case T_CHAR:
        return true;
    default:
        break;
    }
    return false;
}

bool isArrayType(SgType* type)
{    
    if (type)
    {
        SgType* base = type->baseType();
        if (base && 
            type->variant() != T_STRING && 
            base->variant() != T_STRING &&
            (IS_INTRINSIC_TYPE(base) || isIntrinsic(base)))
            return true;
    }
    return false;
}

bool isArrayRef(SgExpression* ex)
{
    if (ex && ex->variant() == ARRAY_REF)
    {
        SgArrayRefExp* arrayRef = isSgArrayRefExp(ex);
        if (arrayRef)
        {
            if (isArrayType(ex->symbol()->type()))
            {
                auto type = isSgArrayType(ex->symbol()->type());
                if (type && type->dimension())
                    return true;
            }
        }
    }
    return false;
}

bool isStringArrayType(SgType* type)
{
    if (type)
    {
        SgType* base = type->baseType();
        if (base && (type->variant() == T_STRING || base->variant() == T_STRING))
            return true;
    }
    return false;
}

void moveLabelBefore(SgStatement* st, SgStatement* next)
{
    SgLabel* lab = next ? next->label() : st->label();
    if (lab)
    {
        SgStatement* cont = new SgContinueStmt();
        cont->setLabel(*lab);

        if (next)
            next->deleteLabel(true);
        else
            st->deleteLabel(true);

        if (next)
        {
            cont->setFileName(next->fileName());
            cont->setProject(next->getProject());
            cont->setFileId(next->getFileId());
            cont->setlineNumber(next->lineNumber());
        }
        else
        {
            cont->setFileName(st->fileName());
            cont->setProject(st->getProject());
            cont->setFileId(st->getFileId());
            cont->setlineNumber(getNextNegativeLineNumber());
        }
        st->insertStmtBefore(*cont, *st->controlParent());
    }
}

bool isEqSymbols(SgSymbol* sym1, SgSymbol* sym2)
{
    if (!sym1 || !sym2)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    return string(OriginalSymbol(sym1)->identifier()) == string(OriginalSymbol(sym2)->identifier());

    
}

set<string> getAllFilesInProject()
{
    set<string> res;
    const string currId = current_file->filename();
    for (int z = 0; z < CurrentProject->numberOfFiles(); ++z)
        res.insert(CurrentProject->file(z).filename());    
    if (SgFile::switchToFile(currId) == -1)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    return res;
}

void LogIftoIfThen(SgStatement* stmt)
{
    SgControlEndStmt* control = new SgControlEndStmt();
    stmt->setVariant(IF_NODE);
    stmt->lexNext()->insertStmtAfter(*control, *stmt);

    if (stmt->numberOfAttributes(OMP_MARK) > 0)
        control->addAttribute(OMP_MARK);
}

void removeSpecialCommentsFromProject(SgFile* file)
{
    //remove SAPFOR comment with version
    SgStatement* stF = file->firstStatement()->lexNext();
    while (stF)
    {
        if (stF && stF->comments())
        {
            string oldComment(stF->comments());
            auto it = oldComment.find("! *** generated by SAPFOR");
            if (it != string::npos)
            {
                auto endIt = oldComment.find("! *** generated by SAPFOR", it + 1);
                endIt = oldComment.find("\n", (endIt != string::npos) ? endIt : it);

                string newComment = oldComment.substr(0, it) + oldComment.substr(endIt + 1);
                stF->delComments();
                if (newComment != "")
                    stF->setComments(newComment.c_str());

                if (genVersionDone.find(file) != genVersionDone.end())
                    genVersionDone.erase(file);
            }
        }
        stF = stF->lexNext();
    } 
}

//TODO: for gen_block and other variations, and redistributions dirs
void getMaxMinBlockDistribution(SgFile* file, pair<int, int>& min_max)
{
    SgStatement* st = file->firstStatement();
    while (st)
    {
        if (isDVM_stat(st))
        {
            if (st->variant() == DVM_DISTRIBUTE_DIR || st->variant() == DVM_VAR_DECL)
            {
                for (int z = 0; z < 3; ++z)
                {
                    SgExpression* ex = st->expr(z);
                    queue<SgExpression*> q;

                    if (ex)
                    {
                        q.push(ex);
                        int blockCount = 0;
                        while (q.size())
                        {
                            ex = q.front();
                            q.pop();

                            if (ex->rhs())
                                q.push(ex->rhs());
                            if (ex->lhs())
                                q.push(ex->lhs());

                            if (ex->variant() == BLOCK_OP)
                                blockCount++;
                        }

                        if (blockCount)
                        {
                            if (min_max == make_pair(-1, -1))
                                min_max = make_pair(blockCount, blockCount);
                            else
                            {
                                min_max.first = std::min(min_max.first, blockCount);
                                min_max.second = std::max(min_max.second, blockCount);
                            }
                        }
                    }
                }
            }
        }
        st = st->lexNext();
    }
}

//TODO: need to add to includes
void addPrivatesToArraysFromGUI(SgFile* file, const map<tuple<int, string, string>, pair<DIST::Array*, DIST::ArrayAccessInfo*>>& declaredArrays,
                                const map<string, int>& distrStateFromGUI)
{
    map<SgStatement*, set<SgSymbol*>> added;

    for (auto& arrayPair : declaredArrays)
    {
        DIST::Array* array = arrayPair.second.first;
        string key = array->GetIndepUniqName();
        auto it = distrStateFromGUI.find(key);
        if (it == distrStateFromGUI.end())
            continue;

        if (it->second != DIST::NO_DISTR)
            continue;
        
        for (auto& decl : array->GetDeclInfoWithSymb())
        {
            auto& place = decl.first;
            const auto& symb = decl.second->GetOriginal();

            if (place.first != file->filename())
                continue;

            SgStatement* declSt = SgStatement::getStatementByFileAndLine(place.first, place.second);
            checkNull(declSt, convertFileName(__FILE__).c_str(), __LINE__);

            added[declSt].insert(symb);
        }
    }

    for (auto& toInsert : added)
    {
        vector<SgExpression*> list;
        for (auto& elem : toInsert.second)
            list.push_back(new SgVarRefExp(elem));
        
        SgStatement* op = new SgStatement(SPF_ANALYSIS_DIR);
        op->setExpression(0, new SgExpression(SPF_PROCESS_PRIVATE_OP, makeExprList(list)));

        toInsert.first->insertStmtBefore(*op, *toInsert.first->controlParent());
    }
}
