#include "leak_detector.h"

#include "dvm.h"
#include "acc_analyzer.h"
#include "expr_transform.h"
#include <stack>
#include <chrono>

#define PRINT_PROF_INFO 0
using std::string;
using std::vector;
using std::map;
using std::list;
using std::make_pair;
using std::set;
using std::pair;
using std::stack;
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::milliseconds;

void showDefs(set<ExpressionValue*> *defs);
void showDefs(map<SymbolKey, ExpressionValue*> *defs);
void showDefs(map<SymbolKey, set<ExpressionValue*>> *defs);
void showDefs(map<SymbolKey, SgExpression*> *defs);
static void showDefs(map <SymbolKey, set<SgExpression*>> *defs);

static CommonVarsOverseer *overseerPtr = NULL;
//static map<string, ExpressionValue*> allocated;
static map<int, set<ExpressionValue*>> allocated2;

static const char* unknownValueChars = "unknownValue";

static set<ExpressionValue> usedVariablesInStatement(SgStatement *st, set<string> &privates) {
    stack<SgExpression*> toCheck;
    set<ExpressionValue> result;

    for(int i=0;i<3;++i)
        if(st->expr(i))
            toCheck.push(st->expr(i));

    while(!toCheck.empty()) {
        SgExpression* top = toCheck.top();

        toCheck.pop();

        if(top->variant() == VAR_REF)
            result.insert(ExpressionValue(top, top->unparse()));
        if(top->variant() == ARRAY_REF) {
            if(privates.find(top->symbol()->identifier()) != privates.end())
                result.insert(ExpressionValue(top, top->unparse()));
        }

        if (top->rhs())
            toCheck.push(top->rhs());
        if (top->lhs())
            toCheck.push(top->lhs());
    }
    return result;

}

static void addDefinitionReachesStatement(map<SgStatement*, pair<set<SgStatement*>, set<SgStatement*>>>& result, SgStatement* definition, SgStatement* reachesHere)
{
    //this depends on
    auto found_r = result.find(reachesHere);
    if (found_r == result.end())
    {
        pair<set<SgStatement*>, set<SgStatement*>> newPair;
        newPair.first = set<SgStatement*>();
        newPair.second = set<SgStatement*>();
        newPair.first.insert(definition);
        result.insert(found_r, make_pair(reachesHere, newPair));
    }
    else
        found_r->second.first.insert(definition);

    //this used in
    auto found_d = result.find(definition);
    if(found_d == result.end())
    {
        pair<set<SgStatement*>, set<SgStatement*>> newPair;
        newPair.first = set<SgStatement*>();
        newPair.second = set<SgStatement*>();
        newPair.second.insert(reachesHere);
        result.insert(found_d, make_pair(definition, newPair));
    }
    else
        found_d->second.second.insert(reachesHere);
}

static bool checkSymbolUsedByProcsAndFuncs(SgStatement *st, const ExpressionValue &symbol, map<SymbolKey, set<ExpressionValue>> &arraysAssingments, vector <ControlFlowItem*> &cfis) {
    if(cfis.size() == 0)
        findCFIsForStmt(st, cfis);

    for(ControlFlowItem *cfi : cfis) {

        AnalysedCallsList *callData = cfi->getCall();
        SgFunctionCallExp *funcCall = NULL;
        if (((st = cfi->getStatement()) != NULL) && (st->variant() == PROC_STAT))
        {
            SgCallStmt *callStmt = isSgCallStmt(st);
            for (int i = 0; i < callStmt->numberOfArgs(); ++i)
            {
                SgExpression *arg = callStmt->arg(i);
                if ((arg->variant() == VAR_REF /*|| arg->variant() == ARRAY_REF*/) && argIsUsed(i, callData)) {
                    if(strcmp(symbol.getExp()->symbol()->identifier(), arg->symbol()->identifier())) {
//                        printf("in %d ", st->lineNumber());
//                        printf(" %s is required\n", arg->unparse());
                        return true;
                    }
                }
            }
        }
        else if ((funcCall = cfi->getFunctionCall()) != NULL)
        {
            for (int i = 0; i < funcCall->numberOfArgs(); ++i)
            {
                SgExpression *arg = funcCall->arg(i);
                if ((arg->variant() == VAR_REF /*|| arg->variant() == ARRAY_REF*/) && argIsUsed(i, callData)) {
                    if(strcmp(symbol.getExp()->symbol()->identifier(), arg->symbol()->identifier())) {
//                        printf("in %d ", st->lineNumber());
//                        printf(" %s is required\n", arg->unparse());
                        return true;
                    }
                }
            }
        }
    }

    return false;
}

/**
 * symbol упоминается в st. Нужно проверить, что значение symbol действительно важно в st.
 * Например для оператора x = 7; значение x не важно, а для x = x + 2 - важно
 * symbol - это ExpressionValue из SgExpression у которого есть symbol()
 */
static bool symbolIsUsed(SgStatement *st, const ExpressionValue &symbol, map<SymbolKey, set<ExpressionValue>> &arraysAssingments, vector <ControlFlowItem*> &cfis) {
    stack<SgExpression*> toCheck;

    if(checkSymbolUsedByProcsAndFuncs(st, symbol, arraysAssingments, cfis)) {
//        if(st->lineNumber() == 345/* || st->lineNumber() == 301*/)
//            printf("from func true\n");
        return true;
    }



    SgExpression *lval = st->expr(0);
    if(lval)
    {
        if(st->variant() == ASSIGN_STAT)
        {
            if(lval->rhs())
                toCheck.push(lval->rhs());
            if(lval->lhs())
                toCheck.push(lval->lhs());
        }
        else
            toCheck.push(lval);
    }

    for(int i=1;i<3;++i)
        if(st->expr(i))
            toCheck.push(st->expr(i));

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

        if(top->variant() == VAR_REF)
            if(symbol.getUnparsed() == top->symbol()->identifier()) {
//                if(st->lineNumber() == 345 || st->lineNumber() == 301)
//                    printf("%s == %s is true\n", symbol.getUnparsed().c_str(), top->symbol()->identifier());
                return true;
            }

        //TODO arraysAssignments
        /*
        if(top->variant() == ARRAY_REF) {

            if(symbol.getUnparsed() == top->unparse())
                return true;
        }*/

        //Не лезть проверять индексы массивов
        if(top->variant() != ARRAY_REF) {
            SgExpression *rhs = top->rhs();
            SgExpression *lhs = top->lhs();

            if (rhs && rhs->variant() != FUNC_CALL)
                toCheck.push(rhs);
            if (lhs && lhs->variant() != FUNC_CALL)
                toCheck.push(lhs);
        }
    }

    return false;
}

