#include "leak_detector.h"

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

#include <map>
#include <vector>
#include <set>
#include <string>

#include "dvm.h"
#include "graph_calls_func.h"
#include "SgUtils.h"
#include "CommonBlock.h"
#include "DefUseList.h"
#include "expr_transform.h"
#include "../VerificationCode/verifications.h"
#include "../DvmhRegions/DvmhRegionInserter.h"
#include "function_purifying.h"

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

static bool isIntrincis(const string& name);

void insertIntrinsicStat(const vector<FuncInfo*>& allFuncInfo)
{
    for (auto& func : allFuncInfo)
    {
        auto start = func->funcPointer->GetOriginal();
        auto end = start->lastNodeOfStmt();
        SgStatement* st = start;
        SgStatement* intr = NULL;

        //find last decl place
        for ( ; st != end; st = st->lexNext())
        {
            if (isSgExecutableStatement(st))
                break;

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

            if (st->variant() == INTRIN_STAT)
                intr = st;
        }

        map<string, SgSymbol*> intrincis;
        for (auto& call : func->callsFromDetailed)
        {
            auto name = call.detailCallsFrom.first;
            if (intrincis.find(name) != intrincis.end())
                continue;

            if (!isIntrincis(name))
                continue;

            void* ptr = call.pointerDetailCallsFrom.first;
            int var = call.pointerDetailCallsFrom.second;

            SgSymbol* s = NULL;
            if (var == PROC_STAT)
                s = ((SgStatement*)ptr)->symbol();
            else if (var == FUNC_CALL)
                s = ((SgExpression*)ptr)->symbol();
            else
                continue;

            if ((s->attributes() & INTRINSIC_BIT) != 0)
                continue;

            intrincis[name] = s;
        }

        if (!intrincis.size())
            continue;

        if (intr == NULL)
        {
            vector<SgExpression*> list;
            for (auto& elem : intrincis)
                list.push_back(new SgVarRefExp(elem.second));

            SgStatement* intr = new SgStatement(INTRIN_STAT);
            intr->setExpression(0, makeExprList(list));

            auto prev = (st->variant() == FOR_NODE) ? st->lexPrev() : st;
            int line = prev->lineNumber();
            while (line <= 0 && !isSgProgHedrStmt(prev)) {
                prev = prev->lexPrev();
                line = prev->lineNumber();
            }

            if (line <= 0)
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            intr->setlineNumber(getNextNegativeLineNumber());
            st->insertStmtBefore(*intr, *func->funcPointer);
        }
        else
        {
            SgExprListExp* list = isSgExprListExp(intr->expr(0));
            checkNull(list, convertFileName(__FILE__).c_str(), __LINE__);

            for (auto& elem : intrincis)
                list->append(*new SgVarRefExp(elem.second));
        }
    }
}

bool checkOutCalls(const set<string>& outCalls)
{
    for (auto& elem : outCalls)
        if (isIntrinsicFunctionName(elem.c_str()) == 0)
            return true;

    return false;
}

void createInterfacesForOutCalls(FuncInfo* func)
{
    if (func->isPure && !func->isMain)
    {
        bool hasOutCalls = checkOutCalls(func->callsFrom);
        if (hasOutCalls)
            DvmhRegionInserter::createInterfaceBlockForOutCalls(func);
    }
}

static bool changeIfHasStarRange(SgExpression* arrayDecl, SgStatement* scope, vector<SgSymbol*>& parNames)
{
    SgExpression* list = arrayDecl->lhs();
    string varN = arrayDecl->symbol()->identifier() + string("_sz");
    bool has = false;

    SgExpression* allDimsBefore = NULL;
    while (list)
    {
        const int var = list->lhs()->variant();
        if (var == STAR_RANGE)
        {
            has = true;

            parNames.push_back(new SgSymbol(VARIABLE_NAME, varN.c_str(), SgTypeInt(), scope));
            SgExpression* par = allDimsBefore ? &(*new SgVarRefExp(parNames.back()) / *allDimsBefore) : (new SgVarRefExp(parNames.back()));

            list->setLhs(par);
            break;
        }
        else
        {
            if (allDimsBefore == NULL)
                allDimsBefore = list->lhs();
            else
                allDimsBefore = &(*allDimsBefore * *list->lhs()->copyPtr());
        }

        list = list->rhs();
    }

    return has;
}

/*static void removeExternalStat(SgStatement* func, const set<string>& addedInterfaceFor)
{
    vector<SgStatement*> toRem;
    for (auto st = func; st != func->lastNodeOfStmt(); st = st->lexNext())
    {
        if (isSgExecutableStatement(st))
            break;
        if (st->variant() == CONTAINS_STMT)
            break;

        if (st->variant() == EXTERN_STAT)
        {
            vector<SgExpression*> list;
            SgExpression* ex = st->expr(0);
            int count = 0;
            while (ex)
            {
                if (addedInterfaceFor.find(ex->lhs()->symbol()->identifier()) == addedInterfaceFor.end())
                    list.push_back(ex->lhs());
                count++;
                ex = ex->rhs();
            }

            if (count == list.size())
                continue;

            if (list.size() == 0)
                toRem.push_back(st);
            else
                st->setExpression(0, makeExprList(list, false));
        }
    }

    for (auto& rem : toRem)
        rem->deleteStmt();
}*/

template<typename T>
static vector<FuncInfo*> sortByName(const T &funcs)
{
    vector<FuncInfo*> funcList;
    for (auto& elem : funcs)
        funcList.push_back(elem);

    std::sort(funcList.begin(), funcList.end(), [](FuncInfo* a, FuncInfo* b) { return a->funcName > b->funcName; });
    return funcList;
}

