#include "leak_detector.h"

#include <cstdio>
#include <cstring>
#include <cstring>
#include <fstream>
#include <iostream>
#include <cstdlib>
#include <set>
#include <vector>
#include <algorithm>

#include "dvm.h"
#include "../ParallelizationRegions/ParRegions.h"
#include "array_assign_to_loop.h"
#include "SgUtils.h"
#include "expr_transform.h"
#include "graph_calls_func.h"
#include "../VerificationCode/verifications.h"

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

static void checkAlloc(SgExpression* toCheck, SgExpression*& alloc, const string& toFind)
{
    if (toCheck->lhs()->symbol()->identifier() == toFind)
        alloc = toCheck->lhs()->lhs();
}

static bool fillBounds(SgSymbol* symb, vector<tuple<SgExpression*, SgExpression*, SgExpression*>>& bounds)
{
    SgStatement* decl = declaratedInStmt(symb);

    int consistInAllocates = 0;
    SgExpression* alloc = NULL;

    for (auto& data : getAttributes<SgStatement*, SgStatement*>(decl, set<int>{ ALLOCATE_STMT }))
    {
        SgExpression* list = data->expr(0);
        if (data->variant() != ALLOCATE_STMT)
            continue;

        while (list)
        {
            SgArrayRefExp* arrayRef = isSgArrayRefExp(list->lhs());
            if (arrayRef != NULL)
            {
                if (string(OriginalSymbol(arrayRef->symbol())->identifier()) == string(symb->identifier()))
                {
                    consistInAllocates++;
                    alloc = list->lhs()->lhs();
                    break;
                }
            }
            list = list->rhs();
        }
    }

    if (consistInAllocates > 1)
        return false;

    if (IS_ALLOCATABLE(symb) && consistInAllocates == 0)
        return false;

    bool symbFound = false;
    if (consistInAllocates == 0)
    {
        const string toFind = string(symb->identifier());
        for (SgExpression* ex = decl->expr(0); ex && (alloc == NULL); ex = ex->rhs())
        {
            if (ex->lhs()->variant() == ASSGN_OP)
            {
                if (ex->lhs()->lhs() && ex->lhs()->lhs()->symbol())
                    checkAlloc(ex->lhs(), alloc, toFind);
            }
            else if (ex->lhs() && ex->lhs()->symbol())
            {
                symbFound |= (ex->lhs()->symbol()->identifier() == toFind);
                checkAlloc(ex, alloc, toFind);
            }
        }
    }

    if (symbFound && alloc == NULL)
    {
        for (SgExpression* ex = decl->expr(2); ex; ex = ex->rhs())
        {
            if (ex->lhs() && ex->lhs()->variant() == DIMENSION_OP)
            {
                alloc = ex->lhs()->lhs();
                break;
            }
        }
    }

    if (alloc == NULL)
        return false;

    for (; alloc; alloc = alloc->rhs())
    {
        const int var = alloc->lhs()->variant();
        if (var == DDOT)
        {
            if (alloc->lhs()->lhs() == NULL || alloc->lhs()->rhs() == NULL)
                return false;
            bounds.push_back(make_tuple(alloc->lhs()->lhs()->copyPtr(), alloc->lhs()->rhs()->copyPtr(), (SgExpression*)NULL));
        }
        else if (var == STAR_RANGE)
            return false;
        else
            bounds.push_back(make_tuple(new SgValueExp(1), alloc->lhs()->copyPtr(), (SgExpression*)NULL));
    }

    for (auto& bound : bounds)
        get<0>(bound) = CalculateInteger(get<0>(bound));
    return true;
}

static bool fillSectionInfo(SgExpression* subs, tuple<SgExpression*, SgExpression*, SgExpression*>& section)
{
    if (subs->lhs())
    {
        if (subs->lhs()->variant() == DDOT)
        {
            if (subs->lhs()->lhs() && subs->lhs()->lhs()->variant() == DDOT)
            {
                if (subs->lhs()->lhs()->lhs() != NULL) // low section
                    get<0>(section) = CalculateInteger(subs->lhs()->lhs()->lhs()->copyPtr());

                if (subs->lhs()->lhs()->rhs() != NULL) // high section 
                    get<1>(section) = CalculateInteger(subs->lhs()->lhs()->rhs()->copyPtr());

                if (subs->lhs()->rhs() != NULL) // step of section
                    get<2>(section) = CalculateInteger(subs->lhs()->rhs()->copyPtr());                
            }
            else
            {
                if (subs->lhs()->lhs() != NULL) // low section
                    get<0>(section) = CalculateInteger(subs->lhs()->lhs()->copyPtr());

                if (subs->lhs()->rhs() != NULL) // high section
                    get<1>(section) = CalculateInteger(subs->lhs()->rhs()->copyPtr());
            }
        }
        else
        {   // low and high sections
            get<0>(section) = CalculateInteger(subs->lhs()->copyPtr());
            get<1>(section) = CalculateInteger(subs->lhs()->copyPtr());
        }
        return true;
    }
    else
        return false;
}

static bool hasSections(SgArrayRefExp* array)
{
    const int subs = array->numberOfSubscripts();
    if (subs == 0)
        return true;
    else
    {
        for (int i = 0; i < subs; ++i)
            if (array->subscript(i)->variant() == DDOT)
                return true;
    }
    return false;
}

static map<SgFile*, map<SgStatement*, map<string, map<int, int>>>> cacheNameShiftAll; // by file and func -> [by base -> [old, new]]
static map<string, map<int, int>>* cacheNameShift; // by base -> [old, new]

static string createNewName(const string& base, int num)
{
    int nextNum = 0;
    if (cacheNameShift->count(base) && (*cacheNameShift)[base].count(num))
        nextNum = (*cacheNameShift)[base][num];
    else
    {
        nextNum = checkSymbNameAndCorrect(base, num);
        (*cacheNameShift)[base][num] = nextNum;
    }

    string ret = base + to_string(nextNum);
    return ret;
}

static void insertMainPart(SgExpression* subsL, SgFile* file, const int deep, SgExpression* shift, SgExpression* step, SgStatement* scope)
{
    auto varRef = new SgVarRefExp(findSymbolOrCreate(file, createNewName("i_", deep), SgTypeInt(), scope));
    bool isNull = false;

    if (shift->isInteger())
        if (shift->valueInteger() == 0)
            isNull = true;

    if (step)
    {
        if (step->variant() == INT_VAL)
        {
            //TODO: move negative step to DO bounds
            /*const int stepVal = abs(step->valueInteger());
            SgExpression *stepAbs = new SgValueExp(stepVal);*/
            const int stepVal = step->valueInteger();
            SgExpression* stepAbs = step;

            if (stepVal != 1)
            {
                if (isNull)
                    subsL->setLhs(*varRef * *stepAbs);
                else
                    subsL->setLhs(*varRef * *stepAbs + *shift);
            }
            else
            {
                if (isNull)
                    subsL->setLhs(*varRef);
                else
                    subsL->setLhs(*varRef + *shift);
            }
        }
        else
        {
            if (isNull)
                subsL->setLhs(*varRef * *step);
            else
                subsL->setLhs(*varRef * *step + *shift);
        }
    }
    else
    {
        if (isNull)
            subsL->setLhs(*varRef);
        else
            subsL->setLhs(*varRef + *shift);
    }
}

static bool isNonDistrArray(SgSymbol* symb, const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls)
{
    SgStatement* decl = declaratedInStmt(symb);

    SgType* type = symb->type();
    if (type && type->variant() == T_STRING)
        return false;

    DIST::Array* array = getArrayFromDeclarated(decl, OriginalSymbol(symb)->identifier());
    checkNull(array, convertFileName(__FILE__).c_str(), __LINE__);
    
    set<DIST::Array*> realRefs;
    getRealArrayRefs(array, array, realRefs, arrayLinksByFuncCalls);

    bool fullNotDistr = true;
    for (auto& real : realRefs)
        fullNotDistr &= real->IsNotDistribute();
    return fullNotDistr;
}

static void fillArgs(SgExpression* ref, 
                     vector<tuple<SgExpression*, SgExpression*, SgExpression*>>& bounds, 
                     const vector<tuple<SgExpression*, SgExpression*, SgExpression*>>& b) 
{
    for (int i = 0; ref; ref = ref->rhs(), ++i)
    {
        if (ref->lhs()->variant() == DDOT)
        {
            auto left = ref->lhs()->lhs();
            auto right = ref->lhs()->rhs();

            SgExpression* declLeft = (SgExpression*)get<0>(b[i]);
            SgExpression* declRight = (SgExpression*)get<1>(b[i]);

            if (left == NULL && right == NULL)
                bounds.push_back(make_tuple(declLeft, declRight, (SgExpression*)NULL));
            else if (left == NULL && right != NULL)
                bounds.push_back(make_tuple(declLeft, right->copyPtr(), (SgExpression*)NULL));
            else if (right == NULL && left != NULL)
                bounds.push_back(make_tuple(left->copyPtr(), declRight, (SgExpression*)NULL));
            else if (left->variant() == DDOT) 
            {
                if (left->lhs() == NULL && left->rhs() == NULL)
                    bounds.push_back(make_tuple(declLeft, declRight, right));
                else if (left->lhs() == NULL && left->rhs() != NULL)
                    bounds.push_back(make_tuple(declLeft, left->rhs(), right));
                else if (left->rhs() == NULL && left->lhs() != NULL)
                    bounds.push_back(make_tuple(left->lhs(), declRight, right));
                else
                    bounds.push_back(make_tuple(left->copyPtr(), right->copyPtr(), right));
            }
            else
                bounds.push_back(make_tuple(left->copyPtr(), right->copyPtr(), (SgExpression*)NULL));

        }
        /*else if (ref->lhs()->variant() == INT_VAL)
            bounds.push_back(make_tuple(ref->lhs()->copyPtr(), ref->lhs()->copyPtr(), (SgExpression*)NULL));
        else if (ref->lhs()->variant() == CONST_REF)
            bounds.push_back(make_tuple(ref->lhs()->copyPtr(), ref->lhs()->copyPtr(), (SgExpression*)NULL)); */
        else
            bounds.push_back(make_tuple(ref->lhs()->copyPtr(), ref->lhs()->copyPtr(), (SgExpression*)NULL));
    }

    for (auto& bound : bounds)
        get<0>(bound) = CalculateInteger(get<0>(bound));
}