static void updateArraysAssingments(map<SymbolKey, set<ExpressionValue>> &arraysAssingments, map<SymbolKey, set<ExpressionValue*>> &definitions, SgStatement* cur)
{
    for(auto it : definitions)
        for(auto itt : it.second)
            if(itt->getUnparsed() == unknownValueChars)
            {
                auto founded = arraysAssingments.find(it.first);
                if(founded != arraysAssingments.end()) {
//                    for(auto fit : founded->second)
//                       delete fit;
                    arraysAssingments.erase(it.first);
                }
            }

    if(cur->variant() == ASSIGN_STAT)
    {
        SgExpression *var = cur->expr(0);
        if(var->variant() == ARRAY_REF)
        {
            auto founded = arraysAssingments.find(var->symbol());
            string unp = var->unparse();
            ExpressionValue expVal = ExpressionValue(var, unp);
            if(founded == arraysAssingments.end())
                arraysAssingments.insert(founded, make_pair(var->symbol(), set<ExpressionValue>()))->second.insert(expVal  );
            else
                founded->second.insert(expVal);
        }
    }
}

/**
 * Построить мап с зависимостями:
 * <Оператор от Х : пара<сет зависящих от Х операторов : сет используюемых в Х операторов>>
 * с since включительно, по till не включительно
 */
map<SgStatement*, pair<set<SgStatement*>, set<SgStatement*>>> buildRequireReachMapForLoop(SgStatement *since, SgStatement *till, set<string> &privates) {

    int sinceLine = since->lineNumber();
    int tillLine = till->lineNumber();

    map<SgStatement*, pair<set<SgStatement*>, set<SgStatement*>>> result;
    map<SymbolKey, set<ExpressionValue>> arraysAssingments;

    for(SgStatement* cur = since; cur != till; cur = cur->lexNext())
    {
        auto definitions = getReachingDefinitionsExt(cur);
        updateArraysAssingments(arraysAssingments, definitions, cur);
        auto usedVariables = usedVariablesInStatement(cur, privates);
        vector <ControlFlowItem*> cfis;

/*
                printf("after %d:\n", cur->lineNumber());
                for(auto it : arraysAssingments)
                {
                    printf("    %s: ", it.first.getVarName().c_str());
                    for(auto itt : it.second)
                        printf("%s, ", itt.getUnparsed().c_str());
                    printf("\n");
                }

*/
        for(auto& var : usedVariables)
        {
/*            if(cur->lineNumber() == 127) {
                printf("checking %d for %s %s\n",cur->lineNumber(), var.getUnparsed().c_str(), cur->unparse());
           }*/
            if(symbolIsUsed(cur, var, arraysAssingments, cfis))
            {
/*                if(cur->lineNumber() == 127) {
                    printf("%s is used\n", var.getUnparsed().c_str());
               }*/


                auto expressions = definitions.find(var.getExp()->symbol());
                if (expressions != definitions.end())
                {
                    for (ExpressionValue* expValue : expressions->second)
                    {
                        SgStatement *def = expValue->getFrom();
                        if (def->lineNumber() >= sinceLine && def->lineNumber() <= tillLine && def->lineNumber() != cur->lineNumber()) {
                            addDefinitionReachesStatement(result, def, cur);
/*                            if(cur->lineNumber() == 127)
                                printf("connected to %s\n", def->unparse());*/
                        }
                    }
                }
            }
        }
    }
/*
    for(auto& it : result) {
        printf("st: %d:\n",it.first->lineNumber());
        printf("Depends on:\n");
        for(auto& itt : it.second.first)
            printf("%d ", itt->lineNumber());
        printf("\n");

        printf("Used in:\n");
        for(auto& itt : it.second.second)
            printf("%d ", itt->lineNumber());
        printf("\n");
        printf("\n");
    }*/


    return result;

}

bool symbolInExpression(const SymbolKey &symbol, SgExpression *exp)
{
    bool result = false;
    if (exp)
    {
        if (exp->variant() == VAR_REF)
            return (symbol == exp->symbol());

        if(exp->variant() == ARRAY_REF && symbol == exp->symbol())
            return true;

        bool hasSymbol = false;
        if (exp->rhs())
            hasSymbol |= symbolInExpression(symbol, exp->rhs());
        if (exp->lhs())
            hasSymbol |= symbolInExpression(symbol, exp->lhs());

        result |= hasSymbol;
    }
    return result;
}

ExpressionValue* allocateExpressionValue(SgExpression* newExp, SgStatement* from)
{
    string unp = (newExp == NULL) ? unknownValueChars : string(newExp->unparse());

    int file_id = from == NULL ? current_file_id : from->getFileId();
    auto allocatedMap = allocated2.find(file_id);
    if(allocatedMap == allocated2.end())
        allocatedMap = allocated2.insert(allocatedMap, make_pair(file_id, set<ExpressionValue*>()));

    for(ExpressionValue* expVal : allocatedMap->second)
        if(expVal->getUnparsed() == unp && expVal->getFrom() == from)
            return expVal;

    ExpressionValue* newExpVal = new ExpressionValue(newExp, unp, from);
    allocatedMap->second.insert(newExpVal);
    return newExpVal;

/*    auto alloc = allocatedMap->second.find(unp);
    if (alloc == allocatedMap->second.end())
        newExpVal = allocatedMap->second.insert(alloc, make_pair(unp, new ExpressionValue(newExp, unp, from)))->second;
    else
        newExpVal = alloc->second;
*/
/*    auto alloc = allocated.find(unp);
    if (alloc == allocated.end())
        newExpVal = allocated.insert(alloc, make_pair(unp, new ExpressionValue(newExp, unp, from)))->second;
    else
        newExpVal = alloc->second;
*/
//    return newExpVal;//new ExpressionValue(newExp, unp, from);
}

