#include "leak_detector.h"

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

#include "dvm.h"
#include <stdlib.h>
#include <stdio.h>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <iterator>
#include <fstream>
#include <chrono>

#include "../ParallelizationRegions/ParRegions.h"
#include "../ParallelizationRegions/ParRegions_func.h"
#include "graph_loops.h"
#include "graph_calls.h"
#include "graph_calls_func.h"
#include "utils.h"
#include "SgUtils.h"
#include "../Distribution/Distribution.h"
#include "../VisualizerCalls/get_information.h"
#include "expr_transform.h"

using std::pair;
using std::map;
using std::set;
using std::tuple;
using std::vector;
using std::string;
using std::iterator;
using std::make_pair;
using std::queue;
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::milliseconds;

#define PRINT_SUBSTITUTION_ABORT 1
#define PRINT_PROF_INFO 0
#define PRINT_PROF_INFO_TIME 0
/*
 * Contains original SgExpressions.
 * map <string, ...> key is a file, where replacements take place
 * map <SgStatement*, vector<SgExpression*>> key is a changed SgStetement, value - 3 SgEpression*'s
 * [i] SgExpresson* is null, if it has not been changed
 *
 * NOTE: 2 levels of replacements: first -- replace constant values, second -- replace other expressions with CFG  
 */

static map<string, map<SgStatement*, vector<SgExpression*>>> replacementsOfConstsInFiles;
static map<string, map<SgStatement*, vector<SgExpression*>>> replacementsInFiles;
static map<SgStatement*, vector<SgExpression*>>* curFileReplacements;

static set<SgStatement*> visitedStatements;
static CommonVarsOverseer overseer;
static GraphsKeeper *graphsKeeper = NULL;
static unsigned int substitutionsCounter = 0;
static void incrementSubstitutionsCounter(int print_prof_info) {
    if(print_prof_info)
        substitutionsCounter++;
}

SgStatement* findReplacedExpression(SgExpression *ex)
{
    const int id = ex->id();
    for (auto& byFile : replacementsInFiles)
    {
        for (auto& elem : byFile.second)
        {
            SgStatement* ret = elem.first;
            for (int z = 0; z < elem.second.size(); ++z)
                if (elem.second[z] == ex)
                    if (elem.second[z]->id() == id)
                        return ret;
        }
    }
    return NULL;
}

void GraphsKeeper::deleteGraphsKeeper()
{
    if(graphsKeeper)
        delete graphsKeeper;
    graphsKeeper = NULL;
}

GraphsKeeper* GraphsKeeper::getGraphsKeeper() {
    if(graphsKeeper == NULL)
        graphsKeeper = new GraphsKeeper();
    return graphsKeeper;
}

GraphItem* GraphsKeeper::buildGraph(SgStatement* st)
{
    GraphItem* result = new GraphItem();

    SetUpVars(&result->commons, &result->calls, result->calls.AddHeader(st, false, st->symbol(), current_file_id), &result->dldl);

    result->CGraph = GetControlFlowGraphWithCalls(true, st, &result->calls, &result->commons);
    result->calls.AssociateGraphWithHeader(st, result->CGraph);
    result->commons.MarkEndOfCommon(GetCurrentProcedure());
    result->file_id = current_file_id;

    /*std::fstream fs;
    fs.open("graph.txt", std::fstream::out);
    fs << result->CGraph->GetVisualGraph(&result->calls);
    fs.close();*/

    auto inserted = graphs.insert(make_pair(st, result));
    return inserted.first->second;
}


GraphItem* GraphsKeeper::getGraph(SgStatement *header)
{
    auto it = graphs.find(header);
    if (it == graphs.end())
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    GraphItem* res = graphs.find(header)->second;
    CurrentProject->file(res->file_id);
    return res;
}

CBasicBlock* GraphsKeeper::findBlock(SgStatement* stmt)
{
    for(auto &graph : graphs) {
        if(graph.second->file_id != stmt->getFileId())
            continue;

        stmt->switchToFile();

        CBasicBlock *b = graph.second->CGraph->getFirst();
        while(b != NULL)
        {
            ControlFlowItem *cfi = b->getStart();
            ControlFlowItem *till = b->getEnd()->getNext();
            while(cfi != till)
            {
                SgStatement *st = cfi->getStatement();
                if(st && st->id() == stmt->id())
                    return b;
                st = cfi->getOriginalStatement();
                if(st && st->id() == stmt->id())
                    return b;
                cfi = cfi->getNext();
            }
            b = b->getLexNext();
        }
    }
    return NULL;
}

bool findCFIsForStmt(SgStatement *stmt, vector <ControlFlowItem*> &cfis) {
    CBasicBlock* b = GraphsKeeper::getGraphsKeeper()->findBlock(stmt);
    if(!b)
        return false;

    ControlFlowItem *cfi = b->getStart();
    ControlFlowItem *till = b->getEnd()->getNext();

    while (cfi != till)
    {
        SgStatement *st = cfi->getStatement();
        if (st && st->id() == stmt->id())
            cfis.push_back(cfi);
        else {
            st = cfi->getOriginalStatement();
            if (st && st->id() == stmt->id())
                cfis.push_back(cfi);
        }
        cfi = cfi->getNext();
    }

    return cfis.size() != 0;

}