static SgExpression* changeNode(SgExpression* ex, const int i, SgExpression* right) 
{
    if (i == 0) {
        SgExpression* tmp = ex->lhs()->lhs();
        ex->setLhs(*tmp);
    } else if (i == 1) {
        SgExpression* tmp = ex->rhs()->lhs();
        ex->setRhs(*tmp);
    } else if (i == 2) {
        SgExpression* tmp = ex->lhs()->rhs();
        ex->setLhs(*tmp);
    } else if (i == 4) {
        ex->lhs()->setLhs(new SgValueExp(1));
        ex->lhs()->setRhs(right);
    } else if (i == 5) {
        ex->rhs()->setLhs(new SgValueExp(1));
        ex->rhs()->setRhs(right);
    } else {
        SgExpression* tmp = ex->rhs()->rhs();
        ex->setRhs(*tmp);        
    }
    return ex;
}

static int constsInArgs(SgExpression* ex, int i, SgExpression* right) 
{
    if (i == 0) {
        while (ex) {
            if (ex->lhs()->variant() == INT_VAL || ex->lhs()->variant() == CONST_REF) {
                return 1;
            }
            else if (ex->lhs()->variant() == DDOT) {
                if (ex->lhs()->lhs() && ex->lhs()->rhs() && string(ex->lhs()->lhs()->unparse()) == string(ex->lhs()->rhs()->unparse())) {
                    ex = changeNode(ex, 0, NULL);
                    return 1;
                }
                else if (ex->lhs()->lhs() && ex->lhs()->rhs() == NULL && string(right->unparse()) == string(ex->lhs()->lhs()->unparse())) {
                    ex = changeNode(ex, 0, NULL);
                    return 1;
                }
                else if (ex->lhs()->lhs() == NULL && ex->lhs()->rhs() != NULL && ex->lhs()->rhs()->valueInteger() == 1) {
                    ex = changeNode(ex, 2, NULL);
                    return 1;
                }
                else if (ex->lhs()->lhs() == NULL && ex->lhs()->rhs() == NULL) {
                    ex = changeNode(ex, 4, right);
                    if (string(ex->lhs()->lhs()->unparse()) == string(ex->lhs()->rhs()->unparse()))
                        return 1;
                    else
                        return 0;
                }
                return 0;
            }
            else if (ex->variant() == INT_VAL || ex->variant() == CONST_REF)
                return 1;
            ex = ex->rhs();
        }
    }
    else {
        if (ex->rhs()->lhs() && ex->rhs()->rhs() && ex->rhs()->lhs()->valueInteger() - ex->rhs()->rhs()->valueInteger() == 0) {
            ex = changeNode(ex, 1, NULL);
            return 1;
        } 
        else if (ex->rhs()->lhs() && ex->rhs()->rhs() == NULL && string(right->unparse()) == string(ex->lhs()->lhs()->unparse())) {
            ex = changeNode(ex, 1, NULL);
            return 1;
        }
        else if (ex->rhs()->lhs() == NULL && ex->lhs()->rhs() && ex->rhs()->rhs()->valueInteger() == 1) {
            ex = changeNode(ex, 3, NULL);
            return 1;
        }
        else if (ex->rhs()->lhs() == NULL && ex->rhs()->rhs() == NULL) {
            ex = changeNode(ex, 5, right);
            if (string(ex->rhs()->lhs()->unparse()) == string(ex->rhs()->rhs()->unparse()))
                return 1;
            else
                return 0;
        }
        return 0;
    }
    return 0;
}

static SgForStmt* constructDoBounds(const vector<tuple<SgExpression*, SgExpression*, SgExpression*>>& leftSections,
                                    const vector<tuple<SgExpression*, SgExpression*, SgExpression*>>& rightSections,
                                    SgStatement* copy, SgFile* file, SgStatement* scope, bool mainSymbolsAreEq = false, int* nestCount = NULL)
{
    SgForStmt* retVal = NULL;
    SgStatement* body = NULL;

    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> forBounds;

    const vector<tuple<SgExpression*, SgExpression*, SgExpression*>>& mainSection = leftSections.size() ? leftSections : rightSections;
    bool onlyRight = leftSections.size() == 0 && rightSections.size() != 0;

    for (int i = 0; i < mainSection.size(); ++i)
    {
        SgExpression* maxBound = NULL;
        if (get<2>(mainSection[i]))
            maxBound = &((*(get<1>(mainSection[i])) - *get<0>(mainSection[i])) / *get<2>(mainSection[i]));
        else
            maxBound = &(*(get<1>(mainSection[i])) - *get<0>(mainSection[i]));
        //TODO: dont calculate in parallel loops
        maxBound = CalculateInteger(maxBound);

        if (onlyRight) // for SUM, WHERE, 
            forBounds.push_back(make_tuple(new SgValueExp(0), maxBound, (SgExpression*)NULL));
        else
        {
            //TODO:
            if (mainSymbolsAreEq && get<1>(leftSections[i])->isInteger() && get<1>(rightSections[i])->isInteger() &&
                                    get<1>(leftSections[i])->valueInteger() > get<1>(rightSections[i])->valueInteger())
                forBounds.push_back(make_tuple(maxBound, new SgValueExp(0), new SgValueExp(-1)));
            else
                forBounds.push_back(make_tuple(new SgValueExp(0), maxBound, (SgExpression*)NULL));
        }
    }

    int loopsCount = 0;
    for (int i = 0; i < forBounds.size(); ++i)
    {
        if (body == NULL)
            body = copy;
        else
            body = retVal;

        if (get<2>(forBounds[i])) // has step
        {
            SgExpression* step = get<2>(forBounds[i]);
            bool inverse = false;
            if (step->variant() == INT_VAL)
                if (step->valueInteger() < 0)
                    inverse = true;
            ++loopsCount;
            retVal = new SgForStmt(*findSymbolOrCreate(file, createNewName("i_", i), SgTypeInt(), scope), get<0>(forBounds[i])->copy(), get<1>(forBounds[i])->copy(), get<2>(forBounds[i])->copy(), *body);
        }
        else
        {
            ++loopsCount;
            retVal = new SgForStmt(*findSymbolOrCreate(file, createNewName("i_", i), SgTypeInt(), scope), get<0>(forBounds[i])->copy(), get<1>(forBounds[i])->copy(), *body);
        }
    }

    if (nestCount)
        nestCount[0] = loopsCount;
    return retVal;
}

static bool hasArrayRef(SgExpression* ex)
{
    bool result = false;
    if (ex)
    {
        if (isArrayRef(ex))
            result = true;

        bool lR = hasArrayRef(ex->lhs());
        bool rR = hasArrayRef(ex->rhs());

        result = result || lR || rR;
    }
    return result;
}

static SgStatement* convertFromAssignExpressionToLoop(SgStatement* assign, SgFile* file, vector<Messages>& messagesForFile,
                                                      const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls)
{
    SgStatement* result = NULL;
    
    SgArrayRefExp* leftPart = (SgArrayRefExp*)assign->expr(0);

    if (!hasSections(leftPart))
        return result;

    if (isNonDistrArray(assign->expr(0)->symbol(), arrayLinksByFuncCalls))
        return result;

    const int leftSubs = leftPart->numberOfSubscripts();
    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> leftBound;

    bool resL = fillBounds(OriginalSymbol(leftPart->symbol()), leftBound);
    
    if (!resL)
    {
        __spf_print(1, "WARN: can not convert array assign to loop on line %d\n", assign->lineNumber());
        messagesForFile.push_back(Messages(WARR, assign->lineNumber(), R94, L"can not convert array assign to loop", 2001));        
        return result;
    }

    SgForStmt* retVal = NULL;
    SgStatement* copy = assign->copyPtr();
    if (copy->label())
        copy->deleteLabel();

    SgArrayRefExp* leftArrayRef = (SgArrayRefExp*)copy->expr(0);
    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> leftArgs;
    fillArgs(leftArrayRef->lhs(), leftArgs, leftBound);
    SgExpression* subsL = leftArrayRef->lhs();

    int lIdx = 0;
    bool bodyInserted = false;

    SgStatement* scope = assign;

    while (true)
    {
        if (scope->variant() == PROG_HEDR || scope->variant() == FUNC_HEDR || scope->variant() == PROC_HEDR)
            break;
        scope = scope->controlParent();
    }

    vector<bool> fixedLeft(leftBound.size());
    for (int i = 0; i < leftBound.size(); ++i)
        fixedLeft[i] = false;

    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> leftSections;
    SgExpression* ex = subsL;
    for (int i = 0; i < leftSubs; ++i)
    {
        tuple<SgExpression*, SgExpression*, SgExpression*> bounds = leftBound[i];

        if (!fillSectionInfo(ex, bounds)) {
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
        }
        if (get<0>(bounds) && get<1>(bounds))
            if (string(get<0>(bounds)->unparse()) == string(get<1>(bounds)->unparse())) // fixed dimension value
                fixedLeft[i] = true;

        if (!fixedLeft[i])
            leftSections.push_back(bounds);
        ex = ex->rhs();
    }

    //fill default
    if (leftSubs == 0)
        leftSections = leftBound;

    __spf_print(1, "was on line %d file %s\n", assign->lineNumber(), assign->fileName());
    __spf_print(1, "%s", string(assign->unparse()).c_str());

    // create DO bounds
    if (!leftSections.size())
        leftSections = leftBound;
    retVal = constructDoBounds(leftSections, { }, copy, file, scope);

    if (leftSubs == 0)
    {
        // A = expr
        for (int i = 0; i < leftSections.size(); ++i)
        {
            SgExpression* shiftA = get<0>(leftBound[i]);
            shiftA = CalculateInteger(shiftA);
            leftArrayRef->addSubscript(*new SgVarRefExp(findSymbolOrCreate(file, createNewName("i_", i), SgTypeInt(), scope)) + *shiftA);
        }
    }
    else if (leftSubs != 0 && !hasArrayRef(assign->expr(1)))
    {
        // A( : : : ) = expr
        ex = subsL;
        for (int i = 0, freeIdx = 0; i < leftSubs; ++i, subsL = subsL->rhs())
        {
            if (!fixedLeft[i])
            {
                SgExpression* shiftA = get<0>(leftSections[freeIdx]);
                if (shiftA == NULL)
                    shiftA = get<0>(leftBound[freeIdx]);
                shiftA = CalculateInteger(shiftA);

                SgExpression* stepA = get<2>(leftSections[freeIdx]);
                if (stepA != NULL)
                    stepA = CalculateInteger(stepA);

                insertMainPart(subsL, file, freeIdx, shiftA, stepA, scope);

                ++freeIdx;
            }
        }
    }
    else
    {
        //A( : : : ) = B( : : : ) + C( : : :)
        ex = subsL;
        for (int i = 0, freeIdx = 0; i < leftSubs; ++i, subsL = subsL->rhs())
        {
            tuple<SgExpression*, SgExpression*, SgExpression*> leftSect = std::make_tuple((SgExpression*)NULL, (SgExpression*)NULL, (SgExpression*)NULL);
            if (freeIdx < leftSections.size())
                leftSect = leftSections[freeIdx];

            SgExpression* rightB = get<1>(leftArgs[i]);
            int flag = 0;
            if (!fillSectionInfo(subsL, leftSect))
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            SgExpression* shiftA = get<0>(leftSect);
            if (shiftA == NULL)
                shiftA = get<0>(leftBound[freeIdx]);
            shiftA = CalculateInteger(shiftA);

            SgExpression* stepA = get<2>(leftSect);

            if (stepA != NULL)
                stepA = CalculateInteger(stepA);
            if (subsL->rhs() && subsL->rhs()->variant() != DDOT)
                flag = constsInArgs(subsL, 0, rightB);
            else if (subsL->rhs() && subsL->rhs()->variant() == DDOT)
                flag = constsInArgs(subsL, 1, rightB);

            if (flag != 1) 
            {
                insertMainPart(subsL, file, freeIdx, shiftA, stepA, scope);
                ++freeIdx;
            }
        }

    }
    __spf_print(1, "%s", string(retVal->unparse()).c_str());
    result = retVal;
    return result;
}