void CBasicBlock::addVarToGen(SymbolKey var, SgExpression *value, SgStatement *defSt)
{
    addVarToKill(var);
    ExpressionValue* expVal = allocateExpressionValue(value, defSt);
    e_gen.insert(expVal);
    gen.insert(make_pair(var, expVal));
//    saveDefinitionStatement(var, expVal, defSt);
}

void CBasicBlock::addVarUnknownToGen(SymbolKey var, SgStatement *defSt) {
    addVarToKill(var);
    ExpressionValue* expVal = allocateExpressionValue(NULL, defSt);

    gen.insert(make_pair(var, expVal));
//    saveDefinitionStatement(var, expVal, defSt);
}

void CBasicBlock::addVarToKill(const SymbolKey &key)
{
    kill.insert(key);

    for (auto it = gen.begin(); it != gen.end();)
        if (it->first == key)
            it = gen.erase(it);
        else
            ++it;

    for (auto it = e_gen.begin(); it != e_gen.end();)
        if (symbolInExpression(key, (*it)->getExp()))
            it = e_gen.erase(it);
        else
            ++it;
}

bool argIsUsed(int i, AnalysedCallsList *callData)
{
    // AnalysedCallsList == -1 or -2 if no user procedure/subroutine found
    if (callData == NULL || (AnalysedCallsList*)(-1) == callData || (AnalysedCallsList*)(-2) == callData)
        return true;

    int stored = SwitchFile(callData->file_id);
    SgProcHedrStmt *header = isSgProcHedrStmt(callData->header);
    SgSymbol *currPar = NULL;
    if (header)
        currPar = header->parameter(i);
    SwitchFile(stored);

    if (header == NULL)
        return false;
    if (header->parameter(i) == NULL)
        return false;

    int attr = currPar->attributes();
    bool isArgOut = callData->isArgOut(i, NULL);
    bool isArgIn = callData->isArgIn(i, NULL);
    if(((attr & (IN_BIT)) || (attr & (INOUT_BIT))) || isArgIn) //argument used in procedure
        return true;
    else
        return false;
}

bool argIsReplaceable(int i, AnalysedCallsList *callData)
{
    // AnalysedCallsList == -1 or -2 if no user procedure/subroutine found
    if (callData == NULL || (AnalysedCallsList*)(-1) == callData || (AnalysedCallsList*)(-2) == callData)
        return false;

    int stored = SwitchFile(callData->file_id);
    SgProcHedrStmt *header = isSgProcHedrStmt(callData->header);
    SgSymbol *currPar = NULL;
    if (header)
        currPar = header->parameter(i);
    SwitchFile(stored);

    if (header == NULL)
        return false;
    if (header->parameter(i) == NULL)
        return false;
    
    int attr = currPar->attributes();
    bool isArgOut = callData->isArgOut(i, NULL);
    bool isArgIn = callData->isArgIn(i, NULL);

    if ((attr & (OUT_BIT)) || (attr & (INOUT_BIT)) || isArgOut) //argument modified inside procedure
        return false;
    else if (!((attr & (IN_BIT)) || isArgIn)) // no information, assume that argument is "inout"
        return false;
    else
        return true;
}

void CBasicBlock::checkFuncAndProcCalls(ControlFlowItem *cfi)
{
    SgStatement *st = NULL;
    AnalysedCallsList *callData = cfi->getCall();
    SgFunctionCallExp *funcCall = NULL;
    set<SgSymbol*> *varsToKill = NULL;
    if (((st = cfi->getStatement()) != NULL) && (st->variant() == PROC_STAT))
    {
        SgCallStmt *callStmt = isSgCallStmt(st);
        for (int i = 0; i < callStmt->numberOfArgs(); ++i)
        {
            SgExpression *arg = callStmt->arg(i);
            if ((arg->variant() == VAR_REF || arg->variant() == ARRAY_REF) && (!argIsReplaceable(i, callData)))
                addVarUnknownToGen(arg->symbol(), st);
        }
        varsToKill = overseerPtr->killedVars(callStmt->symbol()->identifier());
    }
    else if ((funcCall = cfi->getFunctionCall()) != NULL) 
    {
        for (int i = 0; i < funcCall->numberOfArgs(); ++i) 
        {
            SgExpression *arg = funcCall->arg(i);
            if ((arg->variant() == VAR_REF || arg->variant() == ARRAY_REF) && (!argIsReplaceable(i, callData)))
                addVarUnknownToGen(arg->symbol(), cfi->getOriginalStatement());
        }
        varsToKill = overseerPtr->killedVars(funcCall->symbol()->identifier());
    }

    if (varsToKill)
        for (auto var : *varsToKill)
            addVarUnknownToGen(var, cfi->getStatement());
}

set<SymbolKey>* CBasicBlock::getOutVars()
{
    set<SymbolKey> *outVars = new set<SymbolKey>();    
    for (auto elem : out_defs)
        outVars->insert(elem.first);
    return outVars;
}

bool CBasicBlock::varIsPointer(SgSymbol *symbol)
{
    if (symbol == NULL)
        return false;

    auto found = parent->getPointers()->find(symbol);
    if(found != parent->getPointers()->end())
        return true;

    return false;
}

void CBasicBlock::processAssignThroughPointer(SgSymbol *symbol, SgExpression *right, SgStatement* st)
{
    //printf("processAssignThroughPointer REBUILD?!\n");
    auto found = gen.find(symbol);
    if (found != gen.end())
    {
        addVarToGen(found->second->getExp()->symbol(), right, st);
        return;
    }

    auto found_inDefs = in_defs.find(symbol);
    if (found_inDefs != in_defs.end())
    {
        for (auto& value : found_inDefs->second)
            addVarUnknownToGen(value->getExp()->symbol(), st);
    }
}

void CBasicBlock::processPointerAssignment(SgSymbol *symbol, SgExpression *right, SgStatement* st)
{
    //TODO: Record_Ref
    if (symbol == NULL)
        return;

    //right is a single VAR_REF
    SgSymbol *rSymbol = right->symbol();
    parent->getPointers()->insert(SymbolKey(symbol, true));
    if(varIsPointer(rSymbol))
    {
        auto found = gen.find(rSymbol);
        //auto found_inDefsP = in_defs_p.find(rSymbol);
        if(found != gen.end())
            addVarToGen(SymbolKey(symbol, true), found->second->getExp(), st);
        //else if(found_inDefsP != in_defs_p.end())
            //addVarToGenP(SymbolKey(symbol, true), found_inDefsP->second);
    }
    else
        addVarToGen(SymbolKey(symbol, true), right, st);
}