const map<SymbolKey, set<ExpressionValue*>> getReachingDefinitionsExt(SgStatement* stmt)
{
    CBasicBlock* b = GraphsKeeper::getGraphsKeeper()->findBlock(stmt);
    if(!b)
        return map<SymbolKey, set<ExpressionValue*>>();

    return b->getReachedDefinitionsExt(stmt);
}

static void revertReplacements(map<SgStatement*, vector<SgExpression*>> &toRev, bool constant = false)
{
    for (auto &toReplace : toRev)
    {
        SgStatement *parent = toReplace.first;
        for (int i = 0; i < 3; ++i)
        {
            if (toReplace.second[i] != NULL)
            {
                SgExpression *replacement = parent->expr(i);
                parent->setExpression(i, *(toReplace.second[i]));
                toReplace.second[i] = replacement;
            }
        }
    }
}

void revertReplacements(const string &filename, bool back)
{
    auto constF = replacementsOfConstsInFiles.find(filename);
    auto tmpF = replacementsInFiles.find(filename);

    if (back)
    {
        if (tmpF != replacementsInFiles.end())
            revertReplacements(tmpF->second);
        if (constF != replacementsOfConstsInFiles.end())
            revertReplacements(constF->second, true);
    }
    else
    {        
        if (constF != replacementsOfConstsInFiles.end())
            revertReplacements(constF->second, true);
        if (tmpF != replacementsInFiles.end())
            revertReplacements(tmpF->second);
    }    
}

static SgExpression* getFromConstructor(SgExpression *e, int num)
{
    int i = 1;
    while (e)
    {
        if (i == num)
            return e->lhs();
        ++i;
        e = e->rhs();
    }

    return NULL;
}

SgExpression* ReplaceParameter_(SgExpression *e)
{
    if (!e)
        return e;
    
    if (e->variant() == CONST_REF)
    {
        auto t1 = e->type();
        auto t2 = e->symbol()->type();

        SgConstantSymb *sc = isSgConstantSymb(e->symbol());
        if (!sc->constantValue())
            return e;
        auto val = sc->constantValue();
        auto typeVal = val->type();
        //if type of constant does not match type of variable
        if (e->symbol()->type()->variant() == T_DOUBLE && val->variant() == FLOAT_VAL && typeVal->variant() == T_FLOAT)
        {
            char* digit_o = ((SgValueExp*)val)->floatValue();
            SgExpression* val = typeVal->selector();

            char* digit = new char[strlen(digit_o) + 2];
            strcpy(digit, digit_o);
            digit[strlen(digit_o)] = 'f';
            digit[strlen(digit_o) + 1] = '\0';

            SgValueExp* valDouble = new SgValueExp(double(0.0), digit);
            delete[]digit;

            if (val != NULL)
            {
                if (val->valueInteger() == 8) // double
                    return sc->constantValue()->copyPtr();
                else if (val->valueInteger() == 4) // float
                    return valDouble;
                else
                    return valDouble;
            }
            else
                return valDouble;
        }
        else
            return (ReplaceParameter_(&(sc->constantValue()->copy())));
    }
    else if (e->variant() == ARRAY_REF)
    {
        SgConstantSymb *sc = isSgConstantSymb(e->symbol());
        if (sc)
        {            
            SgExpression *constructor = sc->constantValue();
            SgArrayRefExp *ref = isSgArrayRefExp(e);
            if (ref->numberOfSubscripts() == 1)
            {
                auto sub = ref->subscript(0);
                if (sub->isInteger())
                {
                    int num = sub->valueInteger();
                    SgExpression *value = getFromConstructor(constructor->lhs(), num);
                    if (value && ((value->lhs() == NULL && value->rhs() == NULL) || value->variant() == MINUS_OP))
                        return (ReplaceParameter_(&(value->copy())));
                }
            }
        }
    }

    e->setLhs(ReplaceParameter_(e->lhs()));
    e->setRhs(ReplaceParameter_(e->rhs()));
    return e;
}

SgExpression* ReplaceConstant(SgExpression *e)
{
    SgExpression *er;
    er = ReplaceParameter_(&(e->copy()));
    if (er->isInteger())
        return (new SgValueExp(er->valueInteger()));
    else
        return er;
}

SgExpression* ReplaceArrayBoundSizes(SgExpression *edim)
{
    SgSubscriptExp *sbe = NULL;
    SgExpression *low = NULL;

    if (!edim)
        return edim;

    if ((sbe = isSgSubscriptExp(edim)) != NULL) //DDOT
    {
        if (!sbe->ubound())
            return edim;

        edim = new SgExpression(DDOT);
        edim->setLhs(ReplaceArrayBoundSizes(sbe->lbound()));
        edim->setRhs(ReplaceArrayBoundSizes(sbe->ubound()));
    }
    else
        edim = ReplaceConstant(edim);
    return edim;
}