static vector<SgExpression*> fillSteps(SgExpression* subsL, int leftSubs,
                                       vector<tuple<SgExpression*, SgExpression*, SgExpression*>>& leftArgs,
                                       const vector<tuple<SgExpression*, SgExpression*, SgExpression*>>& leftSections,
                                       const vector<tuple<SgExpression*, SgExpression*, SgExpression*>>& leftBound)
{
    vector<SgExpression*> steps;
    for (int i = 0, freeIdx = 0, j = 0; i < leftSubs; ++i, subsL = subsL->rhs())
    {
        if (!fillSectionInfo(subsL, leftArgs[i]))
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        SgExpression* tmp1 = NULL;
        if (freeIdx < leftSections.size())
            tmp1 = get<0>(leftSections[freeIdx]);

        SgExpression* rightB = get<1>(leftArgs[i]);
        if (tmp1 && string(get<0>(leftArgs[i])->unparse()) == string(tmp1->unparse()) && subsL->lhs()->variant() != INT_VAL)
        {
            SgExpression* shiftA = get<0>(leftSections[freeIdx]);
            if (shiftA == NULL)
                shiftA = get<0>(leftBound[freeIdx]);
            shiftA = CalculateInteger(shiftA);

            SgExpression* stepA = get<2>(leftSections[freeIdx]);
            if (stepA != NULL)
                stepA = CalculateInteger(stepA);
            steps.push_back(stepA);
            ++freeIdx;
            j++;
        }
    }

    return steps;
}

static vector<bool> insertPartOfAssign(SgFile* file, SgStatement* scope, SgExpression* subsL, int leftSubs,
                                       const vector<tuple<SgExpression*, SgExpression*, SgExpression*>>& leftArgs,
                                       const vector<tuple<SgExpression*, SgExpression*, SgExpression*>>& leftSections,
                                       const vector<tuple<SgExpression*, SgExpression*, SgExpression*>>& leftBound,
                                       const vector<SgExpression*>& stepsR, const bool symbsAreEq, 
                                       const bool isRight, const vector<bool>& changeLoopSignLeft)
{
    vector<bool> changeLoopSign;
    for (int i = 0, freeIdx = 0, j = 0; i < leftSubs; ++i, subsL = subsL->rhs())
    {
        SgExpression* tmp1 = NULL;
        if (freeIdx < leftSections.size())
            tmp1 = get<0>(leftSections[freeIdx]);

        SgExpression* rightB = get<1>(leftArgs[i]);
        if (tmp1 && string(get<0>(leftArgs[i])->unparse()) == string(tmp1->unparse()) && subsL->lhs()->variant() != INT_VAL)
        {
            SgExpression* stepA = get<2>(leftSections[freeIdx]);
            if (stepA != NULL)
                stepA = CalculateInteger(stepA);

            bool needToChange = false;
            bool singChanged = false;
            if (!symbsAreEq)
            {
                bool stepEq = false;
                if (stepA && stepsR[j])
                {
                    string sA = stepA->unparse();
                    string sR = stepsR[j]->unparse();
                    if (sA == sR)
                    {
                        stepEq = true;
                        if (stepA->isInteger() && stepA->valueInteger() < 0 && abs(stepA->valueInteger()) == 1)
                        {
                            stepA = new SgValueExp(stepA->valueInteger() * -1);
                            needToChange = true;
                        }
                    }
                }
                
                if (!stepEq)
                {
                    if (isRight)
                    {
                        if (stepA == NULL)
                        {
                            if (changeLoopSignLeft.size() && changeLoopSignLeft[j])
                                stepA = new SgValueExp(-1);
                        }
                        else if (stepA)
                        {
                            if (changeLoopSignLeft.size() && changeLoopSignLeft[j])
                            {
                                singChanged = true;
                                stepA = new SgValueExp(stepA->valueInteger() * -1);
                            }
                        }
                    }
                    else
                    {
                        if (stepA && stepA->isInteger() && stepA->valueInteger() < 0 && abs(stepA->valueInteger()) == 1)
                        {
                            stepA = new SgValueExp(stepA->valueInteger() * -1);
                            needToChange = true;
                        }
                    }
                }
            }
            changeLoopSign.push_back(needToChange);

            if (isRight && changeLoopSignLeft.size() && changeLoopSignLeft[j])
                needToChange = true;

            SgExpression* shiftA = needToChange ? get<1>(leftSections[freeIdx]) : get<0>(leftSections[freeIdx]);
            if (shiftA == NULL)
                shiftA = needToChange ? get<1>(leftBound[freeIdx]) : get<0>(leftBound[freeIdx]);

            if (isRight && needToChange && singChanged)
            {
                SgExpression* a = get<0>(leftSections[freeIdx]);
                SgExpression* b = get<1>(leftSections[freeIdx]);
                SgExpression* c = get<2>(leftSections[freeIdx]);

                checkNull(a, convertFileName(__FILE__).c_str(), __LINE__);
                checkNull(b, convertFileName(__FILE__).c_str(), __LINE__);
                checkNull(c, convertFileName(__FILE__).c_str(), __LINE__);

                SgExpression* ex = &(*a + ((*b - *a + *c) / *c - *new SgValueExp(1)) * *c - *get<1>(leftSections[freeIdx]));
                shiftA = &(*shiftA + *ex);
            }

            shiftA = CalculateInteger(shiftA);

            insertMainPart(subsL, file, j, shiftA, stepA, scope);
            ++freeIdx;
            j++;
        }
        else
        {
            if (subsL->rhs() && subsL->rhs()->variant() != DDOT)
                constsInArgs(subsL, 0, rightB);
            else if (subsL->rhs() && subsL->rhs()->variant() == DDOT)
                constsInArgs(subsL, 1, rightB);
        }
    }

    return changeLoopSign;
}

static void swapLoopBounds(SgForStmt* loop, const vector<bool>& changeLoopSign)
{
    for (int z = 0; z < changeLoopSign.size(); ++z)
    {
        checkNull(loop, convertFileName(__FILE__).c_str(), __LINE__);
        if (changeLoopSign[z])
        {
            if (loop->step() == NULL)
                loop->setStep(*new SgValueExp(-1));
            else
            {
                auto step = loop->step();
                if (!step->isInteger())
                    printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                loop->setStep(*new SgValueExp(-1 * step->valueInteger()));
            }
            loop->swapStartEnd();
        }
        loop = isSgForStmt(loop->lexNext());
    }
}

static void doReplace(SgExpression* prev, SgExpression* ex, bool isLeft, const string& s, SgExpression* with)
{
    if (ex)
    {
        if (ex->variant() == VAR_REF && ex->symbol() && ex->symbol()->identifier() == s)
        {
            if (prev == NULL)
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            if (isLeft)
                prev->setLhs(with->copyPtr());
            else
                prev->setRhs(with->copyPtr());
        }

        doReplace(ex, ex->lhs(), true, s, with);
        doReplace(ex, ex->rhs(), false, s, with);
    }
}

static SgStatement* removeOneCycleLoops(SgForStmt* retVal, const int nestCount)
{
    vector<bool> needToDel(nestCount, false);
    vector<pair<string, SgExpression*>> replace(nestCount);

    SgForStmt* loop = retVal;
    SgStatement* last = NULL;
    for (int z = 0; z < nestCount; ++z, loop = (SgForStmt*) loop->lexNext(), last = loop)
    {
        string start = loop->start()->unparse();
        string end = loop->end()->unparse();
        if (start == end)
        {
            needToDel[z] = true;
            replace[z] = make_pair(loop->doName()->identifier(), loop->start()->copyPtr());
        }
    }

    checkNull(last, convertFileName(__FILE__).c_str(), __LINE__);
    for (int z = 0; z < nestCount; ++z)
        if (needToDel[z])
            for (int p = 0; p < 3; ++p)
                doReplace(NULL, last->expr(p), true, replace[z].first, replace[z].second);

    return retVal;
}