void CBasicBlock::adjustGenAndKillP(ControlFlowItem *cfi)
{
    SgStatement *st = cfi->getStatement();
    if (st != NULL)
    {
        SgExpression *left = st->expr(0);
        SgExpression *right = st->expr(1);

        if (st->variant() == POINTER_ASSIGN_STAT)
            processPointerAssignment(left->symbol(), right, st);
    }

    //checkFuncAndProcCalls(cfi);
}

void CBasicBlock::processReadStat(SgStatement *readSt)
{
    SgExpression *input = readSt->expr(0);
    std::stack<SgExpression*> toCheck;

    if (input)
        toCheck.push(input);

    while (!toCheck.empty())
    {
        SgExpression *exp = toCheck.top();
        toCheck.pop();
        if (exp->variant() == VAR_REF || exp->variant() == ARRAY_REF)
            addVarToGen(exp->symbol(), NULL, readSt);
            //addVarToKill(exp->symbol());
        else
        {
            if (exp->rhs())
                toCheck.push(exp->rhs());
            if (exp->lhs())
                toCheck.push(exp->lhs());
        }
    }
}

void CBasicBlock::adjustGenAndKill(ControlFlowItem *cfi)
{
    SgStatement *st = cfi->getStatement();
    if (st != NULL)
    {
        SgExpression *left = st->expr(0);
        SgExpression *right = st->expr(1);

        if (st->variant() == ASSIGN_STAT)
        {
            if (left->variant() == VAR_REF) // x = ...
            {
                if (varIsPointer(left->symbol()))
                    processAssignThroughPointer(left->symbol(), right, st);
                else
                    addVarToGen(left->symbol(), right, st);
            }
            else if (left->variant() == ARRAY_REF) // a(...) = ...
                addVarToGen(left->symbol(), right, st);
        }
        else if(st->variant() == READ_STAT)
            processReadStat(st);
        else if (st->variant() == POINTER_ASSIGN_STAT)
            processPointerAssignment(left->symbol(), right, st);
    }

    checkFuncAndProcCalls(cfi);
}

bool CBasicBlock::expressionIsAvailable(ExpressionValue* expValue) {

    for(auto it = e_gen.begin(); it != e_gen.end(); ++it)
        if(**it == *expValue)
            return true;

    for(auto it = e_in.begin(); it != e_in.end(); ++it)
        if(**it == *expValue)
        {
            bool killed = false;
            for(auto killIt = kill.begin(); killIt != kill.end(); ++killIt)
                if(symbolInExpression(*killIt, expValue->getExp()))
                {
                    killed = true;
                    break;
                }
            if(!killed)
                return true;
        }
    return false;
}

const map<SymbolKey, set<ExpressionValue*>> CBasicBlock::getReachedDefinitionsExt(SgStatement *stmt)
{
    ControlFlowItem *cfi = getStart();
    ControlFlowItem *till = getEnd()->getNext();
    clearGenKill();
    bool founded = false;
    while (cfi != till)
    {
        if (cfi->getStatement() == stmt || cfi->getOriginalStatement() == stmt)
        {
            founded = true;
            break;
        }
        adjustGenAndKill(cfi);
        cfi = cfi->getNext();
    }


    map<SymbolKey, set<ExpressionValue*>> defs;
    if (founded)
    {
        for (auto &it : in_defs)
        {
            if (kill.find(it.first) == kill.end())
            {
                auto founded = defs.find(it.first);
                if (founded == defs.end())
                    founded = defs.insert(founded, make_pair(it.first, set<ExpressionValue*>()));

                for (auto &exp : it.second)
                    founded->second.insert(exp);
            }
        }

        for(auto &it : gen)
            defs.insert(make_pair(it.first, set<ExpressionValue*>())).first->second.insert(it.second);
    }

/*
    for(auto it : defs) {
        printf("%s: ", it.first.getVarName().c_str());
        for(auto itt : it.second)
            printf("%s, ", itt->getUnparsed().c_str());
        printf("\n");
    }
*/
    return defs;
}

size_t max1 = 0;
size_t min1 = 0;
size_t max2 = 0;
size_t min2 = 0;
size_t countMerge = 0;

static bool mergeExpressionMaps(set<ExpressionValue*> &main, set<ExpressionValue*> &term)
{
#if PRINT_PROF_INFO
    ++countMerge;
    max1 = std::max(max1, main.size());
    max2 = std::max(max2, term.size());
    min1 = std::min(min1, main.size());
    min2 = std::min(min2, term.size());
#endif
    size_t mainSize = main.size();
    main.insert(term.begin(), term.end());
    if(main.size() != mainSize)
        return true;
    return false;
}

static void mergeDefs(map<SymbolKey, set<ExpressionValue*>> *main, map<SymbolKey, set<ExpressionValue*>> *term, set<SymbolKey> *allowedVars)
{
    for (auto &toCheck : *term)
    {
        if (!allowedVars || (allowedVars && allowedVars->find(toCheck.first) != allowedVars->end()))
        {
            auto founded = main->find(toCheck.first);
            if (founded == main->end())
                main->insert(founded, toCheck);
            else if (founded != main->end())
                mergeExpressionMaps(founded->second, toCheck.second);
        }
    }
}

void CBasicBlock::initializeOut()
{
    for (auto &elem : gen)
    {
        SgExpression *newExp = NULL;
        if (elem.second)
        {
            auto inserted = out_defs.find(elem.first);
            if (inserted == out_defs.end())
                inserted = out_defs.insert(inserted, make_pair(elem.first, set<ExpressionValue*>()));
            inserted->second.insert(elem.second);
        }
    }
}

void CBasicBlock::initializeEOut(set<ExpressionValue*>& allEDefs)
{
    e_in.insert(allEDefs.begin(), allEDefs.end()); //чтобы потом только уменьшать при пересечении
    if(getPrev() != NULL) //OUT[first] = empty
        e_out.insert(allEDefs.begin(), allEDefs.end());
}