static pair<int, int> getCoefsOfSubscript(SgExpression *exp, SgSymbol *doName, int &err)
{
    pair<int, int> result;
    pair<int, int> lRes = make_pair(0, 0), rRes = make_pair(0, 0);
    int lErr = 0, rErr = 0;
    const int op = exp->variant();

    if (exp->lhs())
        lRes = getCoefsOfSubscript(exp->lhs(), doName, lErr);
    if (exp->rhs())
        rRes = getCoefsOfSubscript(exp->rhs(), doName, rErr);

    switch (op)
    {
    case ADD_OP:
        if (lErr != -1 && rErr != -1)
            result = make_pair(lRes.first + rRes.first, lRes.second + rRes.second);
        else
            err = -1;
        break;
    case MINUS_OP:
        if (lErr != -1)
            result = make_pair(-lRes.first, lRes.second);
        else
            err = -1;
        break;
    case UNARY_ADD_OP:
        if (lErr != -1)
            result = make_pair(+lRes.first, lRes.second);
        else
            err = -1;
        break;
    case MULT_OP:
        if (lErr != -1 && rErr != -1)
        {
            if (lRes.first != 0 && rRes.first != 0)
                err = -1;
            else
                result = make_pair(lRes.first * rRes.second + rRes.first * lRes.second, lRes.second * rRes.second);
        }
        else
            err = -1;
        break;
    case SUBT_OP:
        if (lErr != -1 && rErr != -1)
            result = make_pair(lRes.first - rRes.first, lRes.second - rRes.second);
        else
            err = -1;
        break;
    case DIV_OP:
        if (lErr != -1 && rErr != -1)
        {
            if (rRes.first != 0 || rRes.second == 0)
                err = -1;
            else
                result = make_pair(lRes.first / rRes.second, lRes.second / rRes.second);
        }
        else
            err = -1;
        break;
    case MOD_OP:
        if (lErr != -1 && rErr != -1)
        {
            if (rRes.first != 0 || rRes.second == 0)
                err = -1;
            else
                result = make_pair(lRes.first % rRes.second, lRes.second % rRes.second);
        }
        else
            err = -1;
        break;
    case VAR_REF:
        if (strcmp(exp->symbol()->identifier(), doName->identifier()) == 0)
            result = make_pair(1, 0);
        else
            err = -1;
        break;
    case INT_VAL:
        result = make_pair(0, exp->valueInteger());
        break;
    default:
        err = -1;
    }

    if (err != -1)
        return result;
    else
        return make_pair(0, 0);
}

bool replaceConstantRec(SgExpression *&exp)
{
    bool changed = false;
    if (exp->variant() == CONST_REF)
    {
        SgExpression *ret = ReplaceParameter_(exp);

        int sign = 1;
        SgExpression* toCalc = ret;
        if (toCalc->variant() == UNARY_ADD_OP)
            toCalc = toCalc->lhs();

        if (toCalc->variant() == MINUS_OP)
        {
            toCalc = toCalc->lhs();
            sign = -1;
        }

        if (toCalc->isInteger())
        {
            exp = new SgValueExp(sign * toCalc->valueInteger());
            changed = true;
        }
    }

    if (exp->lhs())
    {
        SgExpression *left = exp->lhs();
        changed |= replaceConstantRec(left);
        exp->setLhs(left);
    }

    if (exp->rhs())
    {
        SgExpression *right = exp->rhs();
        changed |= replaceConstantRec(right);
        exp->setRhs(right);
    }

    return changed;
}

int CalculateInteger(SgExpression *expr, int &result)
{
    if (expr->variant() == INT_VAL)
    {
        result = expr->valueInteger();
        return 0;
    }
    else
    {
        pair<int, int> err = make_pair(-1, -1);
        pair<int, int> res;
        if (expr->lhs())
            err.first = CalculateInteger(expr->lhs(), res.first);
        if (expr->rhs())
            err.second = CalculateInteger(expr->rhs(), res.second);

        switch (expr->variant())
        {
        case ADD_OP:
            if (err.first != -1 && err.second != -1)
            {
                result = res.first + res.second;
                return 0;
            }
            else
                return -1;
            break;
        case MINUS_OP:
            if (err.first != -1)
            {
                result = -res.first;
                return 0;
            }
            else
                return -1;
            break;
        case UNARY_ADD_OP:
            if (err.first != -1)
            {
                result = res.first;
                return 0;
            }
            else
                return -1;
            break;
        case MULT_OP:
            if (err.first != -1 && err.second != -1)
            {
                result = res.first * res.second;
                return 0;
            }
            else if(err.first != -1 && res.first == 0)
            {
                result = 0;
                return 0;
            }
            else if (err.second != -1 && res.second == 0)
            {
                result = 0;
                return 0;
            }
            else
                return -1;
            break;
        case SUBT_OP:
            if (err.first != -1 && err.second != -1)
            {
                result = res.first - res.second;
                return 0;
            }
            else
                return -1;
            break;
        case DIV_OP:
            if (err.first != -1 && err.second != -1 && res.second != 0)
            {
                result = res.first / res.second;
                return 0;
            }
            else
                return -1;
            break;
        case MOD_OP:
            if (err.first != -1 && err.second != -1 && res.second != 0)
            {
                result = res.first % res.second;
                return 0;
            }
            else
                return -1;
            break;
        case EXP_OP:
            if (err.first != -1 && err.second != -1)
            {
                result = (int) pow(res.first, res.second);
                return 0;
            }
            else
                return -1;
            break;
        default:
            return -1;
        }
    }
}

SgExpression* CalculateInteger(SgExpression *expr)
{
    int value, res;
    replaceConstantRec(expr);
    res = CalculateInteger(expr, value);
    if (res != -1)
        return new SgValueExp(value);
    else
        return expr;
}