static bool hasDvmParallel(SgStatement *func)
{
    for (auto st = func; st != func->lastNodeOfStmt(); st = st->lexNext())
    {
        const int var = st->variant();
        if (var == DVM_PARALLEL_ON_DIR || var == ACC_REGION_DIR ||
            var == ACC_ACTUAL_DIR || var == ACC_GET_ACTUAL_DIR)
            return true;

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

    return false;
}

static map<FuncInfo*, bool> createDvmParallelInfo(const map<string, vector<FuncInfo*>>& allFuncInfo) {
    map<FuncInfo*, bool> hasDvmParallelMap;

    for (auto& funcByFile : allFuncInfo)
    {
        if (SgFile::switchToFile(funcByFile.first) == -1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        for (auto& func : funcByFile.second)
        {
            SgProgHedrStmt* prog = isSgProgHedrStmt(func->funcPointer->GetOriginal());
            if (prog == NULL)
                continue;

            hasDvmParallelMap[func] = hasDvmParallel(prog);
        }
    }

    bool changed = true;
    while (changed) 
    {
        changed = false;

        for (auto& funcByFile : allFuncInfo)
        {
            for (auto& func : funcByFile.second)
            {
                if (!hasDvmParallelMap[func]) 
                {
                    bool hasParallel = false;
                    for (auto& callF : func->callsFromV)
                    {
                        if (hasDvmParallelMap[callF]) 
                        {
                            hasParallel = true;
                            break;
                        }
                    }

                    if (hasParallel)
                    {
                        changed = true;
                        hasDvmParallelMap[func] = true;
                    }

                }
            }
        }
    }

    return hasDvmParallelMap;
}


void transformAssumedSizeParameters(const map<string, vector<FuncInfo*>>& allFuncInfo)
{
    map<string, vector<int>> assumedSizeArraysByFunc;
    const map<FuncInfo*, bool> hasDvmParallelMap = createDvmParallelInfo(allFuncInfo);
    
    for (auto& funcByFile : allFuncInfo)
    {
        if (SgFile::switchToFile(funcByFile.first) == -1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        for (auto& func : funcByFile.second)
        {
            SgProgHedrStmt* prog = isSgProgHedrStmt(func->funcPointer->GetOriginal());
            if (prog == NULL)
                continue;

            if (!hasDvmParallelMap.at(func))
                continue;

            vector<SgSymbol*> parNames;
            SgStatement* scope = prog->getScopeForDeclare();

            vector<SgExpression*> arrayRefs;
            bool hasRefs = false;
            for (int z = 0; z < func->funcParams.countOfPars; ++z)
            {
                if (func->funcParams.parametersT[z] == ARRAY_T ||
                    func->funcParams.parametersT[z] == STRING_ARRAY_T)
                {
                    auto s = prog->parameter(z);
                    const string name = s->identifier();

                    vector<SgStatement*> allDecls;
                    declaratedInStmt(s, &allDecls);

                    for (auto& decl : allDecls)
                    {
                        SgExpression* list = decl->expr(0);
                        while (list)
                        {
                            if (list->lhs() && list->lhs()->symbol()->identifier() == name)
                            {
                                if (changeIfHasStarRange(list->lhs(), scope, parNames))
                                    assumedSizeArraysByFunc[func->funcName].push_back(z);
                                break;
                            }
                            list = list->rhs();
                        }
                    }
                }
            }

            if (parNames.size())
            {
                SgProcHedrStmt* proc = isSgProcHedrStmt(func->funcPointer->GetOriginal());
                checkNull(proc, convertFileName(__FILE__).c_str(), __LINE__);

                //makeDeclaration(parNames, scope);

                for (auto& newPar : parNames)
                    proc->AddArg(*new SgVarRefExp(newPar));
            }
        }
    }

    if (assumedSizeArraysByFunc.size() == 0)
        return;

    for (auto& funcByFile : allFuncInfo)
    {
        if (SgFile::switchToFile(funcByFile.first) == -1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        SgSymbol* funcSize = new SgSymbol(FUNCTION_NAME, "size");

        for (auto& func : sortByName(funcByFile.second))
        {
            SgProgHedrStmt* prog = isSgProgHedrStmt(func->funcPointer->GetOriginal());
            if (prog == NULL)
                continue;

            for (auto& detailedCall : func->callsFromDetailed)
            {
                auto it = assumedSizeArraysByFunc.find(detailedCall.detailCallsFrom.first);
                if (it != assumedSizeArraysByFunc.end())
                {
                    auto pointer = detailedCall.pointerDetailCallsFrom;

                    SgExpression* list = NULL;
                    if (pointer.second == FUNC_CALL)
                    {
                        SgExpression* p = (SgExpression*)pointer.first;
                        list = p->lhs();
                    }
                    else
                    {
                        SgStatement* p = (SgStatement*)pointer.first;
                        list = p->expr(0);
                    }

                    SgExpression* last = list;
                    vector<SgExpression*> pars;
                    while (list)
                    {
                        last = list;
                        pars.push_back(list->lhs());
                        list = list->rhs();
                    }

                    for (int z = 0; z < it->second.size(); ++z)
                    {
                        int parNum = it->second[z];
                        if (parNum >= pars.size())
                            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                        //TODO: need to do it better
                        SgExpression* parArray = pars[parNum]->copyPtr();
                        parArray->setLhs(NULL);
                        parArray->setRhs(NULL);

                        SgFunctionCallExp* call = new SgFunctionCallExp(*funcSize, *parArray);

                        last->setRhs(new SgExpression(EXPR_LIST, call));
                        last = last->rhs();
                    }
                }
            }
        }
    }
}

static void setPureStatus(FuncInfo* func)
{
    if (func->isPure && !func->isMain)
    {
        SgStatement* header = func->funcPointer->GetOriginal();
        if (header->switchToFile() == false)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        bool isFunc = (header->variant() == FUNC_HEDR);
        bool hasOut = false;
        for (int z = 0; z < func->funcParams.countOfPars; ++z)
            hasOut |= func->funcParams.isArgOut(z);
        bool hasPure = (header->expr(2) != NULL) || ((header->symbol()->attributes() & PURE_BIT) != 0);
        bool hasIO = func->linesOfIO.size() || func->linesOfStop.size();

        if (!hasPure && !hasIO && ((isFunc == false) || (isFunc && hasOut == false)))
        {
            header->setExpression(2, new SgExpression(PURE_OP));
            header->symbol()->setAttribute(header->symbol()->attributes() | PURE_BIT);
        }
    }
}

void setPureStatus(const set<FuncInfo*>& funcInfo)
{
    for (auto& func : funcInfo)
        setPureStatus(func);
}

void setPureStatus(const map<string, vector<FuncInfo*>>& allFuncInfo)
{
    for (auto& funcByFile : allFuncInfo)
        for (auto& func : funcByFile.second)
            setPureStatus(func);

    for (auto& funcByFile : allFuncInfo)
        for (auto& func : funcByFile.second)
            createInterfacesForOutCalls(func);
}

map<SgStatement*, set<string>> fillFromIntent(SgStatement* header)
{
    map<SgStatement*, set<string>> intentS;

    if (header->variant() == ENTRY_STAT)
        while (isSgProgHedrStmt(header) == NULL)
            header = header->controlParent();

    SgStatement* last = header->lastNodeOfStmt();
    for (auto stmt = header->lexNext(); stmt && stmt != last; stmt = stmt->lexNext())
    {
        if (stmt->variant() == CONTAINS_STMT)
            break;

        if (isSgExecutableStatement(stmt))
            break;

        if (stmt->variant() == INTENT_STMT)
        {
            SgIntentStmt* s = (SgIntentStmt*)stmt;
            for (int i = 0; i < s->numberOfVars(); i++)
                intentS[s].insert(s->var(i)->symbol()->identifier());
        }
        else
        {
            //check intent in decl
            SgExpression* ex = stmt->expr(2);
            bool containsIntent = false;
            while (ex)
            {
                if (ex->lhs())
                {
                    int var = ex->lhs()->variant();
                    if (var == IN_OP || var == INOUT_OP || var == OUT_OP)
                        containsIntent = true;
                }
                ex = ex->rhs();
            }

            if (containsIntent)
            {
                SgExpression* ex = stmt->expr(0);
                while (ex)
                {
                    if (ex->lhs() && ex->lhs()->symbol())
                        intentS[stmt].insert(ex->lhs()->symbol()->identifier());
                    ex = ex->rhs();
                }
            }
        }
    }
    return intentS;
}

static void insertIntents(set<string>& identificators, SgStatement* header, const map<string, SgSymbol*>& parSym, int intentVariant, int intentBit)
{
    if (identificators.size() == 0)
        return;

    if (header->variant() == ENTRY_STAT)
        while (isSgProgHedrStmt(header) == NULL)
            header = header->controlParent();

    SgStatement* last = header->lastNodeOfStmt();
    SgStatement* lastDecl = header;
    for (auto stmt = header->lexNext(); stmt && stmt != last; stmt = stmt->lexNext())
    {
        if (stmt->variant() == CONTAINS_STMT)
            break;

        if (isSgExecutableStatement(stmt))
            break;

        if (stmt->variant() != ENTRY_STAT)
            lastDecl = stmt;

        if (stmt->variant() == VAR_DECL_90)
        {
            SgVarDeclStmt* s = (SgVarDeclStmt*)stmt;
            for (int i = 0; i < s->numberOfAttributes(); i++)
            {
                if (s->attribute(i)->variant() == intentVariant)
                {
                    for (int i = 0; i < s->numberOfVars(); i++)
                    {
                        auto sname = s->var(i)->symbol()->identifier();
                        if (identificators.count(sname))
                            identificators.erase(sname);
                    }
                }
            }
        }
        else if (stmt->variant() == INTENT_STMT)
        {
            SgIntentStmt* s = (SgIntentStmt*)stmt;
            if (s->attribute()->variant() == intentVariant)
            {
                for (int i = 0; i < s->numberOfVars(); i++)
                {
                    auto sname = s->var(i)->symbol()->identifier();
                    if (identificators.count(sname))
                        identificators.erase(sname);
                }
            }
        }
    }

    SgExpression* attr = new SgExpression(intentVariant);
    vector<SgExpression*> args;
    for (auto& par : identificators)
    {
        if (parSym.count(par) == 0)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        auto s = parSym.at(par);
        args.push_back(new SgVarRefExp(s));
        s->setAttribute(s->attributes() | intentBit);
    }

    if (args.size())
    {
        SgIntentStmt* intent = new SgIntentStmt(*makeExprList(args), *attr);
        intent->setlineNumber(getNextNegativeLineNumber());

        lastDecl->insertStmtAfter(*intent, (header == lastDecl) ? *header : *lastDecl->controlParent());
    }
}

static SgSymbol* getParameter(SgStatement* stat, int n)
{
    SgSymbol* retVal = NULL;

    auto funcStat = isSgProgHedrStmt(stat);
    if (funcStat)
        retVal = funcStat->parameter(n);
    else if (stat->variant() == ENTRY_STAT)
    {
        SgExpression* list = stat->expr(0);
        while (n != 0)
        {
            --n;
            list = list->rhs();
        }
        retVal = list->lhs()->symbol();
    }

    checkNull(retVal, convertFileName(__FILE__).c_str(), __LINE__);
    return retVal;
}

static void intentInsert(const FuncInfo* func, SgStatement* headerSt)
{
    if (func == NULL)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    if (func->funcPointer->variant() == ENTRY_STAT)
        return;

    set<string> InIdentificators;
    set<string> OutIdentificators;
    set<string> InOutIdentificators;

    map<string, SgSymbol*> parSym;
    set<string> intentS;
    auto intentsByStat = fillFromIntent(headerSt);
    for (auto& elem : intentsByStat)
        intentS.insert(elem.second.begin(), elem.second.end());

    intentS.insert(func->callsFrom.begin(), func->callsFrom.end());

    for (int i = 0; i < func->funcParams.countOfPars; i++)
    {
        SgSymbol* parS = getParameter(headerSt, i);
        const string ident = parS->identifier();

        if (ident == "*" || parS->attributes() & EXTERNAL_BIT)
            continue;

        parSym[ident] = parS;
        if (intentS.find(ident) != intentS.end())
            continue;

        if (func->funcParams.isArgInOut(i))
            InOutIdentificators.insert(ident);
        else if (func->funcParams.isArgIn(i))
            InIdentificators.insert(ident);
        else if (func->funcParams.isArgOut(i))
            OutIdentificators.insert(ident);
    }

    //remove conflicted intents
    for (auto& entry : func->entry)
    {
        for (int i = 0; i < entry->funcParams.countOfPars; i++)
        {
            const auto& ident = entry->funcParams.identificators[i];
            if (entry->funcParams.isArgInOut(i))
            {
                if (InIdentificators.count(ident))
                    InIdentificators.erase(ident);
                if (OutIdentificators.count(ident))
                    OutIdentificators.erase(ident);
            }
            else if (entry->funcParams.isArgIn(i))
            {
                if (InOutIdentificators.count(ident))
                    InOutIdentificators.erase(ident);
                if (OutIdentificators.count(ident))
                    OutIdentificators.erase(ident);
            }
            else if (entry->funcParams.isArgOut(i))
            {
                if (InIdentificators.count(ident))
                    InIdentificators.erase(ident);
                if (InOutIdentificators.count(ident))
                    InOutIdentificators.erase(ident);
            }
        }
    }

    insertIntents(InOutIdentificators, headerSt, parSym, INOUT_OP, INOUT_BIT);
    insertIntents(InIdentificators, headerSt, parSym, IN_OP, IN_BIT);
    insertIntents(OutIdentificators, headerSt, parSym, OUT_OP, OUT_BIT);
}

void intentInsert(const vector<FuncInfo*>& allFuncInfo)
{
    for (auto& func : allFuncInfo)
    {
        if (func->isMain)
            continue;
        intentInsert(func, func->funcPointer->GetOriginal());
    }
}

void intentInsertToInterfaces(const map<string, vector<FuncInfo*>>& allFuncInfo)
{
    for (auto& funcByFile : allFuncInfo)
    {
        if (SgFile::switchToFile(funcByFile.first) == -1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        for (auto& func : funcByFile.second)
        {
            if (func->interfaceBlocks.size() == 0)
                continue;

            auto start = func->funcPointer->GetOriginal();
            auto end = start->lastNodeOfStmt();
            for (auto st = start; st != end; st = st->lexNext())
            {
                if (isSgExecutableStatement(st))
                    break;

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

                if (st->controlParent()->variant() == INTERFACE_STMT)
                {
                    if (st->variant() == PROC_HEDR || st->variant() == FUNC_HEDR)
                    {
                        auto it = func->interfaceBlocks.find(st->symbol()->identifier());
                        if (it == func->interfaceBlocks.end())
                            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                        intentInsert(it->second, st);
                    }
                }
            }
        }
    }
}

//DEBUG
/*static void printcvu(map<string, vector<int>>& commonVarsUsed)
{
    for (auto& common : commonVarsUsed)
    {
        printf(">%s\n", common.first.c_str());
        for (int posVar : common.second)
            printf("\t%d\n", posVar);
    }
}*/

static void collectForChange(set<FuncInfo*>& allForChange, FuncInfo* start)
{
    allForChange.insert(start);
    bool chagned = true;
    while (chagned)
    {
        set<FuncInfo*> newAdd;
        for (auto& elem : allForChange)
            for (auto& call : elem->callsFromV)
                if (allForChange.find(call) == allForChange.end())
                    newAdd.insert(call);

        chagned = newAdd.size() != 0;

        allForChange.insert(newAdd.begin(), newAdd.end());
    }
}

template<typename CallExp>
static void transferVarToArg(const map<string, vector<int>>& commonVarsUsed, const map<string, CommonBlock*>& commonBlocks,
                             const FuncInfo* curFunc, CallExp* callExp)
{
    for (auto& common : commonVarsUsed)
    {
        for (auto& posVar : common.second)
        {
            const Variable* var = commonBlocks.find(common.first)->second->getGroupedVars().find(posVar)->second[0];
            string name = "";
            if (curFunc->isMain && curFunc->commonBlocks.count(common.first) > 0)
            {
                for (auto& v : commonBlocks.find(common.first)->second->getVariables(curFunc->fileName, curFunc->funcName))
                {
                    if (v->getPosition() == posVar)
                    {
                        name = v->getName();
                        break;
                    }
                }
            }
            else if ((int)(string(var->getName()).find("c_" + common.first + "_")) < 0)
                name = "c_" + common.first + "_" + var->getName();
            else
                name = var->getName();
            SgSymbol* s = new SgSymbol(var->getSymbol()->variant(), name.c_str());
            callExp->addArg(*new SgVarRefExp(*s));
        }
    }
}

static void findInterfaces(FuncInfo* func, vector<SgStatement*>& ifaces)
{
    for (auto& callFunc : func->callsTo)
    {
        SgStatement* iface = NULL;
        if (callFunc->interfaceBlocks.find(func->funcName) != callFunc->interfaceBlocks.end())
        {
            SgStatement* hedr = callFunc->funcPointer->GetOriginal();
            for (SgStatement* start = hedr->lexNext(), *end = hedr->lastNodeOfStmt(); start != end; start = start->lexNext())
            {
                if (start->variant() == INTERFACE_STMT)
                {
                    for (int i = 0; i < start->numberOfChildrenList1(); i++)
                    {
                        if ((isSgProgHedrStmt(start->childList1(i))) && start->childList1(i)->symbol()->identifier() == func->funcName)
                        {
                            iface = start->childList1(i);
                            break;
                        }
                    }
                }

                if (iface)
                {
                    ifaces.push_back(iface);
                    break;
                }
            }
        }
    }
}

static void transferCommons(set<FuncInfo*>& allForChange, map <FuncInfo*, map<string, vector<int>>>& funcCommons, map<string, vector<int>>& commonVarsUsed,
                            FuncInfo* curFunc, FuncInfo* precFunc, const map<string, CommonBlock*>& commonBlocks, map<FuncInfo*, set<string>>& funcCommonDeclared)
{
    if (commonVarsUsed.empty())
        return;

    if (funcCommons.count(curFunc) == 0)
        funcCommons[curFunc] = map<string, vector<int>>();

    //add params to calls
    if (curFunc != precFunc)
    {
        if (SgFile::switchToFile(curFunc->fileName) == -1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        for (auto& callInfo : curFunc->callsFromDetailed)
        {
            auto& call = callInfo.pointerDetailCallsFrom;
            if (isSgFuncHedrStmt(precFunc->funcPointer->GetOriginal()) && call.second == FUNC_CALL)
            {
                SgFunctionCallExp* callExp = (SgFunctionCallExp*)call.first;
                if (callExp->funName()->identifier() == precFunc->funcName)
                    transferVarToArg(commonVarsUsed, commonBlocks, curFunc, callExp);
            }
            else if (isSgProcHedrStmt(precFunc->funcPointer->GetOriginal()) && call.second == PROC_STAT)
            {
                SgCallStmt* callSt = (SgCallStmt*)call.first;
                if (callSt->name()->identifier() == precFunc->funcName)
                    transferVarToArg(commonVarsUsed, commonBlocks, curFunc, callSt);
            }
        }
    }

    map<string, vector<int>> nextCommonVarsUsed;
    //find the things we have already done
    for (auto& common : commonVarsUsed)
    {
        if (funcCommons[curFunc].count(common.first) == 0)
            funcCommons[curFunc][common.first] = vector<int>();

        for (auto& var : common.second)
        {
            bool done = false;
            for (auto& v : funcCommons[curFunc][common.first])
                if (v == var)
                    done = true;

            if (!done)
            {
                funcCommons[curFunc][common.first].push_back(var);
                if (nextCommonVarsUsed.count(common.first) == 0)
                    nextCommonVarsUsed[common.first] = vector<int>();
                nextCommonVarsUsed[common.first].push_back(var);
            }
        }
    }

    vector<SgStatement*> ifaces;
    ifaces.push_back(curFunc->funcPointer->GetOriginal());
    findInterfaces(curFunc, ifaces);

    for (auto& common : nextCommonVarsUsed)
    {
        bool uses = false;
        auto groupedVars = commonBlocks.find(common.first)->second->getGroupedVars();

        if (curFunc->commonBlocks.count(common.first) > 0) //rename common vars in funcs
        {
            uses = true;
            const vector<const Variable*>& vars = commonBlocks.find(common.first)->second->getVariables(curFunc->fileName, curFunc->funcName);
            if (allForChange.count(curFunc))
            {
                for (auto& var : vars)
                {
                    bool contains = false;
                    for (auto& v : common.second)
                        if (v == var->getPosition())
                            contains = true;

                    if (contains)
                    {
                        for (auto& varUse : var->getAllUse())
                        {
                            if (varUse.getFileName() == curFunc->fileName && varUse.getFunctionName() == curFunc->funcName)
                            {
                                string name;
                                if ((int)(string(groupedVars[var->getPosition()][0]->getName()).find("c_" + common.first + "_")) < 0)
                                    name = "c_" + common.first + "_" + groupedVars[var->getPosition()][0]->getName();
                                else
                                    name = groupedVars[var->getPosition()][0]->getName();
                                varUse.getUseS()->changeName(name.c_str());
                                break;
                            }
                        }
                    }
                }
            }
        }
        else if (!allForChange.count(curFunc)) //add of commons to main
        {
            SgExprListExp* res = NULL;
            vector <SgSymbol*> varsToDeclare = vector <SgSymbol*>();
            for (auto it = groupedVars.rbegin(); it != groupedVars.rend(); it++)
            {
                string name;
                if ((int)(string(it->second[0]->getName()).find("c_" + common.first + "_")) < 0)
                    name = "c_" + common.first + "_" + it->second[0]->getName();
                else
                    name = it->second[0]->getName();

                SgSymbol* symb = it->second[0]->getSymbol();
                SgType* type = symb->type();
                SgSymbol* s = new SgSymbol(symb->variant(), name.c_str(), type, curFunc->funcPointer);
                SgVarRefExp* vr = new SgVarRefExp(s);
                SgExprListExp* el = new SgExprListExp();
                el->setLhs(vr);
                el->setRhs(res);
                res = el;
                varsToDeclare.push_back(s);
            }

            SgSymbol* commSymb = new SgSymbol(VARIABLE_NAME, common.first.c_str());
            SgExprListExp* commList = new SgExprListExp(COMM_LIST);
            SgStatement* commStat = new SgStatement(COMM_STAT);
            commStat->setExpression(0, *commList);
            commList->setSymbol(commSymb);
            commList->setLhs(res);

            SgStatement* firstExDec, * hedr = curFunc->funcPointer->GetOriginal();
            for (SgStatement* start = hedr->lexNext(), *end = hedr->lastNodeOfStmt(); start != end; start = start->lexNext())
            {
                if ((isSgExecutableStatement(start) || isSgDeclarationStatement(start)) && !strcmp(hedr->fileName(), start->fileName())) {
                    firstExDec = start;
                    break;
                }
            }
            commStat->setlineNumber(firstExDec->lineNumber());
            firstExDec->insertStmtBefore(*commStat, *(hedr));

            for (auto& var : varsToDeclare)
            {
                vector<SgSymbol*> varVec = vector<SgSymbol*>();
                varVec.push_back(var);
                SgStatement* decl = makeDeclaration(NULL, varVec);
                for (int i = 0; i < 3; i++)
                {
                    SgExpression* e;
                    if (e = decl->expr(i))
                        decl->setExpression(i, CalculateInteger(ReplaceConstant(e)));
                }
                decl->setlineNumber(firstExDec->lineNumber());
                decl->setFileName(hedr->fileName());
                commStat->insertStmtAfter(*decl, *(hedr));
            }
        }

        //parametrs add
        if (allForChange.count(curFunc))
        {
            for (int i = 0; i < ifaces.size(); i++) {
                for (auto& posVar : common.second)
                {
                    const Variable* var = commonBlocks.find(common.first)->second->getGroupedVars().find(posVar)->second[0];
                    SgSymbol* symb = var->getSymbol();

                    string name;
                    if ((int)(string(symb->identifier()).find("c_" + common.first + "_")) < 0)
                        name = "c_" + common.first + "_" + var->getSymbol()->identifier();
                    else
                        name = symb->identifier();

                    SgSymbol* s = new SgSymbol(symb->variant(), name.c_str(), symb->type(), ifaces[i]);

                    SgStatement* hedr = ifaces[i];
                    SgExpression* result = hedr->expr(0) == NULL ? NULL : hedr->expr(0)->copyPtr();

                    if (isSgFuncHedrStmt(hedr))
                        isSgFuncHedrStmt(hedr)->AddArg(*new SgVarRefExp(s));
                    else if (isSgProcHedrStmt(hedr))
                        isSgProcHedrStmt(hedr)->AddArg(*new SgVarRefExp(s));
                    else
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                    if (result == NULL)
                    {
                        if (hedr->expr(0) != NULL)
                            hedr->setExpression(0, NULL);
                    }
                    else
                    {
                        if (hedr->expr(0) != NULL)
                            hedr->setExpression(0, result);
                        else if (string(hedr->expr(0)->unparse()) != result->unparse())
                            hedr->setExpression(0, result);
                    }

                    curFunc->funcPointer->GetOriginal()->lexNext()->deleteStmt();
                    if (!funcCommonDeclared[curFunc].count(common.first) || i!=0)
                    {
                        vector<SgSymbol*> varVec = vector<SgSymbol*>();
                        varVec.push_back(s);
                        SgStatement* decl = makeDeclaration(NULL, varVec);
                        for (int i = 0; i < 3; i++)
                        {
                            SgExpression* e;
                            if (e = decl->expr(i))
                                decl->setExpression(i, CalculateInteger(ReplaceConstant(e)));
                        }
                        SgStatement* firstExDec;
                        for (SgStatement* start = hedr->lexNext(), *end = hedr->lastNodeOfStmt(); start != end; start = start->lexNext())
                        {

                            if ((isSgExecutableStatement(start) || isSgDeclarationStatement(start)) && !strcmp(hedr->fileName(), start->fileName())) {
                                firstExDec = start;
                                break;
                            }
                        }
                        decl->setlineNumber(firstExDec->lineNumber());
                        decl->setFileName(hedr->fileName());
                        firstExDec->insertStmtBefore(*decl, *(hedr));
                    }
                }
            }
        }
    }

    for (auto& callFunc : curFunc->callsTo)
        transferCommons(allForChange, funcCommons, nextCommonVarsUsed, callFunc, curFunc, commonBlocks, funcCommonDeclared);
}

static void fillUsedVars(set<string>& usedVars, SgExpression* exp)
{
    if (exp)
    {
        if (exp->variant() == VAR_REF || exp->variant() == ARRAY_REF)
            usedVars.insert(exp->symbol()->identifier());
        fillUsedVars(usedVars, exp->lhs());
        fillUsedVars(usedVars, exp->rhs());
    }
}

void commonTransfer(const map<string, vector<FuncInfo*>>& allFuncInfo, const map<string, CommonBlock*>& commonBlocks)
{
    map <FuncInfo*, map<string, vector<int>>> funcCommons;
    map <FuncInfo*, set<string>> funcCommonDeclared;
    FuncInfo* start = NULL;
    set<FuncInfo*> allForChange;
    for (auto& byfile : allFuncInfo)
    {
        if (SgFile::switchToFile(byfile.first) == -1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        for (auto& func : byfile.second)
        {
            SgStatement* st = func->funcPointer->GetOriginal();
            if (st->variant() < 0 || func->isInterface)
                continue;

            if (func->commonBlocks.size() > 0 && st->variant() != PROG_HEDR)
            {
                for (SgStatement* start = st, *end = st->lastNodeOfStmt(); start != end;)
                {
                    if (start->variant() == CONTAINS_STMT)
                        break;

                    SgStatement* next = start->lexNext();
                    if (start->variant() == COMM_STAT && string(start->fileName()) == func->fileName)
                    {
                        if (funcCommonDeclared.count(func) == 0)
                            funcCommonDeclared[func] = set<string>();

                        for (SgExpression* e = start->expr(0); e; e = e->rhs())
                            funcCommonDeclared[func].insert(e->symbol() ? e->symbol()->identifier() : "spf_unnamed");
                        start->deleteStmt();
                    }
                    start = next;
                }
            }

            if (func->isMain)
                start = func;
        }
    }

    checkNull(start, convertFileName(__FILE__).c_str(), __LINE__);
    collectForChange(allForChange, start);
    allForChange.erase(start);

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

        SgStatement* st = func->funcPointer->GetOriginal();
        if (st->variant() < 0 || st->variant() == PROG_HEDR || func->isInterface)
            continue;

        if (func->commonBlocks.size() > 0)
        {
            map<string, vector<int>> commonVarsUsed;
            set<string> usedVars;
            for (SgStatement* start = st->lastDeclaration()->lexNext(), *end = st->lastNodeOfStmt(); start != end; start = start->lexNext())
            {
                if (start->variant() == CONTAINS_STMT)
                    break;

                if (isSgExecutableStatement(start))
                    for (int i = 0; i < 3; i++)
                        fillUsedVars(usedVars, start->expr(i));
            }

            for (auto& var : usedVars)
            {
                for (auto& common : func->commonBlocks)
                {
                    const string name = common.first;
                    if (common.second.count(var) > 0)
                    {
                        if (commonVarsUsed.count(common.first) == 0)
                            commonVarsUsed[name] = vector<int>();
                        for (auto& cvar : commonBlocks.find(name)->second->getVariables(func->fileName, func->funcName))
                            if (cvar->getName() == var)
                                commonVarsUsed[name].push_back(cvar->getPosition());
                    }
                }
            }
            transferCommons(allForChange, funcCommons, commonVarsUsed, func, func, commonBlocks, funcCommonDeclared);
        }
    }
}

static string changeData(const string& data, const map<string, string>& constSymVars, const map<string, string>& locVars)
{
    int curChar = 0;
    string ident = "";
    string res = "";
    while (curChar < data.length())
    {
        switch (data[curChar])
        {
        case ' ':
        case ',':
        case ')':
        case '(':
        case '=':
        case '*':
        case '/':
        case '\n':
        case '\t':
            if (ident.size() > 0)
            {
                if (constSymVars.count(ident) > 0)
                    res += constSymVars.at(ident);
                else if (locVars.count(ident) > 0)
                    res += locVars.at(ident);
                else
                    res += ident;
                ident = "";
            }
            res += tolower(data[curChar]);
            curChar++;
            break;
        default:
            ident += tolower(data[curChar]);
            curChar++;
            break;
        }
    }
    return res;
}

static void transferSave(map<FuncInfo*, set<FuncInfo*>>& funcAddedVarsFuncs, vector <SgSymbol*>& varsToTransfer, vector <string>& dataToTransfer,
                         FuncInfo* curFunc, FuncInfo* precFunc, FuncInfo* startFunc)
{
    if (curFunc != precFunc)
    {
        if (SgFile::switchToFile(curFunc->fileName) == -1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        for (auto& callInfo : curFunc->callsFromDetailed)
        {
            auto& call = callInfo.pointerDetailCallsFrom;
            if (isSgFuncHedrStmt(precFunc->funcPointer->GetOriginal()) && call.second == FUNC_CALL)
            {
                SgFunctionCallExp* callExp = (SgFunctionCallExp*)call.first;
                if (callExp->funName()->identifier() == precFunc->funcName)
                    for (auto& var : varsToTransfer)
                        callExp->addArg(*new SgVarRefExp(*var));
            }
            else if (isSgProcHedrStmt(precFunc->funcPointer->GetOriginal()) && call.second == PROC_STAT)
            {
                SgCallStmt* callSt = (SgCallStmt*)call.first;
                if (callSt->name()->identifier() == precFunc->funcName)
                    for (auto& var : varsToTransfer)
                        callSt->addArg(*new SgVarRefExp(*var));
            }
        }
    }

    if (!funcAddedVarsFuncs.count(curFunc))
        funcAddedVarsFuncs[curFunc] = set<FuncInfo*>();
    else if (funcAddedVarsFuncs[curFunc].count(startFunc))
        return;

    vector<SgStatement*> ifaces;
    ifaces.push_back(curFunc->funcPointer->GetOriginal());
    findInterfaces(curFunc, ifaces);

    funcAddedVarsFuncs[curFunc].insert(startFunc);
    if (!curFunc->isMain)
    {
        for (int i = 0; i < ifaces.size(); i++)
        {
            SgStatement* hedr = ifaces[i];
            for (auto& var : varsToTransfer)
            {
                ((SgProcHedrStmt*)(hedr))->AddArg(*new SgVarRefExp(var->copy()));
                hedr->lexNext()->deleteStmt();
                if (curFunc != startFunc || i!=0)
                {
                    vector<SgSymbol*> varVec = vector<SgSymbol*>();
                    varVec.push_back(var);
                    SgStatement* decl = makeDeclaration(NULL, varVec);
                    for (int i = 0; i < 3; i++)
                    {
                        SgExpression* e;
                        if (e = decl->expr(i))
                            decl->setExpression(i, CalculateInteger(ReplaceConstant(e)));
                    }

                    SgStatement* firstExDec = NULL;
                    for (SgStatement* start = hedr->lexNext(), *end = hedr->lastNodeOfStmt(); start != end; start = start->lexNext())
                    {
                        if ((isSgExecutableStatement(start) || isSgDeclarationStatement(start)) && !strcmp(hedr->fileName(), start->fileName()))
                        {
                            firstExDec = start;
                            break;
                        }
                    }
                    checkNull(firstExDec, convertFileName(__FILE__).c_str(), __LINE__);

                    decl->setlineNumber(firstExDec->lineNumber());
                    decl->setFileName(hedr->fileName());
                    firstExDec->insertStmtBefore(*decl, *(hedr));
                }
            }
        }

        for (auto& callFunc : curFunc->callsTo)
            transferSave(funcAddedVarsFuncs, varsToTransfer, dataToTransfer, callFunc, curFunc, startFunc);
    }
    else
    {
        SgStatement* firstExDec = NULL;
        auto hedr = curFunc->funcPointer->GetOriginal();

        for (SgStatement* start = hedr->lexNext(), *end = hedr->lastNodeOfStmt(); start != end; start = start->lexNext())
        {
            if ((isSgExecutableStatement(start) || isSgDeclarationStatement(start)) && !strcmp(hedr->fileName(), start->fileName()))
            {
                firstExDec = start;
                break;
            }
        }
        checkNull(firstExDec, convertFileName(__FILE__).c_str(), __LINE__);

        for (auto& var : varsToTransfer)
        {
            vector<SgSymbol*> varVec = vector<SgSymbol*>();
            varVec.push_back(var);
            SgStatement* decl = makeDeclaration(NULL, varVec);
            for (int i = 0; i < 3; i++)
            {
                SgExpression* e;
                if (e = decl->expr(i))
                    decl->setExpression(i, CalculateInteger(ReplaceConstant(e)));
            }

            decl->setlineNumber(firstExDec->lineNumber());
            decl->setFileName(hedr->fileName());
            firstExDec->insertStmtBefore(*decl, *(hedr));
        }

        for (auto& data : dataToTransfer)
            firstExDec->addComment(data.c_str());
    }
}

void saveTransfer(const map<string, vector<FuncInfo*>>& allFuncInfo)
{
    FuncInfo* start = NULL;
    set<FuncInfo*> allForChange;
    map<FuncInfo*, set<FuncInfo*>> funcAddedVarsFuncs;

    for (auto& byfile : allFuncInfo)
        for (auto& func : byfile.second)
            if (func->isMain)
                start = func;

    collectForChange(allForChange, start);
    allForChange.erase(start);
    for (auto& func : allForChange)
    {
        if (SgFile::switchToFile(func->fileName) == -1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        SgStatement* st = func->funcPointer->GetOriginal();
        vector <SgSymbol*> varsToTransfer;
        vector <string> dataToTransfer;
        map<SgSymbol*, SgExpression*> varInit;
        map<string, string> constSymVars;
        map <string, string> locVars;
        set <string> params;
        bool hasComplType = false;
        SgStatement* lst = st->lastNodeOfStmt();
        SgSymbol* s, * sl;
        bool allSave = false;

        for (SgStatement* start = st, *end = lst; start != end; start = start->lexNext())
        {
            if (start->variant() == CONTAINS_STMT)
                break;

            if (start->variant() == SAVE_DECL && !start->expr(0))
                allSave = true;
        }

        for (auto& s : func->funcParams.identificators)
            params.insert(s);

        sl = lst->lexNext() ? lst->lexNext()->symbol() : NULL;
        for (s = st->symbol(); s != sl && s != NULL; s = s->next())
        {
            SgConstantSymb* sc = isSgConstantSymb(s);
            if (sc && sc->constantValue())
                constSymVars[string(s->identifier())] = string(sc->constantValue()->unparse());

            if (s->scope() == st)
            {
                if ( (s->attributes() & SAVE_BIT) ||
                     (s->attributes() & DATA_BIT) ||
                     allSave && s->variant() == VARIABLE_NAME && !params.count(s->identifier()))
                {
                    if ((s->type() ? s->type()->variant() : (T_COMPLEX + 1)) > T_COMPLEX)
                    {
                        hasComplType = true;
                        break;
                    }

                    string newName = "s_" + func->funcName + "_" + s->identifier();
                    locVars[s->identifier()] = newName;
                    s->changeName(newName.c_str());
                    varsToTransfer.push_back(s);
                }
            }
        }

        if (hasComplType)
            continue;

        for (SgStatement* start = st, *end = lst; start != end;)
        {
            SgStatement* next = start->lexNext();
            if (strcmp(start->fileName(), st->fileName()))
            {
                start = next;
                continue;
            }

            if (start->variant() == DATA_DECL)
            {
                dataToTransfer.push_back(changeData(start->sunparse(), constSymVars, locVars));
                start->deleteStmt();
            }
            else if (start->variant() == SAVE_DECL)
                start->deleteStmt();
            else if (start->variant() == VAR_DECL || start->variant() == VAR_DECL_90)
            {
                SgVarDeclStmt* vst = (SgVarDeclStmt*)start;
                for (int i = 0; i < vst->numberOfVars(); i++)
                {
                    if (vst->initialValue(i))
                    {
                        string data = "      data " + vst->var(i)->lhs()->sunparse() + " / " + vst->initialValue(i)->sunparse() + " / \n";
                        dataToTransfer.push_back(changeData(data, constSymVars, locVars));
                        vst->clearInitialValue(i);
                    }
                }

                SgExprListExp* attrsNoSave = new SgExprListExp();
                bool needChange = false;
                for (int i = 0; i < vst->numberOfAttributes(); i++)
                {
                    if (vst->attribute(i)->variant() != SAVE_OP)
                    {
                        attrsNoSave->append(vst->attribute(i)->copy());
                    }
                    else
                        needChange = true;
                }

                if (needChange)
                {
                    SgVarDeclStmt* newVst;
                    if (!attrsNoSave->length())
                        newVst = new SgVarDeclStmt(vst->varList()->copy(), *attrsNoSave, vst->type()->copy());
                    else
                        newVst = new SgVarDeclStmt(vst->varList()->copy(), vst->type()->copy());

                    newVst->setlineNumber(vst->lineNumber());
                    newVst->setComments(vst->comments());
                    vst->replaceWithStmt(*newVst);
                }
            }
            start = next;
        }

        if (varsToTransfer.size())
            transferSave(funcAddedVarsFuncs, varsToTransfer, dataToTransfer, func, func, func);
    }
}

//DEBUG
/*static void printmbu(map<string, vector<pair<SgSymbol*, SgSymbol*>>> modByUse)
{
    for (auto& mod : modByUse)
    {
        printf("|%s| \n", mod.first.c_str());
        for (auto& var : mod.second)
            printf("\t%s => %s\n", var.first->identifier(), var.second->identifier());
    }
}*/

static string makeName(SgSymbol* var, map<SgSymbol*, set< SgSymbol*>>& modVarsToAdd, const set<string>& useMod,
                       const map<string, vector<pair<SgSymbol*, SgSymbol*>>>& modByUse,
                       const map<string, vector<pair<SgSymbol*, SgSymbol*>>>& modByUseOnly)
{
    string name = "", modName = OriginalSymbol(var)->scope()->symbol()->identifier();
    SgSymbol* modS = OriginalSymbol(var)->scope()->symbol();
    string varOrName = OriginalSymbol(var)->identifier();
    size_t um = useMod.count(modName), mbu = modByUse.count(modName), mbuo = modByUseOnly.count(modName);

    if (um && !mbu && !mbuo)
        name = varOrName;
    else if (mbu)
    {
        for (auto& elem : modByUse.at(modName))
        {
            if ((elem.second ? elem.second : elem.first)->identifier() == varOrName)
            {
                name = elem.first->identifier();
                break;
            }
        }

        if (!name.length())
            name = varOrName;
    }
    else if (mbuo)
    {
        for (auto& elem : modByUseOnly.at(modName))
        {
            if ((elem.second ? elem.second : elem.first)->identifier() == varOrName)
            {
                name = elem.first->identifier();
                break;
            }
        }

        if (!name.length())
        {
            name = "m_" + modName + "_" + varOrName;
            if (!modVarsToAdd.count(modS))
                modVarsToAdd[modS] = set<SgSymbol*>();
            modVarsToAdd[modS].insert(OriginalSymbol(var));
        }
    }
    else
    {
        name = "m_" + modName + "_" + varOrName;
        if (!modVarsToAdd.count(modS))
            modVarsToAdd[modS] = set<SgSymbol*>();
        modVarsToAdd[modS].insert(OriginalSymbol(var));
    }
    return name;
}

static bool filterFromList(SgStatement* st, const set<string>& idents, bool exclude = false)
{
    bool empty = false;
    SgExpression* list = st->expr(0);
    vector<SgExpression*> newList;

    int total = 0;
    while (list)
    {
        if (exclude)
        {
            if (idents.find(list->lhs()->symbol()->identifier()) == idents.end())
                newList.push_back(list->lhs());
        }
        else
        {
            if (idents.find(list->lhs()->symbol()->identifier()) != idents.end())
                newList.push_back(list->lhs());
        }
        total++;
        list = list->rhs();
    }

    if (newList.size() == 0)
        empty = true;
    else if (total != newList.size())
        st->setExpression(0, makeExprList(newList));

    return empty;
}

static string getInterfaceBlock(SgStatement* func, const FuncParam& pars)
{
    string oldFile = current_file->filename();
    if (!func->switchToFile())
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    auto copy = duplicateProcedure(func, NULL, false, false, false, true);
    const set<string> idents(pars.identificators.begin(), pars.identificators.end());

    //remove all exec
    SgStatement* st = copy->lexNext();
    SgStatement* last = copy->lastNodeOfStmt();
    vector<SgStatement*> toExtract;

    while (st != last)
    {
        if (isDVM_stat(st) || isSPF_stat(st))
        {
            if (st->variant() != ACC_ROUTINE_DIR)
            {
                toExtract.push_back(st);
                st = st->lexNext();
            }
            else
                st = st->lexNext();
        }
        else if (isSgExecutableStatement(st))
        {
            SgStatement* next = st->lastNodeOfStmt();
            if (next != last)
                next = next->lexNext();
            toExtract.push_back(st);
            st = next;
        }
        else
            st = st->lexNext();
    }

    //remove unused declarations
    st = copy->lexNext();
    while (st != last)
    {
        const int var = st->variant();

        if (var == VAR_DECL || var == VAR_DECL_90 || var == DIM_STAT ||
            var == INTENT_STMT || var == EXTERN_STAT)
        {
            bool empty = filterFromList(st, idents);
            if (empty)
            {
                toExtract.push_back(st);
                st = st->lexNext();
                continue;
            }
        }
        else if (!isDVM_stat(st) && !isSPF_stat(st))
            toExtract.push_back(st);

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

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

    string codeString = copy->unparse();

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

    //insert tabs
    const string tab = "    ";
    const int countEnds = std::count(codeString.begin(), codeString.end(), '\n');

    string retVal = "  ";
    retVal.reserve(retVal.size() + codeString.size() + countEnds * tab.size());

    for (int z = 0, ends = 0; z < codeString.size(); ++z)
    {
        retVal += codeString[z];
        if (codeString[z] == '\n')
        {
            ends++;
            if (ends == countEnds)
                continue;

            int p = z + 1;
            while (codeString[p] == ' ' && p < codeString.size())
                ++p;

            auto start = p;
            auto end = string::npos;
            auto sub = codeString.find("subroutine", p);
            auto func = codeString.find("function", p);
            auto end_sub = codeString.find("end subroutine", p);
            auto end_func = codeString.find("end function", p);

            if (sub != end && sub == start || end_sub != end && end_sub == start ||
                func != end && func == start || end_func != end && end_func == start)
            {
                retVal += "  ";
            }
            else
                retVal += tab;
        }
    }

    return retVal;
}

void insertInterface(SgStatement* func, const FuncInfo *callFrom)
{
    const string& iface = getInterfaceBlock(callFrom->funcPointer->GetOriginal(), callFrom->funcParams);
    const string& fName = callFrom->funcName;

    string oldFile = current_file->filename();
    if (!func->switchToFile())
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    SgStatement* st = func->lexNext();
    SgStatement* last = func->lastNodeOfStmt();
    while (st != last)
    {
        if (st->variant() == VAR_DECL || st->variant() == VAR_DECL_90)
        {
            bool empty = filterFromList(st, { fName }, true);
            if (empty)
            {
                SgStatement* next = st->lexNext();
                st->extractStmt();
                st = next;
                continue;
            }
        }

        if (isSgExecutableStatement(st))
            break;
        st = st->lexNext();
    }
    SgStatement* ifaceBlock = new SgStatement(INTERFACE_STMT);
    addControlEndToStmt(ifaceBlock->thebif);

    ifaceBlock->setlineNumber(getNextNegativeLineNumber());
    ifaceBlock->setFileName(st->fileName());
    st->insertStmtBefore(*ifaceBlock, *st->controlParent());
    ifaceBlock->lastNodeOfStmt()->addComment(iface.c_str());

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

static void createInterfaceBlockForToCalls(FuncInfo* func)
{
    for (auto& callTo : func->callsTo)
    {
        if (callTo->interfaceBlocks.find(func->funcName) == callTo->interfaceBlocks.end())
        {
            callTo->interfaceBlocks[func->funcName] = func;
            insertInterface(callTo->funcPointer, func);
        }
    }
}

static void transferModule(map<FuncInfo*, set<SgSymbol*>>& funcAddedVarsMods, set<FuncInfo*>& allForChange,
                           vector<SgSymbol*>& varsToTransfer, FuncInfo* curFunc, FuncInfo* precFunc, set<FuncInfo*>& funcForInterfaceAdd)
{
    SgStatement* st = curFunc->funcPointer->GetOriginal();
    map<SgSymbol*, set< SgSymbol*>> modVarsToAdd;
    if (curFunc != precFunc)
    {
        if (SgFile::switchToFile(curFunc->fileName) == -1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        set<string> useMod;
        map<string, vector<pair<SgSymbol*, SgSymbol*>>> modByUse;
        map<string, vector<pair<SgSymbol*, SgSymbol*>>> modByUseOnly;

        if (!allForChange.count(curFunc))
        {
            for (SgStatement* start = st; start != st->lastNodeOfStmt(); start = start->lexNext())
            {
                if (isSgExecutableStatement(start))
                    break;
                if (start->variant() == CONTAINS_STMT)
                    break;
                if (start != st && (start->variant() == PROC_HEDR || start->variant() == FUNC_HEDR))
                    break;
                fillUseStatement(start, useMod, modByUse, modByUseOnly);
            }
        }

        for (auto& callInfo : curFunc->callsFromDetailed)
        {
            auto& call = callInfo.pointerDetailCallsFrom;
            if (isSgFuncHedrStmt(precFunc->funcPointer->GetOriginal()) && call.second == FUNC_CALL)
            {
                SgFunctionCallExp* callExp = (SgFunctionCallExp*)call.first;
                if (callExp->funName()->identifier() == precFunc->funcName)
                {
                    for (auto& var : varsToTransfer)
                    {
                        if (allForChange.count(curFunc))
                            callExp->addArg(*new SgVarRefExp(*var));
                        else
                        {
                            string name = makeName(var, modVarsToAdd, useMod, modByUse, modByUseOnly);
                            SgSymbol* s = new SgSymbol(VARIABLE_NAME, name.c_str());
                            callExp->addArg(*new SgVarRefExp(*s));
                        }
                    }
                }
            }
            else if (isSgProcHedrStmt(precFunc->funcPointer->GetOriginal()) && call.second == PROC_STAT)
            {
                SgCallStmt* callSt = (SgCallStmt*)call.first;
                if (callSt->name()->identifier() == precFunc->funcName)
                {
                    for (auto& var : varsToTransfer)
                    {
                        if (allForChange.count(curFunc))
                            callSt->addArg(*new SgVarRefExp(*var));
                        else
                        {
                            string name = makeName(var, modVarsToAdd, useMod, modByUse, modByUseOnly);
                            SgSymbol* s = new SgSymbol(VARIABLE_NAME, name.c_str());
                            callSt->addArg(*new SgVarRefExp(*s));
                        }
                    }
                }
            }
        }
    }

    map<SgSymbol*, set< SgSymbol*>> modVarsNeedToAdd;
    vector <SgSymbol*> nextVarsToTransfer;
    if (!funcAddedVarsMods.count(curFunc))
        funcAddedVarsMods[curFunc] = set<SgSymbol*>();

    for (auto& var : varsToTransfer)
    {
        if (!funcAddedVarsMods[curFunc].count(OriginalSymbol(var)))
        {
            SgSymbol* modS = OriginalSymbol(var)->scope()->symbol();
            if (modVarsToAdd.count(modS) && modVarsToAdd[modS].count(OriginalSymbol(var)))
            {
                if (!modVarsNeedToAdd.count(modS))
                    modVarsNeedToAdd[modS] = set< SgSymbol*>();
                modVarsNeedToAdd[modS].insert(OriginalSymbol(var));
            }
            nextVarsToTransfer.push_back(var);
            funcAddedVarsMods[curFunc].insert(OriginalSymbol(var));
        }
    }

    vector<SgStatement*> ifaces;
    ifaces.push_back(curFunc->funcPointer->GetOriginal());
    findInterfaces(curFunc, ifaces);

    if (allForChange.count(curFunc))
    {
        for (int i = 0; i < ifaces.size(); i++)
        {
            SgStatement* hedr = ifaces[i];
            for (auto& var : nextVarsToTransfer)
            {
                if (i == 0)
                    curFunc->funcParams.identificators.push_back(var->identifier());

                ((SgProcHedrStmt*)(hedr))->AddArg(*new SgVarRefExp(var->copy()));
                hedr->lexNext()->deleteStmt();
                vector<SgSymbol*> varVec = vector<SgSymbol*>();
                varVec.push_back(var);
                SgStatement* decl = makeDeclaration(NULL, varVec);

                /*TODO:: add some other*/
                if (var->attributes() && ALLOCATABLE_BIT)
                {
                    bool isAuto = false;
                    if (decl->expr(0)->lhs()->variant() == ARRAY_REF && decl->expr(0)->lhs()->lhs())
                    {
                        for (SgExpression* e = decl->expr(0)->lhs()->lhs(); e; e = e->rhs())
                        {
                            if (e->lhs()->variant() == DDOT && (e->lhs()->rhs() || e->lhs()->lhs()) ||
                                e->lhs()->variant() != DDOT)
                            {
                                isAuto = true;
                                break;
                            }
                        }
                    }

                    if (!isAuto)
                    {
                        funcForInterfaceAdd.insert(curFunc);
                        SgAttributeExp* a = new SgAttributeExp(ALLOCATABLE_OP);
                        SgExprListExp* l = new SgExprListExp();
                        l->setLhs(a);
                        ((SgVarDeclStmt*)decl)->addAttributeExpression(a);
                    }
                }

                for (int i = 0; i < 3; i++)
                {
                    SgExpression* e;
                    if (e = decl->expr(i))
                        decl->setExpression(i, CalculateInteger(ReplaceConstant(e)));
                }

                SgStatement* firstExDec = NULL;
                for (SgStatement* start = hedr->lexNext(), *end = hedr->lastNodeOfStmt(); start != end; start = start->lexNext())
                {
                    if ((isSgExecutableStatement(start) || isSgDeclarationStatement(start)) &&
                        !strcmp(hedr->fileName(), start->fileName()))
                    {
                        firstExDec = start;
                        break;
                    }
                }
                checkNull(firstExDec, convertFileName(__FILE__).c_str(), __LINE__);

                decl->setlineNumber(firstExDec->lineNumber());
                decl->setFileName(hedr->fileName());
                firstExDec->insertStmtBefore(*decl, *(hedr));

            }
        }

        for (auto& callFunc : curFunc->callsTo)
            transferModule(funcAddedVarsMods, allForChange, nextVarsToTransfer, callFunc, curFunc, funcForInterfaceAdd);
    }
    else
    {
        for (auto& mod : modVarsNeedToAdd)
        {
            SgSymbol modS = mod.first->copy();
            SgExpression* onlyE = new SgExpression(ONLY_NODE);
            SgExprListExp* renameL = NULL;
            for (auto& var : mod.second)
            {
                string name = "m_" + string(modS.identifier()) + "_" + var->identifier();
                SgSymbol varS = SgSymbol(VARIABLE_NAME, name.c_str());
                SgExprListExp* el = new SgExprListExp();
                SgVarRefExp* nvr = new SgVarRefExp(varS), * vr = new SgVarRefExp(var);
                SgExpression* renameE = new SgExpression(RENAME_NODE, nvr, vr);
                el->setLhs(renameE);
                el->setRhs(renameL);
                renameL = el;
            }

            onlyE->setLhs(renameL);
            SgStatement* useSt = new SgStatement(USE_STMT);
            useSt->setSymbol(modS);
            useSt->setExpression(0, onlyE);
            st->insertStmtAfter(*useSt, *st);
        }
    }
}

static void fillUsedVars(set<SgSymbol*>& usedVars, SgExpression* exp)
{
    if (exp)
    {
        if (exp->variant() == VAR_REF || exp->variant() == ARRAY_REF)
            usedVars.insert(exp->symbol());
        fillUsedVars(usedVars, exp->lhs());
        fillUsedVars(usedVars, exp->rhs());
    }
}

void moduleTransfer(const map<string, vector<FuncInfo*>>& allFuncInfo)
{
    FuncInfo* start = NULL;
    set<FuncInfo*> allForChange;
    map<FuncInfo*, set<SgSymbol*>> funcAddedVarsMods;
    set<FuncInfo*> funcForInterfaceAdd;
    for (auto& byfile : allFuncInfo)
    {
        for (auto& func : byfile.second)
            if (func->isMain)
                start = func;
    }

    collectForChange(allForChange, start);
    allForChange.erase(start);
    for (auto& func : allForChange)
    {
        if (SgFile::switchToFile(func->fileName) == -1)
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        SgStatement* st = func->funcPointer->GetOriginal();
        vector <SgSymbol*> varsToTransfer;
        map <string, string> locVars;
        SgStatement* lst = st->lastNodeOfStmt();
        SgSymbol* s, * sl;
        bool allSave = false;
        set< SgSymbol*> usedVars;

        for (SgStatement* start = st->lexNext(), *end = st->lastNodeOfStmt(); start != end;)
        {
            if (start->variant() == CONTAINS_STMT)
                break;

            SgStatement* next = start->lexNext();
            if (start->variant() == USE_STMT)
            {
                SgExpression* onlyE = start->expr(0);
                if (onlyE)
                {
                    SgExprListExp* renameL = NULL;
                    for (SgExpression* ex = onlyE->lhs(); ex; ex = ex->rhs())
                    {
                        if (ex->lhs()->variant() == RENAME_NODE)
                        {
                            SgSymbol* left = NULL, *right = NULL;
                            if (ex->lhs()->lhs()->symbol())
                                left = ex->lhs()->lhs()->symbol();
                            if (ex->lhs()->rhs() && ex->lhs()->rhs()->symbol())
                                right = ex->lhs()->rhs()->symbol();
                            if (!(right && (right->variant() == VARIABLE_NAME) || left && (left->variant() == VARIABLE_NAME)))
                            {
                                SgExprListExp* el = new SgExprListExp();
                                el->setLhs(ex->lhs());
                                el->setRhs(renameL);
                                renameL = el;
                            }
                        }
                    }

                    if (renameL)
                        onlyE->setLhs(renameL);
                    else
                        start->deleteStmt();
                }
            }
            if (isSgExecutableStatement(start)|| isSgDeclarationStatement(start))
                for (int i = 0; i < 3; i++)
                    fillUsedVars(usedVars, start->expr(i));

          //  if (start->variant() == IMPL_DECL)
            //    start->deleteStmt();
            start = next;
        }

        sl = lst->lexNext() ? lst->lexNext()->symbol() : NULL;
        for (s = st->symbol(); s != sl && s != NULL; s = s->next())
        {
            if (OriginalSymbol(s)->scope()->variant() == MODULE_STMT && (s->variant() == VARIABLE_NAME) && usedVars.count(s))
            {
                string newName = "m_" + string(OriginalSymbol(s)->scope()->symbol()->identifier()) + "_" + OriginalSymbol(s)->identifier();
                locVars[s->identifier()] = newName;
                s->changeName(newName.c_str());
                varsToTransfer.push_back(s);
            }
        }
        transferModule(funcAddedVarsMods, allForChange, varsToTransfer, func, func, funcForInterfaceAdd);
    }

    for (auto& func : funcForInterfaceAdd)
        createInterfaceBlockForToCalls(func);
}

static bool isIntrincis(const string& name)
{
    static set<string> intrinsicF;
    if (intrinsicF.size() == 0)
    {
        intrinsicF.insert(string("abs"));
        intrinsicF.insert(string("adjustl"));
        intrinsicF.insert(string("and"));
        intrinsicF.insert(string("any"));
        intrinsicF.insert(string("associated"));
        intrinsicF.insert(string("allocated"));
        intrinsicF.insert(string("amod"));
        intrinsicF.insert(string("aimax0"));
        intrinsicF.insert(string("ajmax0"));
        intrinsicF.insert(string("akmax0"));
        intrinsicF.insert(string("aimin0"));
        intrinsicF.insert(string("ajmin0"));
        intrinsicF.insert(string("akmin0"));
        intrinsicF.insert(string("amax1"));
        intrinsicF.insert(string("amax0"));
        intrinsicF.insert(string("amin1"));
        intrinsicF.insert(string("amin0"));
        intrinsicF.insert(string("aimag"));
        intrinsicF.insert(string("alog"));
        intrinsicF.insert(string("alog10"));
        intrinsicF.insert(string("asin"));
        intrinsicF.insert(string("asind"));
        intrinsicF.insert(string("asinh"));
        intrinsicF.insert(string("acos"));
        intrinsicF.insert(string("acosd"));
        intrinsicF.insert(string("acosh"));
        intrinsicF.insert(string("atan"));
        intrinsicF.insert(string("atand"));
        intrinsicF.insert(string("atanh"));
        intrinsicF.insert(string("atan2"));
        intrinsicF.insert(string("atan2d"));
        intrinsicF.insert(string("aint"));
        intrinsicF.insert(string("anint"));
        intrinsicF.insert(string("achar"));
        intrinsicF.insert(string("babs"));
        intrinsicF.insert(string("bbits"));
        intrinsicF.insert(string("bbset"));
        intrinsicF.insert(string("bdim"));
        intrinsicF.insert(string("biand"));
        intrinsicF.insert(string("bieor"));
        intrinsicF.insert(string("bior"));
        intrinsicF.insert(string("bixor"));
        intrinsicF.insert(string("btest"));
        intrinsicF.insert(string("bbtest"));
        intrinsicF.insert(string("bbclr"));
        intrinsicF.insert(string("bitest"));
        intrinsicF.insert(string("bjtest"));
        intrinsicF.insert(string("bktest"));
        intrinsicF.insert(string("bessel_j0"));
        intrinsicF.insert(string("bessel_j1"));
        intrinsicF.insert(string("bessel_jn"));
        intrinsicF.insert(string("bessel_y0"));
        intrinsicF.insert(string("bessel_y1"));
        intrinsicF.insert(string("bessel_yn"));
        intrinsicF.insert(string("bmod"));
        intrinsicF.insert(string("bnot"));
        intrinsicF.insert(string("bshft"));
        intrinsicF.insert(string("bshftc"));
        intrinsicF.insert(string("bsign"));
        intrinsicF.insert(string("cos"));
        intrinsicF.insert(string("ccos"));
        intrinsicF.insert(string("cdcos"));
        intrinsicF.insert(string("cosd"));
        intrinsicF.insert(string("cosh"));
        intrinsicF.insert(string("cotan"));
        intrinsicF.insert(string("cotand"));
        intrinsicF.insert(string("ceiling"));
        intrinsicF.insert(string("cexp"));
        intrinsicF.insert(string("conjg"));
        intrinsicF.insert(string("csqrt"));
        intrinsicF.insert(string("clog"));
        intrinsicF.insert(string("clog10"));
        intrinsicF.insert(string("cdlog"));
        intrinsicF.insert(string("cdlog10"));
        intrinsicF.insert(string("csin"));
        intrinsicF.insert(string("cabs"));
        intrinsicF.insert(string("cdabs"));
        intrinsicF.insert(string("cdexp"));
        intrinsicF.insert(string("cdsin"));
        intrinsicF.insert(string("cdsqrt"));
        intrinsicF.insert(string("cdtan"));
        intrinsicF.insert(string("cmplx"));
        intrinsicF.insert(string("char"));
        intrinsicF.insert(string("ctan"));
        intrinsicF.insert(string("cpu_time"));
        intrinsicF.insert(string("dim"));
        intrinsicF.insert(string("ddim"));
        intrinsicF.insert(string("dble"));
        intrinsicF.insert(string("dfloat"));
        intrinsicF.insert(string("dfloti"));
        intrinsicF.insert(string("dflotj"));
        intrinsicF.insert(string("dflotk"));
        intrinsicF.insert(string("dint"));
        intrinsicF.insert(string("dmax1"));
        intrinsicF.insert(string("dmin1"));
        intrinsicF.insert(string("dmod"));
        intrinsicF.insert(string("dprod"));
        intrinsicF.insert(string("dreal"));
        intrinsicF.insert(string("dsign"));
        intrinsicF.insert(string("dshiftl"));
        intrinsicF.insert(string("dshiftr"));
        intrinsicF.insert(string("dabs"));
        intrinsicF.insert(string("dsqrt"));
        intrinsicF.insert(string("dexp"));
        intrinsicF.insert(string("dlog"));
        intrinsicF.insert(string("dlog10"));
        intrinsicF.insert(string("dsin"));
        intrinsicF.insert(string("dcos"));
        intrinsicF.insert(string("dcosd"));
        intrinsicF.insert(string("dtan"));
        intrinsicF.insert(string("dtand"));
        intrinsicF.insert(string("dasin"));
        intrinsicF.insert(string("dasind"));
        intrinsicF.insert(string("dasinh"));
        intrinsicF.insert(string("dacos"));
        intrinsicF.insert(string("dacosd"));
        intrinsicF.insert(string("dacosh"));
        intrinsicF.insert(string("datan"));
        intrinsicF.insert(string("datand"));
        intrinsicF.insert(string("datanh"));
        intrinsicF.insert(string("datan2"));
        intrinsicF.insert(string("datan2d"));
        intrinsicF.insert(string("derf"));
        intrinsicF.insert(string("derfc"));
        intrinsicF.insert(string("dsind"));
        intrinsicF.insert(string("dsinh"));
        intrinsicF.insert(string("dcosh"));
        intrinsicF.insert(string("dcotan"));
        intrinsicF.insert(string("dcotand"));
        intrinsicF.insert(string("dtanh"));
        intrinsicF.insert(string("dnint"));
        intrinsicF.insert(string("dcmplx"));
        intrinsicF.insert(string("dconjg"));
        intrinsicF.insert(string("dimag"));
        intrinsicF.insert(string("exp"));
        intrinsicF.insert(string("erf"));
        intrinsicF.insert(string("erfc"));
        intrinsicF.insert(string("erfc_scaled"));
        intrinsicF.insert(string("etime"));
        intrinsicF.insert(string("float"));
        intrinsicF.insert(string("floati"));
        intrinsicF.insert(string("floatj"));
        intrinsicF.insert(string("floatk"));
        intrinsicF.insert(string("floor"));
        intrinsicF.insert(string("flush"));
        intrinsicF.insert(string("gamma"));
        intrinsicF.insert(string("habs"));
        intrinsicF.insert(string("hbclr"));
        intrinsicF.insert(string("hbits"));
        intrinsicF.insert(string("hbset"));
        intrinsicF.insert(string("hdim"));
        intrinsicF.insert(string("hiand"));
        intrinsicF.insert(string("hieor"));
        intrinsicF.insert(string("hior"));
        intrinsicF.insert(string("hixor"));
        intrinsicF.insert(string("hmod"));
        intrinsicF.insert(string("hnot"));
        intrinsicF.insert(string("hshft"));
        intrinsicF.insert(string("hshftc"));
        intrinsicF.insert(string("hsign"));
        intrinsicF.insert(string("htest"));
        intrinsicF.insert(string("huge"));
        intrinsicF.insert(string("hypot"));
        intrinsicF.insert(string("iiabs"));
        intrinsicF.insert(string("iargc"));
        intrinsicF.insert(string("iiand"));
        intrinsicF.insert(string("iibclr"));
        intrinsicF.insert(string("iibits"));
        intrinsicF.insert(string("iibset"));
        intrinsicF.insert(string("iidim"));
        intrinsicF.insert(string("iieor"));
        intrinsicF.insert(string("iior"));
        intrinsicF.insert(string("iishft"));
        intrinsicF.insert(string("iishftc"));
        intrinsicF.insert(string("iisign"));
        intrinsicF.insert(string("iixor"));
        intrinsicF.insert(string("int"));
        intrinsicF.insert(string("idint"));
        intrinsicF.insert(string("ifix"));
        intrinsicF.insert(string("idim"));
        intrinsicF.insert(string("isign"));
        intrinsicF.insert(string("index"));
        intrinsicF.insert(string("iabs"));
        intrinsicF.insert(string("ibits"));
        intrinsicF.insert(string("idnint"));
        intrinsicF.insert(string("ichar"));
        intrinsicF.insert(string("iachar"));
        intrinsicF.insert(string("isnan"));
        intrinsicF.insert(string("iand"));
        intrinsicF.insert(string("ior"));
        intrinsicF.insert(string("ibset"));
        intrinsicF.insert(string("ibclr"));
        intrinsicF.insert(string("ibchng"));
        intrinsicF.insert(string("ieor"));
        intrinsicF.insert(string("ilen"));
        intrinsicF.insert(string("imag"));
        intrinsicF.insert(string("imax0"));
        intrinsicF.insert(string("imax1"));
        intrinsicF.insert(string("imin0"));
        intrinsicF.insert(string("imin1"));
        intrinsicF.insert(string("imod"));
        intrinsicF.insert(string("inot"));
        intrinsicF.insert(string("isha"));
        intrinsicF.insert(string("ishc"));
        intrinsicF.insert(string("ishft"));
        intrinsicF.insert(string("ishftc"));
        intrinsicF.insert(string("ishl"));
        intrinsicF.insert(string("ixor"));
        intrinsicF.insert(string("jiabs"));
        intrinsicF.insert(string("jiand"));
        intrinsicF.insert(string("jibclr"));
        intrinsicF.insert(string("jibits"));
        intrinsicF.insert(string("jibset"));
        intrinsicF.insert(string("jidim"));
        intrinsicF.insert(string("jieor"));
        intrinsicF.insert(string("jior"));
        intrinsicF.insert(string("jishft"));
        intrinsicF.insert(string("jishftc"));
        intrinsicF.insert(string("jisign"));
        intrinsicF.insert(string("jixor"));
        intrinsicF.insert(string("jmax0"));
        intrinsicF.insert(string("jmax1"));
        intrinsicF.insert(string("jmin0"));
        intrinsicF.insert(string("jmin1"));
        intrinsicF.insert(string("jmod"));
        intrinsicF.insert(string("jnot"));
        intrinsicF.insert(string("kiabs"));
        intrinsicF.insert(string("kiand"));
        intrinsicF.insert(string("kibclr"));
        intrinsicF.insert(string("kibits"));
        intrinsicF.insert(string("kibset"));
        intrinsicF.insert(string("kidim"));
        intrinsicF.insert(string("kieor"));
        intrinsicF.insert(string("kior"));
        intrinsicF.insert(string("kishft"));
        intrinsicF.insert(string("kishftc"));
        intrinsicF.insert(string("kisign"));
        intrinsicF.insert(string("kmax0"));
        intrinsicF.insert(string("kmax1"));
        intrinsicF.insert(string("kmin0"));
        intrinsicF.insert(string("kmin1"));
        intrinsicF.insert(string("kmod"));
        intrinsicF.insert(string("knot"));
        intrinsicF.insert(string("len"));
        intrinsicF.insert(string("len_trim"));
        intrinsicF.insert(string("lge"));
        intrinsicF.insert(string("lgt"));
        intrinsicF.insert(string("lle"));
        intrinsicF.insert(string("llt"));
        intrinsicF.insert(string("log_gamma"));
        intrinsicF.insert(string("log"));
        intrinsicF.insert(string("log10"));
        intrinsicF.insert(string("lshft"));
        intrinsicF.insert(string("lshift"));
        intrinsicF.insert(string("max"));
        intrinsicF.insert(string("max0"));
        intrinsicF.insert(string("max1"));
        intrinsicF.insert(string("merge_bits"));
        intrinsicF.insert(string("min"));
        intrinsicF.insert(string("minval"));
        intrinsicF.insert(string("maxval"));
        intrinsicF.insert(string("min0"));
        intrinsicF.insert(string("min1"));
        intrinsicF.insert(string("mod"));
        intrinsicF.insert(string("modulo"));
        intrinsicF.insert(string("not"));
        intrinsicF.insert(string("nint"));
        intrinsicF.insert(string("null"));
        intrinsicF.insert(string("or"));
        intrinsicF.insert(string("popcnt"));
        intrinsicF.insert(string("poppar"));
        intrinsicF.insert(string("random_number"));
        intrinsicF.insert(string("real"));
        intrinsicF.insert(string("reshape"));
        intrinsicF.insert(string("present"));
        intrinsicF.insert(string("repeat"));
        intrinsicF.insert(string("rshft"));
        intrinsicF.insert(string("rshift"));
        intrinsicF.insert(string("sign"));
        intrinsicF.insert(string("size"));
        intrinsicF.insert(string("scan"));
        intrinsicF.insert(string("sizeof"));
        intrinsicF.insert(string("sngl"));
        intrinsicF.insert(string("sqrt"));
        intrinsicF.insert(string("sin"));
        intrinsicF.insert(string("sind"));
        intrinsicF.insert(string("sinh"));
        intrinsicF.insert(string("shifta"));
        intrinsicF.insert(string("shiftl"));
        intrinsicF.insert(string("shiftr"));
        intrinsicF.insert(string("sum"));
        intrinsicF.insert(string("tan"));
        intrinsicF.insert(string("tand"));
        intrinsicF.insert(string("tanh"));
        intrinsicF.insert(string("tiny"));
        intrinsicF.insert(string("trailz"));
        intrinsicF.insert(string("trim"));
        intrinsicF.insert(string("xor"));
        intrinsicF.insert(string("zabs"));
        intrinsicF.insert(string("zcos"));
        intrinsicF.insert(string("zexp"));
        intrinsicF.insert(string("zlog"));
        intrinsicF.insert(string("zsin"));
        intrinsicF.insert(string("zsqrt"));
        intrinsicF.insert(string("ztan"));
    }

    if (intrinsicF.find(name) != intrinsicF.end())
        return true;
    return false;
}