bool CBasicBlock::updateEDefs()
{
    //IN == intersection of all prev OUT
    for (BasicBlockItem *prev = getPrev(); prev != NULL; prev = prev->next)
    {
        CBasicBlock *b = prev->block;
        for(auto it = e_in.begin(); it != e_in.end();)
            if(b->e_out.find(*it) == b->e_out.end())
                it = e_in.erase(it);
            else
                ++it;
    }
    int out_size = e_out.size();
    //OUT = e_gen обеъденить c (IN - kill)
    e_out.clear();
    e_out.insert(e_gen.begin(), e_gen.end());
    for(auto it = e_in.begin(); it != e_in.end(); ++it)
    {
        bool killed = false;
        for(auto killIt = kill.begin(); killIt != kill.end(); ++killIt)
            if(symbolInExpression(*killIt, (*it)->getExp()))
            {
                killed = true;
                break;
            }
        if(!killed)
            e_out.insert(*it);
    }

    if(out_size != e_out.size())
        return true;
    return false;
}

static bool addDefsFilteredByKill(map<SymbolKey, set<ExpressionValue*>> *main,
                                  map<SymbolKey, set<ExpressionValue*>> *defs, set<SymbolKey> *kill)
{
    bool mainChanged = false;
    for (auto it = defs->begin(); it != defs->end(); ++it)
    {
        SymbolKey key = it->first;
        if (kill->find(key) != kill->end())
            continue;

        auto founded = main->find(key);
        if (founded == main->end())
        {
            main->insert(make_pair(key, it->second));
            mainChanged = true;
        }
        else
            mainChanged |= mergeExpressionMaps(founded->second, it->second);
    }
    return mainChanged;
}

void showDefs(map<SymbolKey, set<ExpressionValue*>> *defs)
{
    printf("Defs: %d\n", (int)defs->size());
    for (auto it = defs->begin(); it != defs->end(); ++it)
    {
        if (it->first.isPointer())
            printf("--- %s => ", it->first.getVarName().c_str());
        else
            printf("--- %s = ", it->first.getVarName().c_str());
        for (auto iter = it->second.begin(); iter != it->second.end(); ++iter)
        {
            printf("%s", (*iter)->getUnparsed().c_str());
            if (iter != it->second.end())
                printf(", ");
        }
        printf("\n");
    }
    printf("\n");
}

void showDefs(set<ExpressionValue*> *defs)
{
    printf("Defs: %d\n", (int)defs->size());
    printf(" ");
    for (auto it = defs->begin(); it != defs->end(); ++it)
    {
        printf("%s, ", (*it)->getUnparsed().c_str());
    }
    printf("\n");
}

void showDefs(map<SymbolKey, ExpressionValue*> *defs)
{
    printf("Defs: %d\n", (int)defs->size());
    for (auto it = defs->begin(); it != defs->end(); ++it)
    {
        if(it->first.isPointer())
            printf("--- %s => %s", it->first.getVarName().c_str(), it->second->getUnparsed().c_str());
        else
            printf("--- %s = %s", it->first.getVarName().c_str(), it->second->getUnparsed().c_str());
        printf("\n");
    }
    printf("\n");
}

void showDefs(map<SymbolKey, SgExpression*> *defs)
{
    printf("Defs: %d\n", (int)defs->size());
    for (auto it = defs->begin(); it != defs->end(); ++it)
    {
        if(it->first.isPointer())
            printf("--- %s => %s", it->first.getVarName().c_str(), it->second == NULL ? "value is undefined" : it->second->unparse());
        else
            printf("--- %s = %s", it->first.getVarName().c_str(), it->second == NULL ? "value is undefined" : it->second->unparse());
        printf("\n");
    }
    printf("\n");
}

static void showDefs(map <SymbolKey, set<SgExpression*>> *defs)
{
    printf("Defs: %d\n", (int)defs->size());
    for(auto it = defs->begin(); it != defs->end(); ++it)
    {
        printf("--- %s = ", it->first.getVarName().c_str());
        for(auto itt = it->second.begin(); itt != it->second.end(); ++itt)
            printf("%s, ", (*itt)->unparse());
        printf("\n");
    }

    printf("\n");
}

void debugStructure(ControlFlowGraph *CGraph, const string &filename)
{
    CBasicBlock *b = CGraph->getFirst();
    printf("strict digraph {\n");
    while (b != NULL)
    {
        printf("\"%s_%d\" [shape = box, label = \"%s_%d\n", b->getProc()->funName, b->getNum(), b->getProc()->funName, b->getNum());

        bool printed = false;
        ControlFlowItem *cfi = b->getStart();
        ControlFlowItem *till = b->getEnd()->getNext();
        while (cfi != till)
        {
            if (cfi->getStatement())
            {
                printed = true;
                printf("%d ", cfi->getStatement()->lineNumber());
                printf("%s\n", cfi->getStatement()->unparse());
            }
            else if(cfi->getOriginalStatement())
            {
                SgStatement *origSt = cfi->getOriginalStatement();
                if(origSt->variant() == IF_NODE)
                {
                    printed = true;
                    printf("%d ", origSt->lineNumber());
                    printf("If:   (%s)\n", ((SgIfStmt*) origSt)->conditional()->unparse());
                }

            }
            cfi = cfi->getNext();
        }
        if (!printed)
        {
            SgStatement* origStmt = NULL;
            cfi = b->getStart();
            while (cfi != till)
            {
                if ((origStmt = cfi->getOriginalStatement()) != NULL)
                {
                    printed = true;
                    printf("Original: %s\n", origStmt->unparse());
                }
                cfi = cfi->getNext();
            }
        }

        printf("\"];\n");

        for (BasicBlockItem *prev = b->getPrev(); prev != NULL; prev = prev->next)
            printf("\"%s_%d\" -> \"%s_%d\";\n", prev->block->getProc()->funName, prev->block->getNum(), b->getProc()->funName, b->getNum());
        printf("\n");

        b = b->getLexNext();
    }
    printf("}\n");
}