bool calculate(SgExpression *&exp)
{
    bool changed = false;

    SgExpression *left, *right;
    int res, err;

    if ((left = exp->lhs()) != NULL)
    {
        err = CalculateInteger(left, res);
        if (err != -1)
        {
            exp->setLhs(new SgValueExp(res));
            changed = true;
        }
        else
            changed |= calculate(left);
    }

    if ((right = exp->rhs()) != NULL)
    {
        err = CalculateInteger(right, res);
        if (err != -1)
        {
            exp->setRhs(new SgValueExp(res));
            changed = true;
        }
        else
            changed |= calculate(right);
    }

    return changed;
}

static bool isNeedFinishCalc(SgExpression *exp, string doName)
{
    bool ret = true;
    if (exp->variant() == VAR_REF)
    {
        if (string(exp->symbol()->identifier()) != doName)
            ret = false;
    }

    if (exp->lhs())
        ret &= isNeedFinishCalc(exp->lhs(), doName);

    if (exp->rhs())
        ret &= isNeedFinishCalc(exp->rhs(), doName);

    return ret;
}

static void replaceVarRefToZero(SgExpression *&exp, int &countVarRefs)
{
    if (exp->variant() == VAR_REF)
    {
        exp = new SgValueExp(0);
        countVarRefs++;
    }

    SgExpression *tmp;
    if (exp->lhs())
    {
        tmp = exp->lhs();
        replaceVarRefToZero(tmp, countVarRefs);
        exp->setLhs(tmp);
    }

    if (exp->rhs())
    {
        tmp = exp->rhs();
        replaceVarRefToZero(tmp, countVarRefs);
        exp->setRhs(tmp);
    }
}

void getCoefsOfSubscript(pair<int, int> &retCoefs, SgExpression *exp, SgSymbol *doName)
{
    SgExpression *copyExp = &(exp->copy());
    replaceConstantRec(copyExp);
    calculate(copyExp);

    if (isNeedFinishCalc(copyExp, doName->identifier()))
    {
        int err = 0;
        retCoefs = getCoefsOfSubscript(copyExp, doName, err);
    }
    else
        retCoefs = make_pair(0, 0);
}

static SgExpression* valueOfVar(SgExpression *var, CBasicBlock *b)
{
    SgExpression *exp = NULL;
    //first, check previous defs within block
    auto founded = b->getGen()->find(SymbolKey(var->symbol()));
    if (founded != b->getGen()->end())
        if (!valueWithFunctionCall(founded->second->getExp()))
            if (!valueWithRecursion(founded->first, founded->second->getExp()))
                if(!valueWithArrayReference(founded->second->getExp(), founded->second->getFrom()))
                    if(b->expressionIsAvailable(founded->second))
                        exp = founded->second->getExp();


    if (exp == NULL)
    {
        //second, check defs from previous blocks
        auto founded_inDefs = b->getInDefs()->find(SymbolKey(var->symbol()));
        if (founded_inDefs != b->getInDefs()->end())
        {
            //if smth is founded_inDefs, it has single value
            //thanks to CorrectInDefs(ControlFlowGraph*) function
            ExpressionValue *expVal = *(founded_inDefs->second.begin());
            //we have to check if this value was killed inside block
            if(b->expressionIsAvailable(expVal))
                exp = expVal->getExp();
        }
    }

    return exp;

}

void createLinksToCopy(SgExpression *exOrig, SgExpression *exCopy)
{
    if (exOrig && exCopy)
    {
        if (exOrig->variant() == ARRAY_REF && exCopy->variant() == ARRAY_REF)
        {
            exOrig->addAttribute(ARRAY_REF, exCopy, sizeof(SgExpression*));
            exCopy->addAttribute(ARRAY_REF, exOrig, sizeof(SgExpression*));
        }

        createLinksToCopy(exOrig->lhs(), exCopy->lhs());
        createLinksToCopy(exOrig->rhs(), exCopy->rhs());
    }    
}

static void createBackup(SgStatement* stmt, int expNumber)
{
    SgExpression *exp = stmt->expr(expNumber);

    if (exp == NULL) // no backup is required
        return;

    auto foundedParent = curFileReplacements->find(stmt);
    if (foundedParent == curFileReplacements->end())
    {
        auto inserted = curFileReplacements->insert(make_pair(stmt, vector<SgExpression*>()));
        foundedParent = inserted.first;
        foundedParent->second.resize(3);
        for (int i = 0; i < 3; ++i)
            foundedParent->second[i] = NULL;
    }

    if (foundedParent->second[expNumber] == NULL)
    {
        SgExpression *expToCopy = exp->copyPtr();
        foundedParent->second[expNumber] = expToCopy;
                
        const string orig = string(exp->unparse());
        const string copy = string(expToCopy->unparse());
        if (orig == copy)
            createLinksToCopy(exp, expToCopy);
    }
}

static SgExpression* tryMakeInt(SgExpression* exp)
{
    if (exp->variant() != INT_VAL)
        if (exp->isInteger())
            return new SgValueExp(exp->valueInteger());
    return exp;
}