static SgStatement* convertFromAssignToLoop(SgStatement* assign, SgFile* file, vector<Messages> &messagesForFile,
                                            const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls)
{
    SgStatement* result = NULL;

    if (assign->variant() != ASSIGN_STAT)
        return result;
    if (assign->expr(0) == NULL || assign->expr(1) == NULL)
        return result;

    if (assign->expr(1)->variant() == CONSTRUCTOR_REF) 
    {
        __spf_print(1, "WARN: can not convert array assign to loop on line %d\n", assign->lineNumber());
        messagesForFile.push_back(Messages(WARR, assign->lineNumber(), R94, L"can not convert array assign to loop", 2001));
        return result;
    }

    if (isArrayRef(assign->expr(0)) && !isArrayRef(assign->expr(1)))
    {
        result = convertFromAssignExpressionToLoop(assign, file, messagesForFile, arrayLinksByFuncCalls);        
        return result;
    }

    if (!isArrayRef(assign->expr(0)) || !isArrayRef(assign->expr(1)))
        return result;

    bool fullPrivate = true;
    if (isArrayRef(assign->expr(0)) && !isNonDistrArray(assign->expr(0)->symbol(), arrayLinksByFuncCalls))
        fullPrivate = false;
    if (isArrayRef(assign->expr(1)) && !isNonDistrArray(assign->expr(1)->symbol(), arrayLinksByFuncCalls))
        fullPrivate = false;

    if (fullPrivate)
        return result;
    
    SgArrayRefExp* leftPart = (SgArrayRefExp*)assign->expr(0);
    SgArrayRefExp* rightPart = (SgArrayRefExp*)assign->expr(1);

    if (!hasSections(leftPart) || !hasSections(rightPart))
        return result;

    const int leftSubs = leftPart->numberOfSubscripts();
    const int rightSubs = rightPart->numberOfSubscripts();

    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> leftBound;
    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> rightBound;
    bool resL = fillBounds(OriginalSymbol(leftPart->symbol()), leftBound);
    bool resR = fillBounds(OriginalSymbol(rightPart->symbol()), rightBound);
    if (!resL || !resR)
    {
        __spf_print(1, "WARN: can not convert array assign to loop on line %d\n", assign->lineNumber());
        messagesForFile.push_back(Messages(WARR, assign->lineNumber(), R94, L"can not convert array assign to loop", 2001));
        return result;
    }

    SgForStmt* retVal = NULL;
    SgStatement* retValSt = NULL;
    SgStatement* copy = assign->copyPtr();
    if (copy->label())
        copy->deleteLabel();

    SgArrayRefExp* leftArrayRef = (SgArrayRefExp*)copy->expr(0);
    SgArrayRefExp* rightArrayRef = (SgArrayRefExp*)copy->expr(1);

    SgExpression* subsL = leftArrayRef->lhs();
    SgExpression* subsR = rightArrayRef->lhs();
    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> leftArgs;
    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> rightArgs;
    fillArgs(subsL, leftArgs, leftBound);
    fillArgs(subsR, rightArgs, rightBound);
    int lIdx = 0;
    int rIdx = 0;
    bool bodyInserted = false;

    SgStatement* scope = assign;

    while (true)
    {
        if (scope->variant() == PROG_HEDR || scope->variant() == FUNC_HEDR || scope->variant() == PROC_HEDR)
            break;
        scope = scope->controlParent();
    }
    vector<bool> fixedLeft(leftBound.size()), fixedRight(rightBound.size());
    for (int i = 0; i < fixedLeft.size(); ++i)
        fixedLeft[i] = false;
    for (int i = 0; i < fixedRight.size(); ++i)
        fixedRight[i] = false;

    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> leftSections, rightSections;
    SgExpression* ex = subsL;
    for (int i = 0; i < leftSubs; ++i)
    {
        tuple<SgExpression*, SgExpression*, SgExpression*> bounds = leftArgs[i];
        if (!fillSectionInfo(ex, bounds))
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        // fixed dimension value
        if (get<0>(bounds) && get<1>(bounds)) 
            if ((string(get<0>(bounds)->unparse()) == get<1>(bounds)->unparse()) && ex->lhs()->variant() != DDOT)
                fixedLeft[i] = true;

        if (!fixedLeft[i])
            leftSections.push_back(bounds);
        ex = ex->rhs();
    }

    //fill default
    if (leftSubs == 0)
        leftSections = leftBound;

    ex = subsR;
    for (int i = 0; i < rightSubs; ++i)
    {
        tuple<SgExpression*, SgExpression*, SgExpression*> bounds = rightArgs[i];
        if (!fillSectionInfo(ex, bounds))
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        // fixed dimension value
        if (get<0>(bounds) && get<1>(bounds)) 
            if (string(get<0>(bounds)->unparse()) == get<1>(bounds)->unparse() && ex->lhs()->variant() != DDOT) 
                fixedRight[i] = true;

        if (!fixedRight[i])
            rightSections.push_back(bounds);
        ex = ex->rhs();
    }

    //fill default
    if (rightSubs == 0)
        rightSections = rightBound;

    if (leftSections.size() != rightSections.size())
    {
        __spf_print(1, "WARN: can not convert array assign to loop on line %d\n", assign->lineNumber());
        messagesForFile.push_back(Messages(WARR, assign->lineNumber(), R94, L"can not convert array assign to loop", 2001));
    }
    else
    {
        __spf_print(1, "was on line %d file %s\n", assign->lineNumber(), assign->fileName());
        __spf_print(1, "%s", string(assign->unparse()).c_str());
        
        bool symbsAreEq = false;
        if (leftPart->symbol() && rightPart->symbol())
            symbsAreEq = strcmp(OriginalSymbol(leftPart->symbol())->identifier(), OriginalSymbol(rightPart->symbol())->identifier()) == 0;
        int nestCount = 0;
        retValSt = retVal = constructDoBounds(leftSections, rightSections, copy, file, scope, symbsAreEq, &nestCount);
        
        if (leftSubs == 0 && rightSubs == 0)
        {
            // A = B
            for (int i = 0; i < leftSections.size(); ++i)
            {
                SgExpression* shiftA = get<0>(leftBound[i]);
                shiftA = CalculateInteger(shiftA);
                leftArrayRef->addSubscript(*new SgVarRefExp(findSymbolOrCreate(file, createNewName("i_", i), SgTypeInt(), scope)) + *shiftA);
            }

            for (int i = 0; i < rightSections.size(); ++i)
            {
                SgExpression* shiftB = get<0>(rightBound[i]);
                shiftB = CalculateInteger(shiftB);
                rightArrayRef->addSubscript(*new SgVarRefExp(findSymbolOrCreate(file, createNewName("i_", i), SgTypeInt(), scope)) + *shiftB);
            }
        }
        else if (leftSubs == 0 && rightSubs != 0)
        {
            // A = B( : : : )
            for (int i = 0; i < leftSections.size(); ++i)
            {
                SgExpression* shiftA = get<0>(leftBound[i]);
                shiftA = CalculateInteger(shiftA);
                leftArrayRef->addSubscript(*new SgVarRefExp(findSymbolOrCreate(file, createNewName("i_", i), SgTypeInt(), scope)) + *shiftA);
            }

            ex = subsR;
            int flag;
            for (int i = 0, freeIdx = 0; i < rightSubs; ++i, subsR = subsR->rhs())
            {
                flag = 0;
                if (!fixedRight[i])
                {
                    SgExpression* shiftB = get<0>(rightSections[freeIdx]);
                    SgExpression* rightB = get<1>(rightArgs[freeIdx]);
                    if (rightB == NULL)
                        rightB = get<1>(rightBound[freeIdx]);
                    if (shiftB == NULL)
                        shiftB = get<0>(rightBound[freeIdx]);
                    shiftB = CalculateInteger(shiftB);

                    SgExpression* stepB = get<2>(rightSections[freeIdx]);
                    if (stepB != NULL)
                        stepB = CalculateInteger(stepB);

                    if (subsR->rhs() && subsR->rhs()->variant() != DDOT)
                        flag = constsInArgs(subsR, 0, rightB);
                    else if (subsR->rhs() && subsR->rhs()->variant() == DDOT)
                        flag = constsInArgs(subsR, 1, rightB);
                    else
                        flag = constsInArgs(subsR, 0, rightB);

                    if (flag != 1)
                        insertMainPart(subsR, file, freeIdx, shiftB, stepB, scope);

                    ++freeIdx;
                }
            }
        }
        else if (leftSubs != 0 && rightSubs == 0)
        {
            // A( : : : ) = B
            ex = subsL;
            vector<bool> changeLoopSign;
            for (int i = 0, freeIdx = 0; i < leftSubs; ++i, subsL = subsL->rhs())
            {
                if (!fixedLeft[i])
                {
                    SgExpression* stepA = get<2>(leftSections[freeIdx]);
                    if (stepA != NULL)
                        stepA = CalculateInteger(stepA);

                    bool needToChange = false;
                    if (!symbsAreEq && stepA)
                        if (stepA->isInteger() && stepA->valueInteger() < 0 && abs(stepA->valueInteger()) == 1)
                        {
                            stepA = new SgValueExp(stepA->valueInteger() * -1);
                            needToChange = true;
                        }
                    changeLoopSign.push_back(needToChange);

                    SgExpression* shiftA = needToChange ? get<1>(leftSections[freeIdx]) : get<0>(leftSections[freeIdx]);
                    if (shiftA == NULL)
                        shiftA = needToChange ? get<1>(leftBound[freeIdx]) : get<0>(leftBound[freeIdx]);
                    shiftA = CalculateInteger(shiftA);

                    insertMainPart(subsL, file, freeIdx, shiftA, stepA, scope);
                    ++freeIdx;
                }
            }

            if (changeLoopSign.size() != rightSections.size())
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            for (int i = 0; i < rightSections.size(); ++i)
            {
                bool invert = changeLoopSign[i];
                SgExpression* shiftB = invert ? get<1>(rightBound[i]) : get<0>(rightBound[i]);
                shiftB = CalculateInteger(shiftB);
                if (invert)
                    rightArrayRef->addSubscript(*new SgVarRefExp(findSymbolOrCreate(file, createNewName("i_", i), SgTypeInt(), scope)) * *new SgValueExp(-1) + *shiftB);
                else
                    rightArrayRef->addSubscript(*new SgVarRefExp(findSymbolOrCreate(file, createNewName("i_", i), SgTypeInt(), scope)) + *shiftB);
            }

            std::reverse(changeLoopSign.begin(), changeLoopSign.end());
            swapLoopBounds(retVal, changeLoopSign);
        }
        else
        {
            //A( : : : ) = B( : : : )
            auto stepsA = fillSteps(subsL, leftSubs, leftArgs, leftSections, leftBound);
            auto stepsB = fillSteps(subsR, rightSubs, rightArgs, rightSections, rightBound);

            if (stepsA.size() != stepsB.size())
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            auto changeLoopSign = insertPartOfAssign(file, scope, subsL, leftSubs, leftArgs, leftSections, leftBound, stepsB, symbsAreEq, false, vector<bool>());
                 changeLoopSign = insertPartOfAssign(file, scope, subsR, rightSubs, rightArgs, rightSections, rightBound, stepsA, symbsAreEq, true, changeLoopSign);

            if (changeLoopSign.size() != nestCount)
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            std::reverse(changeLoopSign.begin(), changeLoopSign.end());
            swapLoopBounds(retVal, changeLoopSign);
            //bad solution
            //retValSt = removeOneCycleLoops(retVal, nestCount);
        }
        __spf_print(1, "%s", string(retValSt->unparse()).c_str());
    }

    result = retValSt;
    return result;
}