void showDefsOfGraph(ControlFlowGraph *CGraph)
{
    CBasicBlock *b = CGraph->getFirst();
    while (b != NULL)
    {

        printf("\n\nBlock %d, prev: ", b->getNum());
        for (BasicBlockItem *prev = b->getPrev(); prev != NULL; prev = prev->next)
            printf("%d, ", prev->block->getNum());
        printf("\n");

        bool printed = false;
        ControlFlowItem *cfi = b->getStart();
        ControlFlowItem *till = b->getEnd()->getNext();
        while (cfi != till)
        {
            if (cfi->getStatement())
            {
                printed = true;
                printf("%d ", cfi->getStatement()->lineNumber());
                cfi->getStatement()->unparsestdout();
            }
            else if(cfi->getOriginalStatement())
            {
                SgStatement *origSt = cfi->getOriginalStatement();
                if(origSt->variant() == IF_NODE)
                {
                    printed = true;
                    printf("%d ", origSt->lineNumber());
                    printf("If:   (%s)\n", ((SgIfStmt*) origSt)->conditional()->unparse());
                }

            }
            cfi = cfi->getNext();
        }
        if (!printed)
        {
            SgStatement* origStmt = NULL;
            cfi = b->getStart();
            while (cfi != till)
            {
                if ((origStmt = cfi->getOriginalStatement()) != NULL)
                {
                    printed = true;
                    printf("Original: ");
                    origStmt->unparsestdout();
                }
                cfi = cfi->getNext();
            }
        }

        if (printed)
        {
            printf("\n In ");
            showDefs(b->getInDefs());
            printf("\n Gen");
            showDefs(b->getGen());
            printf("\n E Gen");
            showDefs(b->getEGen());
            printf("\n Kill %d\n ", (int)b->getKill()->size());
            for (auto it = b->getKill()->begin(); it != b->getKill()->end(); ++it)
                printf("%s ", it->getVarName().c_str());
            printf("\n");
            printf("\n Out ");
            showDefs(b->getOutDefs());
            printf("\n E In ");
            showDefs(b->getEIn());
            printf("\n E Out ");
            showDefs(b->getEOut());
        }
        b = b->getLexNext();
    }
}

void ClearCFGInsAndOutsDefs(ControlFlowGraph *CGraph)
{
    int64_t memCount = 0;
    int64_t elemCount = 0;
    int64_t elemCount1 = 0;
    CBasicBlock *b = CGraph->getFirst();
    while (b != NULL)
    {
#if PRINT_PROF_INFO
        auto gen = b->getGen();
        for (auto &elem : *gen)
        {
            memCount += elem.first.getVarName().size() + sizeof(SgExpression*);
            ++elemCount;
            elemCount1 += 2;
        }

        auto in_def = b->getInDefs();
        for (auto &elem : *in_def)
        {
            memCount += elem.first.getVarName().size() + elem.second.size() * sizeof(ExpressionValue*);
            ++elemCount;
            elemCount1 = elemCount1 + 1 + elem.second.size();
        }
        auto out_def = b->getOutDefs();
        for (auto &elem : *out_def)
        {
            memCount += elem.first.getVarName().size() + elem.second.size() * sizeof(ExpressionValue*);
            ++elemCount;
            elemCount1 = elemCount1 + 1 + elem.second.size();
        }
#endif
        b->clearGenKill();
        b->clearDefs();
        b = b->getLexNext();
    }

    /*int64_t countS = 0;
    for (auto &elem : allocated)
    {
#if PRINT_PROF_INFO
        countS += elem.second->getUnparsed().size();
#endif
        if (elem.second->getUnparsed() != "")
            delete elem.second;        
    }
#if PRINT_PROF_INFO
    if (allocated.size())
        __spf_print(1, "   count of elem %lld, in MB %f, info %lld in MB %f, elemCount = %d, elemCount1 = %d\n", 
                    countS, (double)countS / 1024. / 1024., memCount, double(memCount) / 1024./1024., elemCount, elemCount1);
#endif
*/
    //allocated.clear();
}

void deleteAllocatedExpressionValues(int file_id) {
    auto fa = allocated2.find(file_id);
    if(fa != allocated2.end())
        for(auto &elem : fa->second)
            delete elem;
//            delete elem.second;0
    allocated2.clear();
}

//TODO
//TODO
//TODO вернуть true если GEN изменился
bool BuildGenKillAndUpdateOutP(ControlFlowGraph* CGraph)
{
    CBasicBlock* b = CGraph->getFirst();
    while (b != NULL)
    {
        ControlFlowItem *cfi = b->getStart();
        ControlFlowItem *till = b->getEnd()->getNext();
        while (cfi != till)
        {
            b->adjustGenAndKillP(cfi);
            cfi = cfi->getNext();
        }
        b = b->getLexNext();

        //Обновить OUT
        b->getOutDefsP();
    }

    return false;
}

/*
 * Отдельная подготовка достигающих определений для указателей.
 * Она не может быть проведена вместе с быной т.к. GEN для указателей зависит от IN.
 * Всё также, только GEN обновляется на каждой итерации.
 */
void PreparePointers(ControlFlowGraph *CGraph)
{
    bool setsChanged = true;
    while (setsChanged)
    {
        setsChanged = false;
        //Вычисление KILL и GEN, GEN могло измениться
        setsChanged |= BuildGenKillAndUpdateOutP(CGraph);

        CBasicBlock* b = CGraph->getFirst();
        while (b != NULL)
        {
            /*Updating IN for pointers*/
            for (BasicBlockItem* prev = b->getPrev(); prev != NULL; prev = prev->next)
                mergeDefs(b->getInDefsP(), prev->block->getOutDefsP(), NULL);

            /*Updating OUT for pointers, true, if OUT has been changed*/
            setsChanged |= addDefsFilteredByKill(b->getOutDefsP(), b->getInDefsP(), b->getKillP());
            b = b->getLexNext();
        }
    }
}