static bool typesAreTheSame(SgExpression *oldExp, SgExpression *newExp, int lineNumber) 
{
    paramType oldType = detectExpressionType(oldExp);
    paramType newType = detectExpressionType(newExp);
    if(oldType != newType) 
    {
        char* tmp = oldExp->unparse();
        __spf_print(PRINT_SUBSTITUTION_ABORT, "%d: Substitution aborted %s -> ", lineNumber, tmp);
        __spf_print(PRINT_SUBSTITUTION_ABORT, "%s. Types do not match: %s != %s\n", newExp->unparse(), paramNames[oldType], paramNames[newType]);
        return false;
    }

    return true;
}

static bool setNewSubexpression(SgExpression *parent, bool rightSide, SgExpression *newExp, const int lineNumber)
{
    SgExpression *oldExp = rightSide ? parent->rhs() : parent->lhs();

    __spf_print(PRINT_PROF_INFO, "%d: %s -> ",lineNumber, oldExp->unparse());
    __spf_print(PRINT_PROF_INFO, "%s\n", newExp->unparse());
    incrementSubstitutionsCounter(PRINT_PROF_INFO);

    SgExpression* expToCopy = newExp->copyPtr();
    calculate(expToCopy);

    rightSide ? parent->setRhs(tryMakeInt(expToCopy)) : parent->setLhs(tryMakeInt(expToCopy));

    return true;
}

static bool replaceVarsInExpression(SgStatement *parent, int expNumber, CBasicBlock *b, bool replaceFirstVar)
{
    queue<SgExpression*> toCheck;
    bool wereReplacements = false;
    SgExpression* exp = parent->expr(expNumber);
    if (exp->variant() == VAR_REF && !replaceFirstVar)
        return false;

    //If SgExpression is a single VAR
    if (exp->variant() == VAR_REF)
    {
        SgExpression* newExp = valueOfVar(exp, b);
        if (newExp != NULL && typesAreTheSame(exp, newExp, parent->lineNumber()))
        {
            createBackup(parent, expNumber);

            __spf_print(PRINT_PROF_INFO, "%d: %s -> %s",parent->lineNumber(), exp->unparse(), newExp->unparse());
            incrementSubstitutionsCounter(PRINT_PROF_INFO);
            SgExpression* expToCopy = newExp->copyPtr();
            calculate(expToCopy);
            parent->setExpression(expNumber, *(tryMakeInt(expToCopy)));
            return true;
        }
    }//If not
    else
    {
        toCheck.push(exp);
        while (!toCheck.empty())
        {
            exp = toCheck.front();
            toCheck.pop();

            if (exp->rhs())
            {
                if (exp->rhs()->variant() == VAR_REF)
                {
                    SgExpression* newExp = valueOfVar(exp->rhs(), b);
                    if (newExp != NULL && typesAreTheSame(exp->rhs(), newExp, parent->lineNumber()))
                    {
                        createBackup(parent, expNumber);
                        wereReplacements |= setNewSubexpression(exp, true, newExp, parent->lineNumber());
                    }
                }
                else
                    toCheck.push(exp->rhs());
            }

            if (exp->variant() != FUNC_CALL)// Funcs' arguments are processed in replaceCallArguments
                if (exp->lhs())
                {
                    if (exp->lhs()->variant() == VAR_REF)
                    {
                        SgExpression* newExp = valueOfVar(exp->lhs(), b);
                        if (newExp != NULL && typesAreTheSame(exp->lhs(), newExp, parent->lineNumber()))
                        {
                            createBackup(parent, expNumber);
                            wereReplacements |= setNewSubexpression(exp, false, newExp, parent->lineNumber());
                        }
                    }
                    else
                        toCheck.push(exp->lhs());
                }
        }
    }
    return wereReplacements;
}

static bool needReplacements(SgExpression* exp, map<SymbolKey, vector<SgExpression*>>* ins, bool checkVar)
{
    if (exp->variant() == VAR_REF)
    {
        if (checkVar)
            for (auto it = ins->begin(); it != ins->end(); ++it)
                if (it->first == exp->symbol())
                    return true;
        return false;
    }
    else
    {
        bool needReplacement = false;
        if (exp->rhs())
            needReplacement = needReplacements(exp->rhs(), ins, true);
        if (exp->lhs() && !needReplacement)
            needReplacement = needReplacements(exp->lhs(), ins, true);
        return needReplacement;
    }
}

static bool replaceVarsInCallArgument(SgExpression *exp, const int lineNumber, CBasicBlock *b)
{
    bool wereReplacements = false;
    SgExpression *lhs = exp->lhs(), *rhs = exp->rhs();

    if(lhs && lhs->variant() != FUNC_CALL)     //We don't want expand arguments of other functions.
    {
        if(lhs->variant() == VAR_REF)
        {
            SgExpression* newExp = valueOfVar(lhs, b);
            if (newExp != NULL && typesAreTheSame(lhs, newExp, lineNumber))
            {
                wereReplacements |= setNewSubexpression(exp, false, newExp, lineNumber);
            }
        }
        else
            wereReplacements |= replaceVarsInCallArgument(lhs, lineNumber, b);
    }

    if(rhs && rhs->variant() != FUNC_CALL)    //We don't want expand arguments of other functions
    {
        if(rhs->variant() == VAR_REF)
        {
            SgExpression* newExp = valueOfVar(rhs, b);
            if (newExp != NULL && typesAreTheSame(rhs, newExp, lineNumber))
            {
                wereReplacements |= setNewSubexpression(exp, true, newExp, lineNumber);
            }
        }
        else
            wereReplacements |= replaceVarsInCallArgument(rhs, lineNumber, b);
    }

    return wereReplacements;
}