static SgStatement* convertFromStmtToLoop(SgStatement* assign, SgFile* file, vector<Messages>& messagesForFile,
                                          const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls)
{
    SgStatement* result = NULL;

    if (assign->variant() != ASSIGN_STAT)
        return result;

    if (assign->expr(0) == NULL || assign->expr(1) == NULL)
        return result;

    if (assign->expr(1)->lhs() == NULL || assign->expr(1)->rhs() == NULL)
        return result;

    if (!isArrayRef(assign->expr(0)) ||
        !isArrayRef(assign->expr(1)->rhs()) || 
        !isArrayRef(assign->expr(1)->lhs()))
        return result;

    if (isNonDistrArray(assign->expr(0)->symbol(), arrayLinksByFuncCalls) && 
        isNonDistrArray(assign->expr(1)->rhs()->symbol(), arrayLinksByFuncCalls) &&
        isNonDistrArray(assign->expr(1)->lhs()->symbol(), arrayLinksByFuncCalls))
        return result;

    SgArrayRefExp* leftPart = (SgArrayRefExp*)assign->expr(1)->lhs();
    SgArrayRefExp* rightPart = (SgArrayRefExp*)assign->expr(1)->rhs();
    SgArrayRefExp* assignPart = (SgArrayRefExp*)assign->expr(0);

    if (!hasSections(leftPart) || !hasSections(rightPart) || !hasSections(assignPart))
        return result;

    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> leftBound;
    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> rightBound;
    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> assignBound;

    bool resL = fillBounds(OriginalSymbol(leftPart->symbol()), leftBound);
    bool resR = fillBounds(OriginalSymbol(rightPart->symbol()), rightBound);
    bool resA = fillBounds(OriginalSymbol(assignPart->symbol()), assignBound);
    if (!resL || !resR || !resA)
        return result;

    SgForStmt* retVal = NULL;
    SgStatement* copy = assign->copyPtr();
    if (copy->label())
        copy->deleteLabel();

    SgArrayRefExp* leftArrayRef = (SgArrayRefExp*)copy->expr(1)->lhs();
    SgArrayRefExp* rightArrayRef = (SgArrayRefExp*)copy->expr(1)->rhs();
    SgArrayRefExp* assignArrayRef = (SgArrayRefExp*)copy->expr(0);

    SgExpression* subsL = leftArrayRef->lhs();
    SgExpression* subsR = rightArrayRef->lhs();
    SgExpression* subsA = assignArrayRef->lhs();

    int lIdx = 0;
    int rIdx = 0;
    int aIdx = 0;
    bool bodyInserted = false;

    SgStatement* scope = assign;

    const int leftSubs = leftPart->numberOfSubscripts();
    const int rightSubs = rightPart->numberOfSubscripts();
    const int assignSubs = assignPart->numberOfSubscripts();

    while (true)
    {
        if (scope->variant() == PROG_HEDR || scope->variant() == FUNC_HEDR || scope->variant() == PROC_HEDR)
            break;
        scope = scope->controlParent();
    }

    vector<bool> fixedLeft(leftBound.size()), fixedRight(rightBound.size()), fixedAssign(assignBound.size());
    for (int i = 0; i < leftBound.size(); ++i)
        fixedLeft[i] = false;
    for (int i = 0; i < fixedRight.size(); ++i)
        fixedRight[i] = false;
    for (int i = 0; i < fixedAssign.size(); ++i)
        fixedAssign[i] = false;

    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> leftSections, rightSections, assignSections;

    SgExpression* ex = subsL;
    for (int i = 0; i < leftSubs; ++i)
    {
        tuple<SgExpression*, SgExpression*, SgExpression*> bounds = leftBound[lIdx];
        if (!fillSectionInfo(ex, bounds))
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        if (get<0>(bounds) && get<1>(bounds))
            if (string(get<0>(bounds)->unparse()) == string(get<1>(bounds)->unparse())) // fixed dimension value
                fixedLeft[i] = true;

        if (!fixedLeft[i])
            leftSections.push_back(bounds);
        ex = ex->rhs();
    }

    //fill default
    if (leftSubs == 0)
        leftSections = leftBound;

    ex = subsR;
    for (int i = 0; i < rightSubs; ++i)
    {
        tuple<SgExpression*, SgExpression*, SgExpression*> bounds = rightBound[rIdx];
        if (!fillSectionInfo(ex, bounds))
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        if (get<0>(bounds) && get<1>(bounds))
            if (string(get<0>(bounds)->unparse()) == string(get<1>(bounds)->unparse())) // fixed dimension value
                fixedRight[i] = true;

        if (!fixedRight[i])
            rightSections.push_back(bounds);
        ex = ex->rhs();
    }

    //fill default
    if (rightSubs == 0)
        rightSections = rightBound;

    ex = subsA;
    for (int i = 0; i < assignSubs; ++i)
    {
        tuple<SgExpression*, SgExpression*, SgExpression*> bounds = assignBound[aIdx];
        if (!fillSectionInfo(ex, bounds))
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        if (get<0>(bounds) && get<1>(bounds))
            if (string(get<0>(bounds)->unparse()) == string(get<1>(bounds)->unparse())) // fixed dimension value
                fixedAssign[i] = true;

        if (!fixedAssign[i])
            assignSections.push_back(bounds);
        ex = ex->rhs();
    }

    //fill default
    if (assignSubs == 0)
        assignSections = assignBound;

    if (leftSections.size() != rightSections.size() ||
        leftSections.size() != assignSections.size() ||
        rightSections.size() != assignSections.size())
    {
        __spf_print(1, "WARN: can not convert array assign to loop on line %d\n", assign->lineNumber());
        messagesForFile.push_back(Messages(WARR, assign->lineNumber(), R95, L"can not convert array assign to loop", 2001));
    }
    else
    {
        __spf_print(1, "was on line %d file %s\n", assign->lineNumber(), assign->fileName());
        __spf_print(1, "%s", string(assign->unparse()).c_str());

        if (!leftSections.size())
            leftSections = leftBound;

        if (!rightSections.size())
            rightSections = rightBound;

        if (!assignSections.size())
            assignSections = assignBound;

        bool symbsAreEq = false;
        if (leftPart->symbol() && rightPart->symbol())
            symbsAreEq  = strcmp(OriginalSymbol(leftPart->symbol())->identifier(), OriginalSymbol(rightPart->symbol())->identifier()) == 0;
        retVal = constructDoBounds(leftSections, rightSections, copy, file, scope, symbsAreEq);

        if (leftSubs == 0)
        {
            for (int i = 0; i < leftSections.size(); ++i)
            {
                SgExpression* shiftA = get<0>(leftBound[i]);
                shiftA = CalculateInteger(shiftA);
                leftArrayRef->addSubscript(*new SgVarRefExp(findSymbolOrCreate(file, createNewName("i_", i), SgTypeInt(), scope)) + *shiftA);
            }
        }
        else
        {
            ex = subsL;
            for (int i = 0, freeIdx = 0; i < leftSubs; ++i, subsL = subsL->rhs())
            {
                if (!fixedLeft[i])
                {
                    SgExpression* shiftA = get<0>(leftSections[freeIdx]);
                    if (shiftA == NULL)
                        shiftA = get<0>(leftBound[freeIdx]);
                    shiftA = CalculateInteger(shiftA);

                    SgExpression* stepA = get<2>(leftSections[freeIdx]);
                    if (stepA != NULL)
                        stepA = CalculateInteger(stepA);

                    insertMainPart(subsL, file, freeIdx, shiftA, stepA, scope);

                    ++freeIdx;
                }
            }
        }

        if (rightSubs == 0)
        {
            for (int i = 0; i < rightSections.size(); ++i)
            {
                SgExpression* shiftB = get<0>(rightBound[i]);
                shiftB = CalculateInteger(shiftB);
                rightArrayRef->addSubscript(*new SgVarRefExp(findSymbolOrCreate(file, createNewName("i_", i), SgTypeInt(), scope)) + *shiftB);
            }
        }
        else
        {
            ex = subsR;
            for (int i = 0, freeIdx = 0; i < rightSubs; ++i, subsR = subsR->rhs())
            {
                if (!fixedRight[i])
                {
                    SgExpression* shiftB = get<0>(rightSections[freeIdx]);
                    if (shiftB == NULL)
                        shiftB = get<0>(rightBound[freeIdx]);
                    shiftB = CalculateInteger(shiftB);

                    SgExpression* stepB = get<2>(rightSections[freeIdx]);
                    if (stepB != NULL)
                        stepB = CalculateInteger(stepB);

                    insertMainPart(subsR, file, freeIdx, shiftB, stepB, scope);

                    ++freeIdx;
                }
            }
        }

        if (assignSubs == 0)
        {
            for (int i = 0; i < assignSections.size(); ++i)
            {
                SgExpression* shiftC = get<0>(assignBound[i]);
                shiftC = CalculateInteger(shiftC);
                assignArrayRef->addSubscript(*new SgVarRefExp(findSymbolOrCreate(file, createNewName("i_", i), SgTypeInt(), scope)) + *shiftC);
            }
        }
        else
        {
            ex = subsA;
            for (int i = 0, freeIdx = 0; i < assignSubs; ++i, subsA = subsA->rhs())
            {
                if (!fixedAssign[i])
                {
                    SgExpression* shiftC = get<0>(assignSections[freeIdx]);
                    if (shiftC == NULL)
                        shiftC = get<0>(assignBound[freeIdx]);
                    shiftC = CalculateInteger(shiftC);

                    SgExpression* stepC = get<2>(assignSections[freeIdx]);
                    if (stepC != NULL)
                        stepC = CalculateInteger(stepC);

                    insertMainPart(subsA, file, freeIdx, shiftC, stepC, scope);

                    ++freeIdx;
                }
            }
        }

    }

    __spf_print(1, "%s", string(retVal->unparse()).c_str());

    result = retVal;
    return result;
}