void FillCFGInsAndOutsDefs(ControlFlowGraph *CGraph, map<SymbolKey, set<ExpressionValue*>> *inDefs, CommonVarsOverseer *overseer_Ptr)
{
    overseerPtr = overseer_Ptr;
    CBasicBlock *b = CGraph->getFirst();

    PreparePointers(CGraph);

    b = CGraph->getFirst();
#if PRINT_PROF_INFO
    auto time = high_resolution_clock::now();
#endif
    set<ExpressionValue*> availableExpressions;
    while (b != NULL)
    {
        ControlFlowItem *cfi = b->getStart();
        ControlFlowItem *till = b->getEnd()->getNext();
        while (cfi != till)
        {
            b->adjustGenAndKill(cfi);
            cfi = cfi->getNext();
        }

        availableExpressions.insert(b->getEGen()->begin(), b->getEGen()->end());
        /*Initialization of OUT, it equals to GEN	*/
        b->initializeOut();
        b = b->getLexNext();
    }
    b = CGraph->getFirst();
    while(b != NULL)
    {
        b->initializeEOut(availableExpressions);
        b = b->getLexNext();
    }
#if PRINT_PROF_INFO
    __spf_print(PRINT_PROF_INFO, "   block 1 %f\n", 
                duration_cast<milliseconds>(high_resolution_clock::now() - time).count() / 1000.);
    time = high_resolution_clock::now();
#endif
    if (inDefs != NULL)
        CGraph->getFirst()->setInDefs(inDefs);

    double timeMerge = 0;
    double timeAdd = 0;
    bool setsChanged = true;

    size_t countIn1 = 0;
    countMerge = 0;
    size_t countSet1 = 0;
    size_t countSet2 = 0;
    while (setsChanged)
    {
        setsChanged = false;
        b = CGraph->getFirst();
        while (b != NULL)
        {
#if PRINT_PROF_INFO
            auto locT = high_resolution_clock::now();
#endif
            /*Updating IN*/
            for (BasicBlockItem *prev = b->getPrev(); prev != NULL; prev = prev->next)
            {
#if PRINT_PROF_INFO
                countSet1 = std::max(countSet1, b->getInDefs()->size());
                countSet2 = std::max(countSet2, prev->block->getOutDefs()->size());
                countIn1++;
#endif
                mergeDefs(b->getInDefs(), prev->block->getOutDefs(), NULL);

            }
#if PRINT_PROF_INFO
            timeMerge += (duration_cast<milliseconds>(high_resolution_clock::now() - locT).count() / 1000.);
            locT = high_resolution_clock::now();
#endif
            /*Updating OUT, true, if OUT has been changed*/
            setsChanged |= addDefsFilteredByKill(b->getOutDefs(), b->getInDefs(), b->getKill());
#if PRINT_PROF_INFO
            timeAdd += (duration_cast<milliseconds>(high_resolution_clock::now() - locT).count() / 1000.);
#endif
            b = b->getLexNext();
        }
    }
#if PRINT_PROF_INFO
    __spf_print(PRINT_PROF_INFO, "   block 2 %f\n", 
                duration_cast<milliseconds>(high_resolution_clock::now() - time).count() / 1000.);
    __spf_print(PRINT_PROF_INFO, "     merge %f, count %d, MM [%lld, %lld] [%lld, %lld], merge call %lld, %lld %lld\n",
                timeMerge, countIn1, min1, max1, min2, max2, countMerge, countSet1, countSet2);
    __spf_print(PRINT_PROF_INFO, "     add %f\n", timeAdd);
#endif
    setsChanged = true;
    while (setsChanged)
    {
        setsChanged = false;
        b = CGraph->getFirst();
        while (b != NULL)
        {
            setsChanged |= b->updateEDefs();
            b = b->getLexNext();
        }
    }
    //printf("[%s]: %s\n", tag[newDeclaration->variant()], newDeclaration->unparse());

    /*Showtime*/
    //showDefsOfGraph(CGraph);
}

bool valueWithRecursion(const SymbolKey &var, SgExpression *exp)
{
    if (!exp)
        return false;

    if (exp->variant() == VAR_REF)
        return var == exp->symbol();

    bool recursionFounded = false;
    if (exp->rhs())
        recursionFounded = valueWithRecursion(var, exp->rhs());
    if (exp->lhs() && !recursionFounded)
        recursionFounded = valueWithRecursion(var, exp->lhs());

    return recursionFounded;
}

bool valueWithFunctionCall(SgExpression *exp)
{
    if (!exp)
        return false;

    if (exp->variant() == FUNC_CALL)
        return true;

    bool funcFounded = false;
    if (exp->rhs())
        funcFounded = valueWithFunctionCall(exp->rhs());
    if (exp->lhs() && !funcFounded)
        funcFounded = valueWithFunctionCall(exp->lhs());

    return funcFounded;
}

static bool ifExprExist(SgExpression *curr, const int toFind)
{
    if (curr)
    {
        if (curr->id() == toFind)
            return true;
        else
            return ifExprExist(curr->lhs(), toFind) || ifExprExist(curr->rhs(), toFind);
    }
    return false;
}

static SgStatement* getStatmentByExpression(SgStatement* begin, SgExpression* ex)
{
    SgStatement* start = begin ? begin : current_file->firstStatement();
    SgStatement* end = NULL;
    if (begin)
        end = getFuncStat(begin, { MODULE_STMT })->lastNodeOfStmt();

    for (auto st = start; st != end; st = st->lexNext())
    {
        for (int z = 0; z < 3; ++z)
            if (ifExprExist(st->expr(z), ex->id()))
                return st;
    }

    // if NULL found, try to find across full file
    if (begin)
    {
        for (auto st = current_file->firstStatement()->lexNext(); st; st = st->lexNext())
            for (int z = 0; z < 3; ++z)
                if (ifExprExist(st->expr(z), ex->id()))
                    return st;
    }
    return NULL;
}

static void findFuncCall(SgExpression* ex, vector<SgExpression*>& calls)
{
    if (ex)
    {
        if (ex->variant() == FUNC_CALL)
            if (!isIntrinsicFunctionName(ex->symbol()->identifier()))
                calls.push_back(ex);

        findFuncCall(ex->rhs(), calls);
        findFuncCall(ex->lhs(), calls);
    }
}

static bool hasArrayRef(SgExpression* ex, const string& arrName)
{
    bool retVal = false;
    if (ex)
    {
        if (ex->variant() == ARRAY_REF && ex->symbol()->identifier() == arrName)
            return true;

        retVal |= hasArrayRef(ex->lhs(), arrName);
        retVal |= hasArrayRef(ex->rhs(), arrName);
    }

    return retVal;
}