static bool replaceCallArguments(ControlFlowItem *cfi, CBasicBlock *b)
{
    bool wereReplacements = false;
    int numberOfArgs = 0, lineNumber = 0;
    SgExpression *args = NULL, *arg = NULL;
    SgFunctionCallExp *funcCall = NULL;
    SgStatement *st = cfi->getStatement();
    AnalysedCallsList *callData = cfi->getCall();

    if(callData == NULL)//no info of arguments, can't expand expressions;
        return false;

    if (st != NULL && st->variant() == PROC_STAT) //procedure call
    {
        createBackup(st, 0);
        SgCallStmt *callStmt = isSgCallStmt(cfi->getStatement());
        args = callStmt->expr(0);
        numberOfArgs = callStmt->numberOfArgs();
        lineNumber = callStmt->lineNumber();
    }
    else if ((funcCall = cfi->getFunctionCall()) != NULL) //functionCall
    {
        st = cfi->getOriginalStatement();
        for (int i = 0; i < 3; ++i)
            if (st->expr(i))// TODO: search for expression?
                createBackup(st, i);
        args = funcCall->lhs();
        lineNumber = st->lineNumber();
        numberOfArgs = funcCall->numberOfArgs();
    }

    if (args)
    {
        for (int i = 0; i < numberOfArgs; ++i)
        {
            arg = args->lhs();

            if (arg->variant() == VAR_REF && argIsReplaceable(i, callData))
            {
                SgExpression* newExp = valueOfVar(arg, b);
                if (newExp != NULL && typesAreTheSame(arg, newExp, lineNumber))
                {
                    wereReplacements |= setNewSubexpression(args, false, newExp, lineNumber);
                }
            }
            else if (arg->variant() != VAR_REF)
                wereReplacements |= replaceVarsInCallArgument(arg, lineNumber, b);
            args = args->rhs();
        }
    }

    return wereReplacements;
}

/*
 * Have to run b->adjustGenAndKill(cfi) here to track changes inside block.
 */
static bool replaceVarsInBlock(CBasicBlock* b)
{
    bool wereReplacements = false;
    SgStatement* st;
    for (ControlFlowItem* cfi = b->getStart(); cfi != b->getEnd()->getNext(); cfi = cfi->getNext())
    {
        st = cfi->getStatement();
        if(cfi->getFunctionCall())
        {
            wereReplacements |= replaceCallArguments(cfi, b);
            b->checkFuncAndProcCalls(cfi);
        }

        if (st != NULL)
        {
            if (isDVM_stat(st))
                continue;
            if (isSPF_stat(st))
                continue;

            switch (st->variant())
            {
            case ASSIGN_STAT:
                wereReplacements |= replaceVarsInExpression(st, 0, b, false);
                wereReplacements |= replaceVarsInExpression(st, 1, b, true);
                b->adjustGenAndKill(cfi);
                break;
            case PROC_STAT:
                wereReplacements |= replaceCallArguments(cfi, b);
                b->adjustGenAndKill(cfi);
                break;
            case READ_STAT:
                //TODO вообще-то подставлять нельзя только переменные, а индексы массивов можно, но стоиот ли?
                b->adjustGenAndKill(cfi);
                break;
            case POINTER_ASSIGN_STAT:
                b->adjustGenAndKill(cfi);
                break;
            default:
                for (int i = 0; i < 3; ++i)
                    if (st->expr(i))
                        wereReplacements |= replaceVarsInExpression(st, i, b, true);
            }
        }
        else if ((st = cfi->getOriginalStatement()) != NULL)    //if, loop or funcCall statement condition expressions
        {
            if(cfi->getFunctionCall())//processed already
                continue;
            if (visitedStatements.find(st) == visitedStatements.end())
            {
                visitedStatements.insert(st);
                if (st->expr(0))
                    wereReplacements |= replaceVarsInExpression(st, 0, b, true);
            }
        }
    }
    return wereReplacements;
}

void ExpandExpressions(ControlFlowGraph* CGraph, map<SymbolKey, set<ExpressionValue*>> &inDefs)
{
    bool wereReplacements = true;
    while (wereReplacements)
    {
        createNeededException();
        __spf_print(PRINT_PROF_INFO_TIME, "New substitution iteration\n");
        
        auto time = high_resolution_clock::now();
        wereReplacements = false;
        visitedStatements.clear();

        __spf_print(PRINT_PROF_INFO_TIME, " clear vis %f\n", 
                    duration_cast<milliseconds>(high_resolution_clock::now() - time).count() / 1000.);
        time = high_resolution_clock::now();

        ClearCFGInsAndOutsDefs(CGraph);
        __spf_print(PRINT_PROF_INFO_TIME, " clear CFG %f\n", 
                    duration_cast<milliseconds>(high_resolution_clock::now() - time).count() / 1000.);
        time = high_resolution_clock::now();

        FillCFGInsAndOutsDefs(CGraph, &inDefs, &overseer);
        __spf_print(PRINT_PROF_INFO_TIME, " fill %f\n", 
                    duration_cast<milliseconds>(high_resolution_clock::now() - time).count() / 1000.);
        time = high_resolution_clock::now();

        CorrectInDefs(CGraph);
        __spf_print(PRINT_PROF_INFO_TIME, " correct %f\n", 
                    duration_cast<milliseconds>(high_resolution_clock::now() - time).count() / 1000.);
        time = high_resolution_clock::now();

        for (CBasicBlock* b = CGraph->getFirst(); b != NULL; b = b->getLexNext())
        {
            b->clearGenKill(); //Needed for correct expanding within block
            if (replaceVarsInBlock(b))
                wereReplacements = true;
        }
        __spf_print(PRINT_PROF_INFO_TIME, " replace %f\n", 
                    duration_cast<milliseconds>(high_resolution_clock::now() - time).count() / 1000.);
    }
}