static SgStatement* convertFromSumToLoop(SgStatement * assign, SgFile * file, vector<Messages> &messagesForFile,
                                         const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls)
{
    SgStatement* result = NULL;

    if (assign->expr(0) == NULL || assign->expr(1) == NULL)
        return result;

    if (assign->expr(1)->lhs() == NULL || assign->expr(1)->lhs()->lhs() == NULL ||
        isArrayRef(assign->expr(1)->lhs()->lhs()))
        return result;

    if (isNonDistrArray(assign->expr(1)->lhs()->lhs()->symbol(), arrayLinksByFuncCalls))
        return result;

    SgForStmt* retVal = NULL;
    SgStatement* copy = assign->copyPtr();
    if (copy->label())
        copy->deleteLabel();

    copy->setExpression(1, assign->expr(1)->lhs()->lhs()->copy());

    SgExpression* leftPart = assign->expr(0);
    SgArrayRefExp* rightPart = (SgArrayRefExp*)assign->expr(1)->lhs()->lhs();
    const int Subs = rightPart->numberOfSubscripts();

    if (!hasSections(rightPart))
        return result;

    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> rightBound;

    bool resR = fillBounds(OriginalSymbol(rightPart->symbol()), rightBound);
    if (!resR)
        return result;

    SgArrayRefExp* rightArrayRef = (SgArrayRefExp*)copy->expr(1);
    SgExpression* subsR = rightArrayRef->lhs();

    int rIdx = 0;
    bool bodyInserted = false;

    SgStatement* scope = assign;

    const int rightSubs = rightPart->numberOfSubscripts();

    while (true)
    {
        if (scope->variant() == PROG_HEDR || scope->variant() == FUNC_HEDR || scope->variant() == PROC_HEDR)
            break;
        scope = scope->controlParent();
    }

    vector<bool> fixedRight(rightBound.size());
    for (int i = 0; i < fixedRight.size(); ++i)
        fixedRight[i] = false;

    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> rightSections;

    SgExpression* ex = subsR;
    for (int i = 0; i < rightSubs; ++i)
    {
        tuple<SgExpression*, SgExpression*, SgExpression*> bounds = rightBound[rIdx];
        if (!fillSectionInfo(ex, bounds))
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        if (get<0>(bounds) && get<1>(bounds))
            if (string(get<0>(bounds)->unparse()) == string(get<1>(bounds)->unparse())) // fixed dimension value
                fixedRight[i] = true;

        if (!fixedRight[i])
            rightSections.push_back(bounds);
        ex = ex->rhs();
    }

    //fill default
    if (rightSubs == 0)
        rightSections = rightBound;

    __spf_print(1, "was on line %d file %s\n", assign->lineNumber(), assign->fileName());
    __spf_print(1, "%s", string(assign->unparse()).c_str());

    if (!rightSections.size())
        rightSections = rightBound;
    retVal = constructDoBounds({ }, rightSections, copy, file, scope);

    if (rightSubs == 0)
    {
        for (int i = 0; i < rightSections.size(); ++i)
        {
            SgExpression* shiftB = get<0>(rightBound[i]);
            shiftB = CalculateInteger(shiftB);
            rightArrayRef->addSubscript(*new SgVarRefExp(findSymbolOrCreate(file, createNewName("i_", i), SgTypeInt(), scope)) + *shiftB);
        }
    }
    else
    {
        ex = subsR;
        for (int i = 0, freeIdx = 0; i < rightSubs; ++i, subsR = subsR->rhs())
        {
            if (!fixedRight[i])
            {
                SgExpression* shiftB = get<0>(rightSections[freeIdx]);
                if (shiftB == NULL)
                    shiftB = get<0>(rightBound[freeIdx]);
                shiftB = CalculateInteger(shiftB);

                SgExpression* stepB = get<2>(rightSections[freeIdx]);
                if (stepB != NULL)
                    stepB = CalculateInteger(stepB);

                insertMainPart(subsR, file, freeIdx, shiftB, stepB, scope);

                ++freeIdx;
            }
        }
    }


    SgAssignStmt* init = new SgAssignStmt(*(assign->expr(0)), *(new SgValueExp(0)));   //      sum = 0 

    SgExpression* newRightPart = new SgExpression(ADD_OP);
    newRightPart->setLhs(copy->expr(0)->copyPtr());
    newRightPart->setRhs(copy->expr(1));
    copy->setExpression(1, *newRightPart);

    result = new SgIfStmt(*new SgValueExp(true));
    result->insertStmtAfter(*retVal, *result);
    result->insertStmtAfter(*init, *result);

    __spf_print(1, "%s\n", " ----------- ");
    __spf_print(1, "%s", string(result->unparse()).c_str());

    // add SPF ANALYSIS REDUCTION(SUM(<var>)) after convertion
    SgStatement* redDir = new SgStatement(SPF_ANALYSIS_DIR);
    SgExpression* list = new SgExpression(EXPR_LIST,
        new SgExpression(REDUCTION_OP,
            new SgExpression(EXPR_LIST,
                new SgExpression(ARRAY_OP, new SgKeywordValExp("sum"), new SgVarRefExp(init->expr(0)->symbol())))));
    redDir->setExpression(0, list);
    retVal->addAttribute(SPF_ANALYSIS_DIR, redDir, sizeof(SgStatement));

    return result;
}

static SgStatement* convertFromWhereToLoop(SgStatement * assign, SgFile * file, vector<Messages> &messagesForFile)
{
    SgStatement* result = NULL;

    if (assign->expr(0) == NULL || assign->expr(1) == NULL)
        return result;

    if (assign->expr(0)->lhs() == NULL ||
        isArrayRef(assign->expr(0)->lhs()) ||
        isArrayRef(assign->expr(1)))
        return result;

    /*
    if (!strcmp(assign->expr(0)->lhs()->symbol()->identifier(),
                assign->expr(1)->symbol()->identifier()))
        return result;
    */
    __spf_print(1, "%s\n", " ----------- ");

    SgForStmt* retVal = NULL;
    SgStatement* copy = assign->copyPtr();
    if (copy->label())
        copy->deleteLabel();

    SgExpression* leftPart = assign->expr(0);
    SgArrayRefExp* rightPart = (SgArrayRefExp*)assign->expr(1);
    const int Subs = rightPart->numberOfSubscripts();

    if (!hasSections(rightPart))
        return result;

    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> rightBound;

    bool resR = fillBounds(OriginalSymbol(rightPart->symbol()), rightBound);
    if (!resR)
        return result;

    SgArrayRefExp* rightArrayRef = (SgArrayRefExp*)copy->expr(1);
    SgExpression* subsR = rightArrayRef->lhs();

    int rIdx = 0;
    bool bodyInserted = false;

    SgStatement* scope = assign;

    const int rightSubs = rightPart->numberOfSubscripts();

    while (true)
    {
        if (scope->variant() == PROG_HEDR || scope->variant() == FUNC_HEDR || scope->variant() == PROC_HEDR)
            break;
        scope = scope->controlParent();
    }

    vector<bool> fixedRight(rightBound.size());
    for (int i = 0; i < fixedRight.size(); ++i)
        fixedRight[i] = false;

    vector<tuple<SgExpression*, SgExpression*, SgExpression*>> rightSections;

    SgExpression* ex = subsR;
    for (int i = 0; i < rightSubs; ++i)
    {
        tuple<SgExpression*, SgExpression*, SgExpression*> bounds = rightBound[rIdx];
        if (!fillSectionInfo(ex, bounds))
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

        if (get<0>(bounds) && get<1>(bounds))
            if (string(get<0>(bounds)->unparse()) == string(get<1>(bounds)->unparse())) // fixed dimension value
                fixedRight[i] = true;

        if (!fixedRight[i])
            rightSections.push_back(bounds);
        ex = ex->rhs();
    }

    //fill default
    if (rightSubs == 0)
        rightSections = rightBound;

    __spf_print(1, "was on line %d file %s\n", assign->lineNumber(), assign->fileName());
    __spf_print(1, "%s", string(assign->unparse()).c_str());
        
    if (!rightSections.size())
        rightSections = rightBound;
    retVal = constructDoBounds({ }, rightSections, copy, file, scope);

    if (rightSubs == 0)
    {
        for (int i = 0; i < rightSections.size(); ++i)
        {
            SgExpression* shiftB = get<0>(rightBound[i]);
            shiftB = CalculateInteger(shiftB);
            rightArrayRef->addSubscript(*new SgVarRefExp(findSymbolOrCreate(file, createNewName("i_", i), SgTypeInt(), scope)) + *shiftB);
        }
    }
    else
    {
        ex = subsR;
        for (int i = 0, freeIdx = 0; i < rightSubs; ++i, subsR = subsR->rhs())
        {
            if (!fixedRight[i])
            {
                SgExpression* shiftB = get<0>(rightSections[freeIdx]);
                if (shiftB == NULL)
                    shiftB = get<0>(rightBound[freeIdx]);
                shiftB = CalculateInteger(shiftB);

                SgExpression* stepB = get<2>(rightSections[freeIdx]);
                if (stepB != NULL)
                    stepB = CalculateInteger(stepB);

                insertMainPart(subsR, file, freeIdx, shiftB, stepB, scope);

                ++freeIdx;
            }
        }
    }

    SgIfStmt* ret = (SgIfStmt*) new SgStatement(EXPR_IF);

    ret->setExpression(0, *retVal->lexNext()->expr(0));
    ret->expr(0)->setLhs(retVal->lexNext()->expr(1));
    ret->setExpression(1, *retVal->lexNext()->expr(1));
    ret->setExpression(2, *retVal->lexNext()->expr(2));

    retVal->setLexNext(*ret);

    __spf_print(1, "%s\n", " ----------- ");
    __spf_print(1, "%s", string(retVal->unparse()).c_str());

    result = retVal;
    return result;
}