static bool checkRange(SgStatement *start, SgStatement *end, const DIST::Array* array)
{
    const string arrName = array->GetShortName();
    for (auto st = start; st != end; st = st->lexNext())
    {
        if (st->variant() == ASSIGN_STAT)
        {
            auto ex = st->expr(0);
            if (ex->variant() == ARRAY_REF && ex->symbol()->identifier() == arrName)
                return false;
        }
        else if (st->variant() == READ_STAT || st->variant() == WRITE_STAT || st->variant() == PRINT_STAT)
        {
            SgInputOutputStmt* s = isSgInputOutputStmt(st);
            if (s)
                if (hasArrayRef(s->itemList(), arrName))
                    return false;
        }
        else if (st->variant() == PROC_STAT)
        {
            for (auto args = st->expr(0); args; args = args->rhs())
            {
                if (args->lhs()->variant() == ARRAY_REF)
                    if (args->lhs()->symbol()->identifier() == arrName)
                        return false;
            }
        }
        else
        {
            //TODO: 
        }

        //find func call
        vector<SgExpression*> calls;
        for (int z = 0; z < 3; ++z)
            if (st->expr(z))
                findFuncCall(st->expr(z), calls);
        
        for (auto& elem : calls)
        {
            for (auto args = elem->lhs(); args; args = args->rhs())
            {
                if (args->lhs()->variant() == ARRAY_REF)
                    if (args->lhs()->symbol()->identifier() == arrName)
                        return false;
            }
        }

        if (calls.size())
        {
            if (array->GetLocation().first == DIST::l_MODULE || array->GetLocation().first == DIST::l_COMMON)
            {
                //TODO: need to add IP analysis
            }
        }
    }

    return true;
}

static map<SgSymbol*, pair<SgStatement*, DIST::Array*>> cache;

//TODO: improve for MODULE
static bool allowedArrayReference(SgExpression *exp, SgStatement* from)
{    
    //Must return false if exp is not allowed to be substituted
    SgSymbol* arr = OriginalSymbol(exp->symbol());
    checkNull(arr, convertFileName(__FILE__).c_str(), __LINE__);

    SgStatement* decl = NULL;
    DIST::Array* array = NULL;

    auto it = cache.find(arr);
    if (it != cache.end())
    {
        decl = it->second.first;
        array = it->second.second;
    }

    if (!decl)
    {
        decl = declaratedInStmt(arr);
        checkNull(decl, convertFileName(__FILE__).c_str(), __LINE__);
    }

    if (!array)
    {
        array = getArrayFromDeclarated(decl, arr->identifier());
        checkNull(array, convertFileName(__FILE__).c_str(), __LINE__);
    }

    if (it == cache.end())
        cache.insert(it, make_pair(arr, make_pair(decl, array)));

    if (array->GetDistributeFlagVal() == DIST::SPF_PRIV)
        return false;
    
    SgStatement* st = NULL;
    for (int z = 0; z < 3; ++z)
    {
        if (ifExprExist(from->expr(z), exp->id()))
        {
            st = from;
            break;
        }
    }
    
    if (st == NULL)
        st = getStatmentByExpression(decl, exp);    
    checkNull(st, convertFileName(__FILE__).c_str(), __LINE__);

    SgStatement* cp = st->controlParent();
    SgStatement* prev = st;
    while (isSgProgHedrStmt(cp) == NULL)
    {
        prev = cp;
        cp = cp->controlParent();
    }
    auto last = prev->lastNodeOfStmt();
    auto retVal = checkRange(cp, last, array);    
    return retVal;
}

bool valueWithArrayReference(SgExpression *exp, SgStatement* from)
{
    if (!exp)
        return false;

    if (isArrayRef(exp))
        return !allowedArrayReference(exp, from);

    bool arrayFounded = false;
    if (exp->rhs())
        arrayFounded = valueWithArrayReference(exp->rhs(), from);
    if (exp->lhs() && !arrayFounded)
        arrayFounded = valueWithArrayReference(exp->lhs(), from);

    return arrayFounded;
}

/*
* Can't expand var if:
* 1. it has multiple values
* 2. it is a NULL value
* 3. value has function call
* 4. value has itself within (recursion)
* 5. value has array reference
*/
void CBasicBlock::correctInDefsSimple()
{
    for (auto it = in_defs.begin(); it != in_defs.end();)
    {
        if (it->second.size() != 1) //1
            it = in_defs.erase(it);
        else if((*(it->second.begin()))->getExp() == NULL)
            it = in_defs.erase(it);
        else if (valueWithFunctionCall((*(it->second.begin()))->getExp())) //3
            it = in_defs.erase(it);
        else if (valueWithRecursion(it->first, (*(it->second.begin()))->getExp())) //4
            it = in_defs.erase(it);
        else if(valueWithArrayReference((*(it->second.begin()))->getExp(), (*(it->second.begin()))->getFrom()))
            it = in_defs.erase(it);
        else
            it++;
    }
}


/*
* Can't expand var if
* value is not present in one of previous block
*/
bool CBasicBlock::correctInDefsIterative() 
{
    bool changed = false;
    BasicBlockItem *bi = getPrev();

    if (bi)
    {
        set<SymbolKey> *allowedVars = bi->block->getOutVars();

        for (bi = bi->next; bi != NULL; bi = bi->next)
        {
            set<SymbolKey> *nextAllowedVars = bi->block->getOutVars();
            for (auto it = allowedVars->begin(); it != allowedVars->end();)
            {
                if (nextAllowedVars->find(*it) == nextAllowedVars->end())
                    it = allowedVars->erase(it);
                else
                    ++it;
            }
            delete nextAllowedVars;
        }

        //Clean inDefs
        for (auto it = in_defs.begin(); it != in_defs.end();)
            if (allowedVars->find(it->first) == allowedVars->end())
                it = in_defs.erase(it);
            else
                it++;

        //Clean outDefs
        vector<map<SymbolKey, set<ExpressionValue*>>::const_iterator> toDel;
        for (auto it = out_defs.begin(); it != out_defs.end(); ++it)
            if (allowedVars->find(it->first) == allowedVars->end()
                && gen.find(it->first) == gen.end())
                toDel.push_back(it);

        for (int i = 0; i < toDel.size(); ++i)
            out_defs.erase(toDel[i]);

        if (toDel.size() != 0)
            changed = true;

        delete allowedVars;
    }

    return changed;
}

void CorrectInDefs(ControlFlowGraph *CGraph)
{
    CBasicBlock *b = CGraph->getFirst();
    while (b != NULL)
    {
        b->correctInDefsSimple();
        b = b->getLexNext();
    }
/*    bool changes = true;
    while (changes)
    {
        changes = false;
        b = CGraph->getFirst();
        while (b != NULL)
        {
            changes |= b->correctInDefsIterative();
            b = b->getLexNext();
        }
    }*/
}