void BuildUnfilteredReachingDefinitions(ControlFlowGraph* CGraph, map<SymbolKey, set<ExpressionValue*>> &inDefs, const string &filename)
{
    __spf_print(PRINT_PROF_INFO, "Building unfiltered reaching definitions\n");

    visitedStatements.clear();
    ClearCFGInsAndOutsDefs(CGraph);
    FillCFGInsAndOutsDefs(CGraph, &inDefs, &overseer);

    /* Showtime */
//    showDefsOfGraph(CGraph);
//    debugStructure(CGraph, filename);
}

ControlFlowGraph* BuildUnfilteredReachingDefinitionsFor(SgStatement *header) 
{
    auto inDefs = createHeaderInDefs(header);
    ControlFlowGraph *CGraph = GraphsKeeper::getGraphsKeeper()->getGraph(header)->CGraph;
    BuildUnfilteredReachingDefinitions(CGraph, inDefs, header->getFile()->filename());
    return CGraph;
}

static void initOverseer(const map<string, vector<DefUseList>> &defUseByFunctions, const map<string, CommonBlock*> &commonBlocks, const map<string, vector<FuncInfo*>> &allFuncInfo)
{
    vector<FuncCallSE> funcCalls;
    for(auto &file : allFuncInfo)
        for(auto& funcInfo : file.second)
            funcCalls.push_back(FuncCallSE(funcInfo->funcName, funcInfo->callsFrom));

    for (auto& commonBlock : commonBlocks) {
        auto grouped = commonBlock.second->getGroupedVars();
        for (auto& var : commonBlock.second->getVariables())
            if (var->getType() == SCALAR)
                for (auto& fun : var->getAllUse())
                {
                    bool deffed = false;
                    auto func = fun.getFunctionName();
                    auto foundedDefUse = defUseByFunctions.find(func);
                    if (foundedDefUse != defUseByFunctions.end())
                        for (auto& varDefUse : foundedDefUse->second)
                            if (varDefUse.isDef() && varDefUse.getVar() == var->getName())
                            {
                                deffed = true;
                                break;
                            }
                    if (deffed)
                    {
                        queue<string> killerFuncs = queue<string>();
                        set<string> processed = set<string>();
                        killerFuncs.push(fun.getFunctionName());
                        while (!killerFuncs.empty())
                        {
                            string& curFunc = killerFuncs.front();
                            processed.insert(curFunc);
                            killerFuncs.pop();
                            for (auto& file : allFuncInfo)
                                for (auto& funcInfo : file.second)
                                    if (funcInfo->callsFrom.find(curFunc) != funcInfo->callsFrom.end() &&
                                        processed.find(funcInfo->funcName) == processed.end())
                                        killerFuncs.push(funcInfo->funcName);
                            //added all variables at same common position
                            overseer.addKilledVar(var->getSymbol(), fun.getFunctionName());
                            auto atPlace = grouped.find(var->getPosition());
                            if (atPlace == grouped.end())
                                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                            for (auto& sameVar : atPlace->second)
                                overseer.addKilledVar(sameVar->getSymbol(), fun.getFunctionName());
                        }
                    }
                }
    }
    overseer.riseInited();
}

static bool findConstRef(SgExpression *ex)
{
    bool ret = false;
    if (ex)
    {
        if (ex->variant() == CONST_REF)
            return true;

        ret = ret || findConstRef(ex->lhs());
        ret = ret || findConstRef(ex->rhs());
    }
    return ret;
}

static void replaceOrigFuncSymbols(SgExpression* ex, SgExpression* copy)
{
    if (ex && copy)
    {
        if (ex->variant() == FUNC_CALL)
            copy->setSymbol(ex->symbol());
        replaceOrigFuncSymbols(ex->lhs(), copy->lhs());
        replaceOrigFuncSymbols(ex->rhs(), copy->rhs());
    }
}