bool notDeletedVectorAssign(SgStatement* st)
{
    if (!st)
        return false;

    SgExpression* rPart = st->expr(1);
    if (!rPart)
        return false;

    const int var = rPart->variant();
    return (var == ADD_OP || var == MULT_OP || var == DIV_OP || var == INT_VAL || var == FLOAT_VAL || var == DOUBLE_VAL ||
            var == BOOL_VAL || var == CHAR_VAL || var == STRING_VAL || var == COMPLEX_VAL || var == SUBT_OP || var == MINUS_OP ||
            var == FUNC_CALL && !strcmp(rPart->symbol()->identifier(), "sum") || 
            isArrayRef(rPart) || var == VAR_REF || var == CONST_REF);
}

static SgStatement* runConversion(SgStatement* st, SgFile* file, vector<Messages>& messagesForFile, 
                                  const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls)
{
    int oldVar = st->variant();
    st->setVariant(abs(st->variant()));

    SgStatement* conv = NULL;
    if (st->expr(1)->variant() == FUNC_CALL)
    {
        const string fName = st->expr(1)->symbol()->identifier();
        if (fName == "sum")
            conv = convertFromSumToLoop(st, file, messagesForFile, arrayLinksByFuncCalls);
    }
    else
    {
        if ((st->expr(1)->variant() == ADD_OP || st->expr(1)->variant() == MULT_OP || st->expr(1)->variant() == SUBT_OP))
            conv = convertFromStmtToLoop(st, file, messagesForFile, arrayLinksByFuncCalls);
        else
        {
            if (st->variant() == WHERE_NODE)
                conv = convertFromWhereToLoop(st, file, messagesForFile);
            else
                conv = convertFromAssignToLoop(st, file, messagesForFile, arrayLinksByFuncCalls);
        }
    }

    //move label if converted
    if (conv)
        moveLabelBefore(st);

    st->setVariant(oldVar);
    return conv;
}

// functionality: convert A[(...)] = B[(...)] to loop 
//                move (create copy) init assigns in DECL before the first executable
//                move SPF ANALYSIS PARAMETER to assigns
void convertFromAssignToLoop(SgFile *file, const vector<ParallelRegion*>& regions, vector<Messages> &messagesForFile,
                             const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls)
{
    if (cacheNameShiftAll.find(file) == cacheNameShiftAll.end())
        cacheNameShiftAll[file] = map<SgStatement*, map<string, map<int, int>>>();

    int funcNum = file->numberOfFunctions();
    auto useMapMod = createMapOfModuleUses(file);

    vector<SgStatement*> modules;
    findModulesInFile(file, modules);
    map<string, SgStatement*> modMap;
    for (auto& elem : modules)
        modMap[elem->symbol()->identifier()] = elem;

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

        if (cacheNameShiftAll[file].find(st) == cacheNameShiftAll[file].end())
            cacheNameShiftAll[file][st] = map<string, map<int, int>>();
        cacheNameShift = &cacheNameShiftAll[file][st];

        SgStatement* lastNode = st->lastNodeOfStmt();

        vector<pair<SgStatement*, SgStatement*>> toMove;

        SgStatement* firstExec = NULL;
        SgStatement* controlParFristExec = NULL;

        for (SgStatement* st1 = file->functions(i); st1 != lastNode && !firstExec; st1 = st1->lexNext())
        {
            const int var = st1->variant();
            if (isSgExecutableStatement(st1))
                firstExec = st1;
            if ((var == CONTAINS_STMT || var == PROC_HEDR || var == FUNC_HEDR) && st1 != file->functions(i))
                break;
        }

        if (firstExec)
            controlParFristExec = firstExec->controlParent();

        set<SgStatement*> useMods;
        map<string, set<SgSymbol*>> byUse = moduleRefsByUseInFunction(st);

        map<string, SgStatement*> derivedTypesDecl = createDerivedTypeDeclMap(st);
        bool isInTypeDecl = false;
        for (; st != lastNode; st = st->lexNext())
        {
            if (st->variant() == CONTAINS_STMT)
                break;

            if (st->variant() == STRUCT_DECL)
                isInTypeDecl = true;
            if (st->variant() == CONTROL_END && st->controlParent()->variant() == STRUCT_DECL)
                isInTypeDecl = false;

            //move init assigns before the first statement
            // skip if in TYPE-END_TYPE construction
            if (firstExec && isSgDeclarationStatement(st) && !isInTypeDecl)
            {
                SgVarDeclStmt* declStat = (SgVarDeclStmt*)st;
                for (int k = 0; k < declStat->numberOfSymbols(); ++k)
                {
                    SgExpression* completeInit = declStat->completeInitialValue(k);
                    if (completeInit)
                    {
                        auto copyLeft = completeInit->lhs()->copyPtr();
                        auto copyRight = completeInit->rhs()->copyPtr();
                        SgStatement* toAdd = new SgStatement(ASSIGN_STAT, NULL, NULL, copyLeft, copyRight, NULL);

                        if (isDerivedAssign(toAdd))
                            replaceDerivedAssigns(file, toAdd, firstExec, derivedTypesDecl);
                        else
                        {
                            toAdd->setFileId(controlParFristExec->getFileId());
                            toAdd->setProject(controlParFristExec->getProject());

                            firstExec->insertStmtBefore(*toAdd, *controlParFristExec);

                            toAdd->setlineNumber(getNextNegativeLineNumber());
                            toAdd->setLocalLineNumber(st->lineNumber());
                        }
                        toMove.push_back(make_pair(st, toAdd));
                    }
                }
            }

            //move SPF ANALYSIS PARAMETER before current statement
            vector<SgStatement*> parameters;
            for (auto& attr : getAttributes<SgStatement*, SgStatement*>(st, set<int>{ SPF_ANALYSIS_DIR }))
            {
                SgExpression* exprList = attr->expr(0);
                while (exprList)
                {
                    if (exprList->lhs()->variant() == SPF_PARAMETER_OP)
                    {
                        SgExpression* list = exprList->lhs()->lhs();
                        while (list)
                        {
                            if (list->lhs()->variant() != ASSGN_OP)
                                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
                            parameters.push_back(new SgStatement(ASSIGN_STAT, NULL, NULL, list->lhs()->lhs()->copyPtr(), list->lhs()->rhs()->copyPtr(), NULL));

                            parameters.back()->setFileId(st->getFileId());
                            parameters.back()->setProject(st->getProject());

                            parameters.back()->setlineNumber(getNextNegativeLineNumber());
                            parameters.back()->setLocalLineNumber(attr->lineNumber());

                            list = list->rhs();
                        }
                    }
                    exprList = exprList->rhs();
                }
            }

            for (auto& par : parameters)
            {
                st->insertStmtBefore(*par, *st->controlParent());
                auto inserted = st->lexPrev();
                inserted->setUnparseIgnore(true);
                inserted->addAttribute(SPF_PARAMETER_OP, st, sizeof(SgStatement*));
            }

            if (firstExec && st->variant() == USE_STMT)
                useMods.insert(st);

            //TODO: change names by use
            //TODO: create init of decl for all USE map
            if (firstExec && isSgExecutableStatement(st))
            {
                for (auto& elem : useMods)
                {
                    auto it = modMap.find(elem->symbol()->identifier());
                    if (it == modMap.end())
                        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

                    for (SgStatement* stM = it->second->lexNext(); stM != it->second->lastNodeOfStmt(); stM = stM->lexNext())
                    {
                        if (isSgExecutableStatement(stM))
                            break;
                        const int var = stM->variant();
                        if (var == PROC_HEDR || var == FUNC_HEDR)
                            break;

                        if (isSgDeclarationStatement(stM))
                        {
                            SgVarDeclStmt* declStat = (SgVarDeclStmt*)stM;
                            bool ifHasParameterAttr = false;

                            for (int z = 0; z < declStat->numberOfAttributes(); ++z)
                                if (declStat->attribute(z)->variant() == PARAMETER_OP)
                                    ifHasParameterAttr = true;

                            if (ifHasParameterAttr)
                            {
                                for (int k = 0; k < declStat->numberOfSymbols(); ++k)
                                {
                                    SgExpression* completeInit = declStat->completeInitialValue(k);
                                    if (completeInit)
                                    {
                                        SgStatement* toAdd = new SgStatement(ASSIGN_STAT, NULL, NULL, completeInit->lhs()->copyPtr(), completeInit->rhs()->copyPtr(), NULL);
                                        toAdd->setFileId(controlParFristExec->getFileId());
                                        toAdd->setProject(controlParFristExec->getProject());

                                        firstExec->insertStmtBefore(*toAdd, *controlParFristExec);

                                        toMove.push_back(make_pair(elem, toAdd));
                                        toAdd->setlineNumber(getNextNegativeLineNumber());
                                        toAdd->setLocalLineNumber(elem->lineNumber());
                                    }
                                }
                            }
                        }
                    }
                }
                firstExec = NULL;
            }

            if (getRegionByLine(regions, st->fileName(), st->lineNumber()) && 
                (st->variant() == ASSIGN_STAT || st->variant() == WHERE_NODE))
            {
                SgStatement* conv = runConversion(st, file, messagesForFile, arrayLinksByFuncCalls);

                if (conv)
                {
                    auto currFile = st->getFileId();
                    auto currProj = st->getProject();

                    st->insertStmtBefore(*conv, *st->controlParent());
                    toMove.push_back(make_pair(st, conv));

                    if (conv->variant() != ASSIGN_STAT)
                    {
                        SgStatement* end = conv->lastNodeOfStmt();
                        for (SgStatement* st1 = conv; st1 != end; st1 = st1->lexNext())
                        {
                            st1->setlineNumber(getNextNegativeLineNumber());
                            st1->setLocalLineNumber(st->lineNumber());
                            st1->setFileId(currFile);
                            st1->setProject(currProj);
                        }
                        end->setlineNumber(getNextNegativeLineNumber());
                        end->setLocalLineNumber(st->lineNumber());
                        end->setFileId(currFile);
                        end->setProject(currProj);
                    }
                    else
                    {
                        conv->setlineNumber(getNextNegativeLineNumber());
                        conv->setLocalLineNumber(st->lineNumber());
                        conv->setFileId(currFile);
                        conv->setProject(currProj);
                    }

                    //TODO: may be need to move more attributes
                    //move only SPF_ANALYSIS_DIR attributes
                    for (auto& attr : getAttributes<SgStatement*, SgStatement*>(st, set<int>{ SPF_ANALYSIS_DIR }))
                        conv->addAttribute(SPF_ANALYSIS_DIR, attr, sizeof(SgStatement*));
                }
            }
        }

        for (auto& move : toMove)
        {
            move.first->addAttribute(ASSIGN_STAT, move.second, sizeof(SgStatement*));

            char* comments = move.first->comments();
            if (comments)
                move.second->setComments(comments);
        }
    }
}