static void replaceConstants(const string &file, SgStatement *st)
{
    auto it = replacementsOfConstsInFiles.find(file);
    if (it == replacementsOfConstsInFiles.end())    
        it = replacementsOfConstsInFiles.insert(it, make_pair(file, map<SgStatement*, vector<SgExpression*>>()));
        
    auto last = st->lastNodeOfStmt();
    for (SgStatement *currS = st; currS != last; currS = currS->lexNext())
    {
        if (currS->variant() == CONTAINS_STMT)
            break;

        if (isSgExecutableStatement(currS))
        {
            vector<SgExpression*> toRepl = { NULL, NULL, NULL };
            vector<SgExpression*> original = { NULL, NULL, NULL };

            for (int i = 0; i < 3; ++i)
            {
                if (findConstRef(currS->expr(i)))
                {
                    original[i] = currS->expr(i)->copyPtr();
                    SgExpression *copy = ReplaceConstant(original[i]);
                    createLinksToCopy(original[i], copy);

                    calculate(copy);
                    toRepl[i] = copy;

                    currS->setExpression(i, *copy);
                }
            }

            if (toRepl[0] || toRepl[1] || toRepl[2])
            {
                it->second[currS] = original;
                for (int z = 0; z < 3; ++z)
                    replaceOrigFuncSymbols(currS->expr(z), original[z]);
            }
        }
    }
}

static bool isInParallelRegion(SgStatement *func, const vector<ParallelRegion*> &regions)
{
    bool ok = false;
    auto last = func->lastNodeOfStmt();
    for (auto st = func; st != last; st = st->lexNext())
    {
        if (getAllRegionsByLine(regions, st->fileName(), st->lineNumber()).size())
        {
            ok = true;
            break;
        }
    }

    return ok;
}

map<SymbolKey, set<ExpressionValue*>> createHeaderInDefs(SgStatement *header) {
    map<SymbolKey, set<ExpressionValue*>> inDefs;
    ExpressionValue* emptyValue = allocateExpressionValue(NULL, NULL);
    emptyValue->setFrom(header);
    if(header->variant() == PROC_HEDR || header->variant() == FUNC_HEDR)
        for(int i=0; i<((SgProcHedrStmt*)header)->numberOfParameters();++i)
            inDefs.insert(make_pair(((SgProcHedrStmt*)header)->parameter(i), set<ExpressionValue*>()))
            .first->second.insert(emptyValue);
    return inDefs;
}

void expressionAnalyzer(SgFile *file, const map<string, vector<DefUseList>> &defUseByFunctions, 
                        const map<string, CommonBlock*> &commonBlocks, const map<string, vector<FuncInfo*>> &allFuncInfo,
                        const vector<ParallelRegion*> &regions)
{
    if(!overseer.isInited())
        initOverseer(defUseByFunctions, commonBlocks, allFuncInfo);

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

    auto itRep = replacementsInFiles.find(filename);
    if (itRep == replacementsInFiles.end())
        itRep = replacementsInFiles.insert(itRep, make_pair(filename, map<SgStatement*, vector<SgExpression*>>()));
    else // replecemets are have been made already
    {
        revertReplacements(filename);
        return;
    }
    curFileReplacements = &(itRep->second);

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

        if (isInParallelRegion(st, regions) == false)
        {
            __spf_print(1, "  skip function '%s\n", st->symbol()->identifier());
            continue;
        }

        if (st->variant() == PROG_HEDR)
        {
            SgProgHedrStmt *progH = (SgProgHedrStmt*) st;
            __spf_print(PRINT_PROF_INFO, "*** Program <%s> started at line %d / %s\n", progH->symbol()->identifier(), st->lineNumber(), st->fileName());
        }
        else if (st->variant() == PROC_HEDR)
        {
            SgProcHedrStmt *procH = (SgProcHedrStmt*) st;
            __spf_print(PRINT_PROF_INFO, "*** Function <%s> started at line %d / %s\n", procH->symbol()->identifier(), st->lineNumber(), st->fileName());
        }
        else if (st->variant() == FUNC_HEDR)
        {
            SgFuncHedrStmt *funcH = (SgFuncHedrStmt*) st;
            __spf_print(PRINT_PROF_INFO, "*** Function <%s> started at line %d / %s\n", funcH->symbol()->identifier(), st->lineNumber(), st->fileName());
        }


        GraphsKeeper* gk = GraphsKeeper::getGraphsKeeper();

        replaceConstants(filename, st);

        ControlFlowGraph* CGraph = gk->buildGraph(st)->CGraph;

        auto inDefs = createHeaderInDefs(st);
        ExpandExpressions(CGraph, inDefs);

        __spf_print(PRINT_PROF_INFO, "%u total substitutions\n", substitutionsCounter);
    }

    for (auto &stmt : *curFileReplacements)
    {
        for (int i = 0; i < 3; ++i)
        {
            auto expr = stmt.first->expr(i);
            if (expr)
            {
                calculate(expr);
                stmt.first->setExpression(i, *expr);
            }
        }
    }

    const string fileName = file->filename();
    int fileId = -1;
    for (SgStatement* st = file->firstStatement(); st; st = st->lexNext())
    {
        if (st->fileName() == fileName)
        {
            fileId = st->getFileId();
            break;
        }
    }

    if (fileId != -1)
        deleteAllocatedExpressionValues(fileId);
}

void runPrivateAnalysis(SgStatement *main)
{
    if (main->variant() != PROG_HEDR)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    GraphsKeeper *gk = GraphsKeeper::getGraphsKeeper();

    GraphItem *gItem = gk->getGraph(main);
    ControlFlowGraph *graph = gItem->CGraph;
    if (gItem->privateDone == false)
    {
        graph->privateAnalyzer();
        gItem->privateDone = true;
    }
}