static bool isUnderParallelLoop(SgStatement * st)
{
    bool isUnder = false;

    while (st)
    {
        const int var = st->variant();
        if (var == FUNC_HEDR || var == PROG_HEDR || var == PROC_HEDR)
            break;

        if (var == FOR_NODE)
        {
            auto prev = st->lexPrev();
            if (prev->variant() == DVM_PARALLEL_ON_DIR)
            {
                isUnder = true;
                break;
            }
        }
        st = st->controlParent();
    }
    return isUnder;
}

static bool hasParallelDir(SgStatement * st)
{
    bool has = false;
    SgStatement* last = st->lastNodeOfStmt();
    while (st != last)
    {
        int var = st->variant();
        if ((var == FOR_NODE || var == -FOR_NODE) && st->lexPrev()->variant() == DVM_PARALLEL_ON_DIR)
            has = true;
        st = st->lexNext();
    }
    return has;
}

static void addToDeclSet(SgExpression* exp, set<string> &symbolSet)
{
    if (exp)
    {
        if (exp->symbol())
            symbolSet.insert(OriginalSymbol(exp->symbol())->identifier());

        if (exp->lhs())
            addToDeclSet(exp->lhs(), symbolSet);
        if (exp->rhs())
            addToDeclSet(exp->rhs(), symbolSet);
    }
}

void restoreConvertedLoopForParallelLoops(SgFile* file, const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls, bool reversed)
{
    if (cacheNameShiftAll.find(file) == cacheNameShiftAll.end())
        cacheNameShiftAll[file] = map<SgStatement*, map<string, map<int, int>>>();

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

        if (cacheNameShiftAll[file].find(st) == cacheNameShiftAll[file].end())
            cacheNameShiftAll[file][st] = map<string, map<int, int>>();
        cacheNameShift = &cacheNameShiftAll[file][st];

        map<string, SgSymbol*> newDeclarations;
        set<string> declaratedInFunction;

        SgStatement* lastDeclarated = NULL;
        for (st = file->functions(i); st != lastNode; st = st->lexNext())
        {
            if (isSgExecutableStatement(st))
                break;
            if (st->variant() == CONTAINS_STMT)
                break;

            if (st->variant() != DATA_DECL && st->variant() != FORMAT_STAT)
                for (int i = 0; i < 3; ++i)
                    addToDeclSet(st->expr(i), declaratedInFunction);

            if (st->fileName() == file->filename())
                lastDeclarated = st;
        }

        vector<SgStatement*> toDel;
        vector<Messages> messagesForFileTmp;
        for (st = file->functions(i); st != lastNode; st = st->lexNext())
        {
            for (auto& data : getAttributes<SgStatement*, SgStatement*>(st, set<int>{ ASSIGN_STAT }))
            {
                if (reversed)
                {
                    if (data->lineNumber() < 0)
                    {
                        data->setVariant(abs(data->variant()));

                        auto secondConv = runConversion(st, file, messagesForFileTmp, arrayLinksByFuncCalls);
                        if (secondConv && notDeletedVectorAssign(st))
                            st->setVariant(-abs(st->variant()));
                        else
                            st->setVariant(abs(st->variant()));
                    }
                }
                else
                {
                    if (data->lineNumber() < 0)
                    {
                        auto secondConv = runConversion(st, file, messagesForFileTmp, arrayLinksByFuncCalls);
                        if (isUnderParallelLoop(st) || secondConv && notDeletedVectorAssign(st))
                        {
                            st->setVariant(-abs(st->variant()));
                            auto prev = st->lexPrev();
                            if (prev->variant() == DVM_REMOTE_ACCESS_DIR)
                                toDel.push_back(prev);

                            data->setVariant(abs(data->variant()));

                            if (data->variant() == FOR_NODE || data->variant() == IF_NODE)
                            {
                                for (auto st_loc = data; st_loc != data->lastNodeOfStmt(); st_loc = st_loc->lexNext())
                                    if (st_loc->variant() == FOR_NODE)
                                        newDeclarations[st_loc->symbol()->identifier()] = st_loc->symbol();
                            }
                            else if (data->variant() == ASSIGN_STAT && data->lexNext()->variant() == FOR_NODE)
                            {
                                for (auto st_loc = data->lexNext(); st_loc != data->lexNext()->lastNodeOfStmt(); st_loc = st_loc->lexNext())
                                    if (st_loc->variant() == FOR_NODE)
                                        newDeclarations[st_loc->symbol()->identifier()] = st_loc->symbol();
                            }
                        }
                        else
                            data->setVariant(-abs(data->variant()));
                    }
                }
            }
        }

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

        //insert new declaration of symbols for converted loops
        for (auto& toDecl : newDeclarations)
        {
            auto found = declaratedInFunction.find(toDecl.first);
            if (found == declaratedInFunction.end())
            {
                declaratedInFunction.insert(toDecl.first);
                auto toInsert = toDecl.second->makeVarDeclStmt();
                lastDeclarated->insertStmtAfter(*toInsert, *st->controlParent());
            }
        }
    }
}

static map<SgStatement*, set<string>> newSymbDeclaredInFunction;

bool needDeclare(SgStatement* start, SgStatement* end)
{
    bool isImplicit = false;
    for (auto st = start; st != end && st; st = st->lexNext())
    {
        if (isSgExecutableStatement(st) && !isDVM_stat(st) && !isSPF_stat(st))
            break;

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

        if (st->variant() == IMPL_DECL)
            isImplicit = true;

        if (st->variant() == VAR_DECL || st->variant() == VAR_DECL_90)
        {
            SgExpression* list = st->expr(0);
            while (list)
            {
                if (list->lhs()->variant() == VAR_REF)
                    newSymbDeclaredInFunction[start].insert(list->lhs()->symbol()->identifier());
                list = list->rhs();
            }
        }
    }
    return isImplicit;
}

set<SgSymbol*> findLoopSymbolsToDecl(SgStatement* stat, const set<string>& declared)
{
    set<SgSymbol*> retVal;
    set<string> added;
    SgStatement* last = stat->lastNodeOfStmt();
    for (auto st = stat; st != last; st = st->lexNext())
    {
        auto isFor = isSgForStmt(st);
        if (isFor)
        {
            auto s = isFor->doName();
            string ident = s->identifier();
            if (ident.find("i_") != string::npos && s->type()->variant() == T_INT)
            {
                if (declared.find(ident) == declared.end() && added.find(ident) == added.end())
                {
                    retVal.insert(s);
                    added.insert(ident);
                }
            }
        }
    }

    return retVal;
}

void restoreAssignsFromLoop(SgFile* file, const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls)
{
    if (cacheNameShiftAll.find(file) == cacheNameShiftAll.end())
        cacheNameShiftAll[file] = map<SgStatement*, map<string, map<int, int>>>();

    int funcNum = file->numberOfFunctions();
    for (int i = 0; i < funcNum; ++i)
    {
        SgStatement* currF = file->functions(i);
        if (cacheNameShiftAll[file].find(currF) == cacheNameShiftAll[file].end())
            cacheNameShiftAll[file][currF] = map<string, map<int, int>>();
        cacheNameShift = &cacheNameShiftAll[file][currF];

        SgStatement* lastNode = currF->lastNodeOfStmt();

        bool needDecl = needDeclare(currF, lastNode);

        vector<pair<SgStatement*, SgStatement*>> toMove;
        for (auto st = file->functions(i); st != lastNode; st = st->lexNext())
        {
            if (st->variant() == CONTAINS_STMT)
                break;

            for (auto& data : getAttributes<SgStatement*, SgStatement*>(st, set<int>{ ASSIGN_STAT }))
                if (data->lineNumber() < 0)
                    toMove.push_back(make_pair(st, data));
        }

        vector<Messages> messagesForFileTmp;
        for (auto& move : toMove)
        {
            auto secondConv = runConversion(move.first, file, messagesForFileTmp, arrayLinksByFuncCalls);
            if (secondConv && notDeletedVectorAssign(move.first))
            {
                move.first->setVariant(-abs(move.first->variant()));
                __spf_print(1, "  save assign\n");

                if (needDecl) // declare loops symbol
                {
                    auto it = newSymbDeclaredInFunction.find(currF);

                    set<string> empty;
                    set<SgSymbol*> newDecl;
                    if (it != newSymbDeclaredInFunction.end())
                        newDecl = findLoopSymbolsToDecl(secondConv, it->second);
                    else
                        newDecl = findLoopSymbolsToDecl(secondConv, empty);

                    if (newDecl.size())
                    {
                        vector<SgSymbol*> newDeclV(newDecl.begin(), newDecl.end());
                        makeDeclaration(currF, newDeclV);

                        for (auto& elem : newDecl)
                            newSymbDeclaredInFunction[currF].insert(elem->identifier());
                    }
                }
            }
            else {
                move.second->setVariant(-abs(move.second->variant()));
                __spf_print(1, "  restored assign\n");
            }
        }
    }
}
