#include "private_removing.h"

#include "../Utils/errors.h"
#include "../Utils/SgUtils.h"
#include "../Utils/utils.h"

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

using CFG_Type = map<FuncInfo*, vector<SAPFOR::BasicBlock*>>;
using UsersDirectives = map<pair<string, int>, set<SgStatement*>>;

// FixedSubscript represents subscript of array. Subscript is fixed if it is INT_VAL value
struct FixedSubscript {
    bool isFixed;
    int value;
};

// DefUseStmtsPair represents pair of DEF and USE statements for private variable
using DefUseStmtsPair = pair<SgAssignStmt*, SgAssignStmt*>;

// privatesToRemoveGlobal stores the result of PRIVATE_REMOVING_ANALYSIS,
// used in PRIVATE_REMOVING pass - private vars that can be removed
static vector<PrivateToRemove> privatesToRemoveGlobal;


/* ******************************* *
 * Block of common used functions: *
 * ******************************* */

// fillArrayReferences fill all var references of var in exp into refs
static void fillArrayReferences(SgExpression* exp, SgSymbol* arraySymbol, vector<SgArrayRefExp*>& refs)
{
    if (exp == nullptr)
        return;

    if (exp->symbol() != nullptr)
    {
        if (isEqSymbols(exp->symbol(), arraySymbol))
            refs.push_back((SgArrayRefExp*)exp);
    
        return;
    }

    fillArrayReferences(exp->lhs(), arraySymbol, refs);
    fillArrayReferences(exp->rhs(), arraySymbol, refs);
}

// getArrayReferences returns all array references of array in stmt
static vector<SgArrayRefExp*> getArrayReferences(SgStatement* stmt, SgSymbol* arraySymbol)
{
    vector<SgArrayRefExp*> arrayRefs;
    for (SgStatement* st = stmt; st != stmt->lastNodeOfStmt(); st = st->lexNext())
        for (int i = 0; i < 3; ++i)
            fillArrayReferences(st->expr(i), arraySymbol, arrayRefs);

    return arrayRefs;
}

// getShortFixedSubscriptsVector returns vector of fixed INT_VAL subscripts of arrayRef
static vector<int> getShortFixedSubscriptsVector(SgArrayRefExp* arrayRef,
                                            const vector<bool>& fixedDimensions)
{
    vector<int> subscriptsVector;
    for (int i = 0; i < arrayRef->numberOfSubscripts(); ++i)
        if (fixedDimensions[i])
            subscriptsVector.push_back(arrayRef->subscript(i)->valueInteger());

    return subscriptsVector;
}

// removeDuplicateArrayRefs returns unique array refereces in fixed dimensions
static vector<SgArrayRefExp*> removeDuplicateArrayRefs(const vector<SgArrayRefExp*>& arrayRefs,
                                                       const vector<bool>& fixedDimensions)
{
    map<vector<int>, SgArrayRefExp*> uniqueRefs;
    for (SgArrayRefExp* ref : arrayRefs)
    {
        vector<int> subscripts = getShortFixedSubscriptsVector(ref, fixedDimensions);
        if (uniqueRefs.find(subscripts) == uniqueRefs.end())
            uniqueRefs.insert(make_pair(subscripts, ref));
    }

    vector<SgArrayRefExp*> result;
    for (auto& ref : uniqueRefs)
        result.push_back(ref.second);

    return result;
}

static bool isSymbolInExpression(SgSymbol* symbol, SgExpression* exp)
{
    if (exp == nullptr)
        return false;

    if (exp->symbol() != nullptr && isEqSymbols(exp->symbol(), symbol))
        return true;

    return isSymbolInExpression(symbol, exp->lhs()) ||
           isSymbolInExpression(symbol, exp->rhs());
}

/* ************************************** *
 * End of block of common used functions: *
 * ************************************** */


 /* ************************************* *
  * Block of creating messages functions: *
  * ************************************* */

static void addMessageRemoveLoop(vector<Messages>& messages, int loopLineNum)
{
    __spf_print(1, "NOTE: loop on line %d was removed\n", loopLineNum);

    wstring messageE, messageR;
    __spf_printToLongBuf(messageE, L"Loop on line %d was removed", loopLineNum);
    __spf_printToLongBuf(messageR, R198, loopLineNum);

    messages.push_back(Messages(typeMessage::NOTE, loopLineNum, messageR, messageE, 2024));
}

static void addMessageRemovePrivateVar(vector<Messages>& messages, string varName, int loopLineNum)
{
    __spf_print(1, "NOTE: private variable '%s' was removed in loop %d\n", varName.c_str(), loopLineNum);

    wstring messageE, messageR;
    __spf_printToLongBuf(messageE, L"Private variable '%s' was removed", to_wstring(varName).c_str());
    __spf_printToLongBuf(messageR, R191, to_wstring(varName).c_str());

    messages.push_back(Messages(typeMessage::NOTE, loopLineNum, messageR, messageE, 2018));
}

static void addMessageRemovePrivateVarPart(vector<Messages>& messages, string varName, int loopLineNum)
{
    __spf_print(1, "NOTE: private variable '%s' was partially removed in loop %d\n",
                varName.c_str(), loopLineNum);

    wstring messageE, messageR;
    __spf_printToLongBuf(messageE, L"Private variable '%s' was partially removed",
                         to_wstring(varName).c_str());
    __spf_printToLongBuf(messageR, R201, to_wstring(varName).c_str());

    messages.push_back(Messages(typeMessage::NOTE, loopLineNum, messageR, messageE, 2018));
}

static void addMessageCannotFindRD(vector<Messages>& messages, string varName, int loopLineNum)
{
    __spf_print(1, "WARR: cannot remove private var '%s' - cannot find reaching definition for the statement in line %d\n",
                varName.c_str(), loopLineNum);

    wstring messageE, messageR;
    __spf_printToLongBuf(messageE, L"Cannot remove private var '%s' - cannot find reaching definition for the statement",
                         to_wstring(varName).c_str());
    __spf_printToLongBuf(messageR, R194, to_wstring(varName).c_str());

    messages.push_back(Messages(typeMessage::WARR, loopLineNum, messageR, messageE, 2021));
}

static void addMessageMoreThanOneRD(vector<Messages>& messages, string varName, int loopLineNum)
{
    __spf_print(1, "WARR: cannot remove private var '%s' - more than one definition reaches the statement in line %d\n",
                varName.c_str(), loopLineNum);

    wstring messageE, messageR;
    __spf_printToLongBuf(messageE, L"Cannot remove private var '%s' - more than one definition reaches the statement",
                         to_wstring(varName).c_str());
    __spf_printToLongBuf(messageR, R193, to_wstring(varName).c_str());

    messages.push_back(Messages(typeMessage::WARR, loopLineNum, messageR, messageE, 2020));
}

static void addMessageRecursiveDependency(vector<Messages>& messages, string varName, int lineNum)
{
    __spf_print(1, "WARR: cannot remove private var '%s' in line %d - it has recursive dependency\n",
                varName.c_str(), lineNum);

    wstring messageE, messageR;
    __spf_printToLongBuf(messageE, L"cannot remove private var '%s' - it has recursive dependency",
                         to_wstring(varName).c_str());
    __spf_printToLongBuf(messageR, R189, to_wstring(varName).c_str());

    messages.push_back(Messages(typeMessage::WARR, lineNum, messageR, messageE, 2017));
}

static void addMessageDependOnNonInvariant(vector<Messages>& messages, string varName, string dependOn, int lineNum)
{
    __spf_print(1, "WARR: cannot remove private var '%s' in line %d - it depends on non-invariant var '%s'\n",
                varName.c_str(), lineNum, dependOn.c_str());

    wstring messageE, messageR;
    __spf_printToLongBuf(messageE, L"Cannot remove private var '%s' - it depends on non-invariant var '%s'",
                         to_wstring(varName).c_str(), to_wstring(dependOn).c_str());
    __spf_printToLongBuf(messageR, R190, to_wstring(varName).c_str(), to_wstring(dependOn).c_str());

    messages.push_back(Messages(typeMessage::WARR, lineNum, messageR, messageE, 2017));
}

static void addMessageDoesNotMachMask(vector<Messages>& messages, string varName, int loopLneNum)
{
    __spf_print(1, "WARR: cannot remove private var '%s' in loop %d - it doesn't match any fixed dimensions mask\n",
                varName.c_str(), loopLneNum);

    wstring messageE, messageR;
    __spf_printToLongBuf(messageE, L"Cannot remove private var '%s' - it doesn't match any fixed dimensions mask",
                         to_wstring(varName).c_str());
    __spf_printToLongBuf(messageR, R188, to_wstring(varName).c_str());

    messages.push_back(Messages(typeMessage::WARR, loopLneNum, messageR, messageE, 2016));
}

  /* ****************************************** *
 * End of block of creating messages functions: *
 * ******************************************** */


/* ************************************ *
 * PRIVATE_REMOVING block of functions: *
 * ************************************ */

// getDimensionVarName returns var name in style A(1, 2, *)
static string getDimensionVarName(SgSymbol* var, const vector<int>& subscripts, 
                                  const vector<bool>& fixedDimensions)
{
    string result = var->identifier();
    int currentDim = 0;

    result += "(";
    for (int i = 0; i < fixedDimensions.size(); ++i)
    {
        if (fixedDimensions[i])
            result += std::to_string(subscripts[currentDim]);
        else
            result += "*";
    
        if (i != fixedDimensions.size() - 1)
            result += ", ";
    }
    result += ")";

    return result;
}

// findExpByVar returns expression related to var in varToExpMap
static SgExpression* findExpByVar(SgSymbol* var, const map<SgSymbol*, SgExpression*>& varToExpMap)
{
    for (const auto& pair : varToExpMap)
        if (isEqSymbols(pair.first, var))
            return pair.second;

    return nullptr;
}

// replaceVarsWithExps changes variables to expressions in exp using varToExpMap,
// returns modified expression
static SgExpression* replaceVarsWithExps(SgExpression* exp,
                                         const map<SgSymbol*, SgExpression*>& varToExpMap)
{
    if (exp == nullptr)
        return nullptr;

    if (exp->variant() == VAR_REF)
    {
        SgExpression* toReplace = findExpByVar(exp->symbol(), varToExpMap);
        if (toReplace != nullptr)
            return toReplace;

        return exp;
    }

    exp->setLhs(replaceVarsWithExps(exp->lhs(), varToExpMap));
    exp->setRhs(replaceVarsWithExps(exp->rhs(), varToExpMap));

    return exp;
}

// substituteExpressions replaces expressions-array references in exp using refToExpMap
static SgExpression* substituteExpressions(SgExpression* exp, 
                                           const map<string, SgExpression*>& refToExpMap)
{
    if (exp == nullptr)
        return nullptr;
    
    if (exp->variant() == ARRAY_REF)
    {
        const auto& refToExp = refToExpMap.find(exp->unparse());
        if (refToExp == refToExpMap.end())
            return exp;

        return refToExp->second;
    }
    
    exp->setLhs(substituteExpressions(exp->lhs(), refToExpMap));
    exp->setRhs(substituteExpressions(exp->rhs(), refToExpMap));
    
    return exp;
}

static bool isVarReadInLoop(SgSymbol* var, SgForStmt* loopStmt)
{
    for (SgStatement* st = loopStmt; st != loopStmt->lastNodeOfStmt(); st = st->lexNext())
    {
        int i = 0;
        if (st->variant() == ASSIGN_STAT && isEqSymbols(st->expr(0)->symbol(), var))
            i = 1;

        for (; i < 3; ++i)
            if (st->expr(i) && isSymbolInExpression(var, st->expr(i)))
                return true;
    }

    return false;
}

// removeDeadCodeFromLoop removes assign statements to private scalar vars which are not read in loop
static void removeDeadCodeFromLoop(LoopGraph* loop)
{
    SgForStmt* loopStmt = (SgForStmt*) loop->loop->GetOriginal();
    set<Symbol*> privateVars;
    for (auto data : getAttributes<SgStatement*, SgStatement*>(loopStmt, set<int>{ SPF_ANALYSIS_DIR }))
        fillPrivatesFromComment(new Statement(data), privateVars);

    set<string> privates;
    for (Symbol* symbol : privateVars)
        privates.insert(OriginalSymbol((SgSymbol*)symbol)->identifier());

    vector<SgStatement*> stmtsToDelete;
    for (SgStatement* st = loopStmt; st != loopStmt->lastNodeOfStmt(); st = st->lexNext())
    {
        if (st->variant() != ASSIGN_STAT)
            continue;

        SgSymbol* var = st->expr(0)->symbol();
        if (var == nullptr || var->variant() != VARIABLE_NAME)
            continue;

        if (privates.find(var->identifier()) != privates.end() && !isVarReadInLoop(var, loopStmt))
            stmtsToDelete.push_back(st);
    }

    for (auto stmt : stmtsToDelete)
        stmt->deleteStmt();

    for (auto childLoop : loop->children)
        removeDeadCodeFromLoop(childLoop);
}

// fillReadShortFixedSumscripts fills all short fixed subscripts vectors of array var, 
// which are used for reading from array var in exp
static void fillReadShortFixedSumscripts(SgExpression* exp, const PrivateToRemove& var, 
                                         set<vector<int>>& fixedSubscripts)
{
    if (exp == nullptr)
        return;

    if (exp->symbol() != nullptr)
    {
        if (isEqSymbols(exp->symbol(), var.var))
        {
            auto subscripts = getShortFixedSubscriptsVector((SgArrayRefExp*)exp, var.fixedDimensions);
            fixedSubscripts.insert(subscripts);
        }

        return;
    }

    fillReadShortFixedSumscripts(exp->lhs(), var, fixedSubscripts);
    fillReadShortFixedSumscripts(exp->rhs(), var, fixedSubscripts);
}

// getReadShortFixedSumscripts return set of all short fixed subscripts vectors of array var from loop, 
// which are used for reading from array var
static set<vector<int>> getReadShortFixedSumscripts(const PrivateToRemove& var, SgForStmt* loopStmt)
{
    set<vector<int>> fixedSubscripts;
    for (SgStatement* st = loopStmt; st != loopStmt->lastNodeOfStmt(); st = st->lexNext())
    {
        int i = 0;
        if (st->variant() == ASSIGN_STAT && isEqSymbols(st->expr(0)->symbol(), var.var))
            i = 1;

        for (; i < 3; ++i)
            fillReadShortFixedSumscripts(st->expr(i), var, fixedSubscripts);
    }

    return fixedSubscripts;
}

// removeExcessiveDefs removes assignments to var in loop, which are excessive -- 
// there are no any reading from var with such fixed subscripts vector
static void removeExcessiveDefs(const PrivateToRemove& var)
{
    SgForStmt* loopStmt = (SgForStmt*)var.loop->loop->GetOriginal();
    set<vector<int>> usedFixedSubscripts = getReadShortFixedSumscripts(var, loopStmt);

    vector<SgStatement*> stmtsToRemove;
    for (SgStatement* st = loopStmt; st != loopStmt->lastNodeOfStmt(); st = st->lexNext())
    {
        if (st->variant() != ASSIGN_STAT)
            continue;

        if (st->expr(0)->symbol() == nullptr || !isEqSymbols(st->expr(0)->symbol(), var.var))
            continue;

        auto subscripts = getShortFixedSubscriptsVector((SgArrayRefExp*) st->expr(0), var.fixedDimensions);
        if (usedFixedSubscripts.find(subscripts) == usedFixedSubscripts.end())
            stmtsToRemove.push_back(st);
    }

    for (auto st : stmtsToRemove)
        st->deleteStmt();
}

// removeEmptyLoops removes loops with empty body and create messages
static void removeEmptyLoops(LoopGraph* loop, vector<Messages>& messages)
{
    vector<LoopGraph*> loopsToDelete;
    vector<LoopGraph*> newChildrenVector;
    for (auto childLoop : loop->children)
    {
        SgStatement* loopStmt = childLoop->loop->GetOriginal();
        if (loopStmt->lastNodeOfStmt() == loopStmt->lexNext())
            loopsToDelete.push_back(childLoop);
        else
            newChildrenVector.push_back(childLoop);
    }

    for (auto loopToDelete : loopsToDelete)
    {
        addMessageRemoveLoop(messages, loopToDelete->lineNum);
        loopToDelete->loop->extractStmt();
    }

    loop->children.swap(newChildrenVector);
    for (auto childLoop : loop->children)
        removeEmptyLoops(childLoop, messages);
}

// removeVarFromPrivateAttributes removes var from SPF ANALYSIS PRIVATE attributes of loop
static void removeVarFromPrivateAttributes(SgSymbol* var, LoopGraph* loop)
{
    SgStatement* loopStmt = loop->loop->GetOriginal();
    for (int i = 0; i < loopStmt->numberOfAttributes(); ++i)
    {
        SgAttribute* attr = loopStmt->getAttribute(i);
        if (attr->getAttributeType() != SPF_ANALYSIS_DIR)
            continue;

        SgStatement* data = (SgStatement*) attr->getAttributeData();
        SgExprListExp* analysisList = (SgExprListExp*) data->expr(0);
        if (analysisList == nullptr)
            continue;

        vector<SgExpression*> newAnalysisList;
        for (int i = 0; i < analysisList->length(); ++i)
        {
            if (analysisList->elem(i)->variant() == ACC_PRIVATE_OP)
            {
                SgExprListExp* varList = (SgExprListExp*) analysisList->elem(i)->lhs();
                vector<SgExpression*> newVarList;
                for (int j = 0; j < varList->length(); ++j)
                    if (!isEqSymbols(var, varList->elem(j)->symbol()))
                        newVarList.push_back(varList->elem(j)->copyPtr());

                if (!newVarList.empty())
                    newAnalysisList.push_back(new SgExpression(ACC_PRIVATE_OP, makeExprList(newVarList)));
            }
            else
                newAnalysisList.push_back(analysisList->elem(i)->copyPtr());
        }

        if (!newAnalysisList.empty())
            data->setExpression(0, makeExprList(newAnalysisList));
        else
            loopStmt->deleteAttribute(i);
    }
}

// getVarToExpMap returns map SgSymbol* from defRef -> SgExpression* from useRef
static map<SgSymbol*, SgExpression*> getVarToExpMap(SgArrayRefExp* defRef, SgArrayRefExp* useRef,
                                                    const vector<bool>& fixedDimensions)
{
    map<SgSymbol*, SgExpression*> varToExpMap;
    for (int i = 0; i < fixedDimensions.size(); ++i)
        if (!fixedDimensions[i])
            varToExpMap.insert(make_pair(defRef->subscript(i)->symbol(), useRef->subscript(i)));
    
    return varToExpMap;
}

// removeArray removes array by substituting it in DEF-USE pairs.
// Returns set of removed fixed subscripts
static set<vector<int>> removeArray(string filename, const PrivateToRemove& varToRemove)
{
    set<vector<int>> removedFixedSubscripts;

    auto& fixedDimensions = varToRemove.fixedDimensions;
    for (auto& defUsePair : varToRemove.defUseStmtsPairs)
    {
        map<string, SgExpression*> refToExpMap;

        SgAssignStmt* defStmt = defUsePair.first;
        SgAssignStmt* useStmt = defUsePair.second;

        SgArrayRefExp* defRef = (SgArrayRefExp*)defStmt->lhs();
        vector<int> defFixedSubscripts = getShortFixedSubscriptsVector(defRef, fixedDimensions);

        vector<SgArrayRefExp*> arrayUseRefs;
        fillArrayReferences(useStmt->rhs(), defRef->symbol(), arrayUseRefs);
        for (SgArrayRefExp* useRef : arrayUseRefs)
        {
            vector<int> useFixedSubscripts = getShortFixedSubscriptsVector(useRef, fixedDimensions);
            if (defFixedSubscripts != useFixedSubscripts)
                continue; // because useRef and defRef can be different in subscripts of fixed dimensions

            removedFixedSubscripts.insert(useFixedSubscripts);

            auto varToExpMap = getVarToExpMap(defRef, useRef, fixedDimensions);

            SgExpression* expToSubst = defStmt->rhs()->copyPtr();
            expToSubst = replaceVarsWithExps(expToSubst, varToExpMap);

            refToExpMap.insert(make_pair(useRef->unparse(), expToSubst));
        }

        SgExpression* substExp = substituteExpressions(useStmt->rhs(), refToExpMap);
        useStmt->replaceRhs(*substExp);

        // don't revert substitutions in useStmt:
        cancelRevertionForStatement(filename, useStmt, 1);
    }

    return removedFixedSubscripts;
}

void removePrivates(SgFile* file, vector<Messages>& messages, int& countOfTransform)
{
    for (auto& varToRemove : privatesToRemoveGlobal)
    {
        if (string(file->filename()) != varToRemove.loop->fileName)
            continue;

        auto removedDimensions = removeArray(file->filename(), varToRemove);
        countOfTransform++;

        removeDeadCodeFromLoop(varToRemove.loop);
        removeExcessiveDefs(varToRemove);
        removeEmptyLoops(varToRemove.loop, messages);

        SgForStmt* loopStmt = (SgForStmt*)varToRemove.loop->loop->GetOriginal();
        vector<SgArrayRefExp*> varRefs = getArrayReferences(loopStmt, varToRemove.var);
        int loopLineNum = varToRemove.loop->lineNum;
        string varName = varToRemove.var->identifier();
        auto& fixedDimensions = varToRemove.fixedDimensions;
        if (varRefs.empty())
        {
            removeVarFromPrivateAttributes(varToRemove.var, varToRemove.loop);
            addMessageRemovePrivateVar(messages, varName, loopLineNum);
        }
        else
        {
            varRefs = removeDuplicateArrayRefs(varRefs, fixedDimensions);
            for (auto& varRef : varRefs)
            {
                vector<int> subscripts = getShortFixedSubscriptsVector(varRef, fixedDimensions);
                if (removedDimensions.find(subscripts) != removedDimensions.end())
                {
                    varName = getDimensionVarName(varToRemove.var, subscripts, fixedDimensions);
                    addMessageRemovePrivateVarPart(messages, varName, loopLineNum);
                }

                removedDimensions.erase(subscripts);
            }

            for (auto& removedDimension : removedDimensions)
            {
                varName = getDimensionVarName(varToRemove.var, removedDimension, fixedDimensions);
                addMessageRemovePrivateVar(messages, varName, loopLineNum);
            }
        }
    }
}

/* ****************************************** *
 * End of PRIVATE_REMOVING block of functions *
 * ****************************************** */


/* ********************************************* *
 * PRIVATE_REMOVING_ANALYSIS block of functions: *
 * ********************************************* */

 // ReducedArrayVars represents mapping of array reference to reduced scalar var:
 // arr(1, i) -> pair<vector<int>,  SgSymbol*>: [1], arr_1
class ReducedArrayVars {
    map<vector<int>, SgSymbol*> arrayMap;

public:
    void insert(vector<int> subscripts, SgSymbol* var)
    {
        this->arrayMap.insert(make_pair(subscripts, var));
    }

    // find looks up for reduced array var with provided subscripts,
    // returns this var or nullptr
    SgSymbol* find(const vector<int>& subscripts) const
    {
        const auto& arr = this->arrayMap.find(subscripts);
        if (arr == this->arrayMap.end())
            return nullptr;

        return arr->second;
    }

    // getAllVars returns all reduced array vars
    vector<SgSymbol*> getAllVars() const
    {
        vector<SgSymbol*> vars;
        for (auto& reducedVar : this->arrayMap)
            vars.push_back(reducedVar.second);

        return vars;
    }
};

enum class TypeOfInsertedStmt { DEF = 1, USE, DECLARATION, NONE };

// InsertedStatement represents inserted statement for reaching definition analysis
struct InsertedStatement {
    TypeOfInsertedStmt type = TypeOfInsertedStmt::NONE;
    SgStatement* insertedStmt  = nullptr;
    SgStatement* relatedToStmt = nullptr;
    bool isRecursive = false; // true for DEF stmt if it is recursive

    InsertedStatement() {};

    InsertedStatement(TypeOfInsertedStmt type, SgStatement* insertedStmt)
    {
        this->type = type;
        this->insertedStmt = insertedStmt;
    }
};

// findInsertedStmt looking for stmt in insertedStmts
static vector<InsertedStatement>::const_iterator findInsertedStmt(const vector<InsertedStatement>& insertedStmts,
                                                                  SgStatement* stmt)
{
    for (auto iter = insertedStmts.begin(); iter != insertedStmts.end(); ++iter)
        if (iter->insertedStmt->id() == stmt->id())
            return iter;

    return insertedStmts.end();
}

// matchesFixedDimensionsMask checks if all array references have INT_VAL value in fixed dimension
// and writes found mismatches to messages
static bool matchesFixedDimensionsMask(const vector<SgArrayRefExp*>& arrayRefs,
                                       const vector<bool>& fixedDimensions)
{
    for (SgArrayRefExp* ref : arrayRefs)
        for (int i = 0; i < fixedDimensions.size(); ++i)
            if (fixedDimensions[i] && ref->subscript(i)->variant() != INT_VAL)
                return false;

    return true;
}

// defStmtRefsMatchesMask checks if all DEF array refs have INT_VAL value in fixed dimensions
// and VAR_REF in non-fixed dimensions
static bool defStmtRefsMatchesMask(SgStatement* loopStmt, const vector<bool>& fixedDimensions, 
                                   SgSymbol* arraySym)
{
    for (SgStatement* st = loopStmt; st != loopStmt->lastNodeOfStmt(); st = st->lexNext())
    {
        if (st->variant() != ASSIGN_STAT)
            continue;

        if (isEqSymbols(st->expr(0)->symbol(), arraySym)) // DEF statement
        {
            SgArrayRefExp* ref = (SgArrayRefExp*)st->expr(0);
            for (int i = 0; i < fixedDimensions.size(); ++i)
            {   
                if (fixedDimensions[i] && ref->subscript(i)->variant() == INT_VAL)
                    continue;
                if (!fixedDimensions[i] && ref->subscript(i)->variant() == VAR_REF)
                    continue;
                
                return false;
            }
        }
    }

    return true;
}

// getFixedDimensionsVector returns vector of fixed dimensions for arrayRef.
// dimension is fixed, when the value of subsctipt is INT_VAL
static vector<bool> getFixedDimensionsVector(SgArrayRefExp* arrayRef)
{
    vector<bool> fixedDimensions(arrayRef->numberOfSubscripts(), false);
    for (int i = 0; i < arrayRef->numberOfSubscripts(); ++i)
        if (arrayRef->subscript(i)->variant() == INT_VAL)
            fixedDimensions[i] = true;

    return fixedDimensions;
}

// sunparseFixedDimensionsVector unparses fixed dimensions vector as string
static string sunparseFixedDimensionsVector(const vector<bool>& fixedDimensions)
{
    string result = "<";
    result.reserve(result.size() + 7 * fixedDimensions.size());
    for (int i = 0; i < fixedDimensions.size(); ++i) {
        if (fixedDimensions[i] == true)
            result += "true";
        else
            result += "false";

        if (i != fixedDimensions.size() - 1)
            result += ", ";
    }
    result += ">";

    return result;
}

// getFixedDimensionsMask finds fixed dimensions vector with minimum number of fixed dimensions
// and writes messages if array doesn't have fixed dimensions
static vector<bool> getFixedDimensionsMask(const vector<SgArrayRefExp*>& arrayRefs,
                                           vector<Messages>& messages, int loopLineNum)
{
    int fixedDimensionsNumber = arrayRefs[0]->numberOfSubscripts();
    vector<bool> resultMask(fixedDimensionsNumber, true);
    for (const auto arrayRef : arrayRefs) {
        vector<bool> fixedDimensions = getFixedDimensionsVector(arrayRef);
        for (int i = 0; i < fixedDimensionsNumber; ++i)
            resultMask[i] = resultMask[i] && fixedDimensions[i];
    }

    const char* arrayIdent = arrayRefs[0]->symbol()->identifier();

    string unparsedFixedDimensions = sunparseFixedDimensionsVector(resultMask);
    __spf_print(1, "NOTE: found fixed subsripts mask %s for array '%s' in loop %d\n",
                unparsedFixedDimensions.c_str(), arrayIdent, loopLineNum);

    return resultMask;
}

// getReducedArrayVarName returns name for new reduced array variable, 
// made by symbol and subscripts: arr, [1, 2] -> arr_1_2
static string getReducedArrayVarName(SgSymbol* symbol, const vector<int>& subscripts)
{
    string name = OriginalSymbol(symbol)->identifier();
    if (subscripts.empty())
        return name + "_";

    name.reserve(name.size() + subscripts.size() * 3);
    for (int i : subscripts)
        name += "_" + std::to_string(i);

    return name;
}

// getReducedArrayVars makes reduced array variables for arrayRefs
static ReducedArrayVars getReducedArrayVars(const vector<SgArrayRefExp*>& arrayRefs,
                                            const vector<bool>& fixedDimensions,
                                            SgStatement* scope)
{
    ReducedArrayVars reducedArrayVars;
    if (arrayRefs.empty())
        return reducedArrayVars;

    SgType* type = arrayRefs[0]->type();
    for (SgArrayRefExp* ref : arrayRefs)
    {
        vector<int> subscripts = getShortFixedSubscriptsVector(ref, fixedDimensions);
        if (reducedArrayVars.find(subscripts) == nullptr)
        {
            string name = getReducedArrayVarName(ref->symbol(), subscripts);

            int nameNumber = checkSymbNameAndCorrect(name + "__", 0);
            if (nameNumber != 0)
                name = name + "__" + std::to_string(nameNumber);

            SgSymbol* newSymbol = new SgSymbol(VARIABLE_NAME, name.c_str(), type, scope);
            reducedArrayVars.insert(subscripts, newSymbol);
        }
    }

    return reducedArrayVars;
}

// getVarsToDependOn returns all vars that var depends on
static set<string> getVarsToDependOn(SgForStmt* loopStmt, SgSymbol* var)
{
    set<string> dependOn;
    for (SgStatement* st = loopStmt; st != loopStmt->lastNodeOfStmt(); st = st->lexNext())
        if (st->variant() == ASSIGN_STAT && isEqSymbols(st->expr(0)->symbol(), var))
            getVariables(st->expr(1), dependOn, { VAR_REF, ARRAY_REF });

    return dependOn;
}

// getDefStmtForReducedArray returns DFE statement for reduced array variable
static InsertedStatement getDefStmtForReducedArray(SgArrayRefExp* arrayRef, 
                                                   const vector<bool>& fixedDimensions,
                                                   const ReducedArrayVars& reducedArrayVars)
{
    vector<int> subscriptVector = getShortFixedSubscriptsVector(arrayRef, fixedDimensions);
    SgSymbol* reducedVar = reducedArrayVars.find(subscriptVector);
    if (reducedVar == nullptr)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    SgExpression* lhs = new SgVarRefExp(reducedVar);
    SgExpression* rhs = new SgValueExp(0);
    SgStatement*  assignStmt = new SgAssignStmt(*lhs, *rhs);

    return InsertedStatement(TypeOfInsertedStmt::DEF, assignStmt);
}

// getUseStmtForReducedArray returns USE statement for reduced array variable
// (assignment to receiverVar) 
static InsertedStatement getUseStmtForReducedArray(SgArrayRefExp* arrayRef,
                                                   const vector<bool>& fixedDimensions,
                                                   const ReducedArrayVars& reducedArrayVars,
                                                   SgSymbol* receiverVar)
{
    vector<int> subscriptVector = getShortFixedSubscriptsVector(arrayRef, fixedDimensions);
    SgSymbol* reducedVar = reducedArrayVars.find(subscriptVector);
    if (reducedVar == nullptr)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    SgExpression* lhs = new SgVarRefExp(receiverVar);
    SgExpression* rhs = new SgVarRefExp(reducedVar);
    SgStatement*  assignStmt = new SgAssignStmt(*lhs, *rhs);

    return InsertedStatement(TypeOfInsertedStmt::USE, assignStmt);
}

// insertReducedArrayVarStmts inserts in loop assignment statements with reduced vars,
// returns vector of inserted statements
static vector<InsertedStatement> insertReducedArrayVarStmts(SgForStmt* loopStmt, SgSymbol* arraySym,
                                                            const vector<bool>& fixedDimensions,
                                                            const ReducedArrayVars& reducedArrayVars,
                                                            SgSymbol* receiverVar)
{
    vector<InsertedStatement> insertedStmts;
    for (SgStatement* st = loopStmt; st != loopStmt->lastNodeOfStmt(); st = st->lexNext())
    {
        if (st->variant() != ASSIGN_STAT)
            continue;

        bool isUseStmt = false;
        if (isSymbolInExpression(arraySym, st->expr(1))) // reading from array - USE
        {
            vector<SgArrayRefExp*> arrayRefs;
            fillArrayReferences(st->expr(1), arraySym, arrayRefs);
            arrayRefs = removeDuplicateArrayRefs(arrayRefs, fixedDimensions);
            if (!arrayRefs.empty())
                isUseStmt = true;

            for (SgArrayRefExp* arrayRef : arrayRefs)
            {
                InsertedStatement useStmt = getUseStmtForReducedArray(arrayRef, fixedDimensions,
                                                                      reducedArrayVars, receiverVar);
                useStmt.relatedToStmt = st;
                st->insertStmtBefore(*useStmt.insertedStmt, *st->controlParent());
                insertedStmts.push_back(useStmt);
            }
        }

        if (isEqSymbols(st->expr(0)->symbol(), arraySym)) // assignment to array - DEF
        {
            InsertedStatement defStmt = getDefStmtForReducedArray((SgArrayRefExp*)st->expr(0), 
                                                                  fixedDimensions, reducedArrayVars);
            defStmt.relatedToStmt = st;
            defStmt.isRecursive = isUseStmt;
            st->insertStmtBefore(*defStmt.insertedStmt, *st->controlParent());
            insertedStmts.push_back(defStmt);
        }
    }

    return insertedStmts;
}

// makeTmpVar returns new var with provided type and name like tmp_N
static SgSymbol* makeTmpVar(SgType* type, SgStatement* scope)
{
    string varName = "tmp_";
    int nameNumber = checkSymbNameAndCorrect(varName, 0);
    varName = "tmp_" + std::to_string(nameNumber);

    return new SgSymbol(VARIABLE_NAME, varName.c_str(), type, scope);
}

// buildDefUsePairs builds pairs of DEF and USE statements for array
static vector<DefUseStmtsPair> buildDefUsePairs(const CFG_Type& CFGraph,
                                                const vector<InsertedStatement>& insertedStmts,
                                                vector<Messages>& messages,
                                                SgSymbol* arrayToRemove)
{
    vector<DefUseStmtsPair> defUsePairs;

    for (const InsertedStatement& stmt : insertedStmts)
    {
        if (stmt.type != TypeOfInsertedStmt::USE)
            continue;

        int relLineNum = stmt.relatedToStmt->lineNumber();

        // looking for reaching definitions for the current block:
        auto useInsAndBlock = getInstructionAndBlockByStatement(CFGraph, stmt.insertedStmt);
        auto useArg = useInsAndBlock.first->getArg1();
        const auto& RD_In = useInsAndBlock.second->getRD_In();

        const auto& RD_forUseArg = RD_In.find(useArg);
        if (RD_forUseArg == RD_In.end()) // cannot find reaching definitions for argument
        {
            addMessageCannotFindRD(messages, arrayToRemove->identifier(), relLineNum);
            continue;
        }

        set<int> RD_defArgs = RD_forUseArg->second; // make copy
        for (int defArgNum : RD_defArgs)
        {
            auto defInsAndBlock = getInstructionAndBlockByNumber(CFGraph, defArgNum);
            SgStatement* defStmt = defInsAndBlock.first->getOperator();
            auto defInsertedStmt = findInsertedStmt(insertedStmts, defStmt);
            if (stmt.relatedToStmt == defInsertedStmt->relatedToStmt) // recursive definition
            {
                RD_defArgs.erase(defArgNum);
                break;
            }
        }

        if (RD_defArgs.size() == 0) // argument is not initialized
        {
            addMessageCannotFindRD(messages, arrayToRemove->identifier(), relLineNum);
            continue;
        }

        if (RD_defArgs.size() > 1) // more than one reaching definition
        {
            addMessageMoreThanOneRD(messages, arrayToRemove->identifier(), relLineNum);
            continue;
        }

        int defArgNum = *RD_defArgs.begin();

        auto defInsAndBlock = getInstructionAndBlockByNumber(CFGraph, defArgNum);
        SgStatement* defStmt = defInsAndBlock.first->getOperator();
        auto defInsertedStmt = findInsertedStmt(insertedStmts, defStmt);
        if (defInsertedStmt == insertedStmts.end())
        {
            addMessageCannotFindRD(messages, arrayToRemove->identifier(), relLineNum);
            continue;
        }

        //don't substitute def stmt into use, if def is recursive
        if (defInsertedStmt->isRecursive)
        {
            addMessageRecursiveDependency(messages, arrayToRemove->identifier(), relLineNum);
            continue;
        }

        SgAssignStmt* def = (SgAssignStmt*)defInsertedStmt->relatedToStmt;
        SgAssignStmt* use = (SgAssignStmt*)stmt.relatedToStmt;
        defUsePairs.push_back(make_pair(def, use));
    }

    return defUsePairs;
}

// getScopeLoopStmt returns least outer scope loop statement
static SgForStmt* getScopeLoopStmt(SgStatement* stmt)
{
    while (stmt != nullptr && stmt->variant() != FOR_NODE)
        stmt = stmt->controlParent();

    return (SgForStmt*)stmt;
}

// findChildLoop returns LoopGraph for provided loop statement
static LoopGraph* findLoop(LoopGraph* outerLoop, SgForStmt* loopStmt)
{
    SgStatement* outerLoopStmt = outerLoop->loop->GetOriginal();
    if (outerLoopStmt->id() == loopStmt->id())
        return outerLoop;

    for (auto childLoop : outerLoop->children)
    {
        auto childLoopGraph = findLoop(childLoop, loopStmt);
        if (childLoopGraph != nullptr)
            return childLoopGraph;
    }

    return nullptr;
}

// minCommonAncestor finds least common ancestor of a and b loops in loop graph tree with root parent
static LoopGraph* leastCommonAncestor(LoopGraph* a, LoopGraph* b, LoopGraph* parent)
{
    LoopGraph* tmp;

    int aDepth = 0;
    tmp = a;
    while (tmp != parent)
    {
        aDepth++;
        tmp = tmp->parent;
    }

    int bDepth = 0;
    tmp = b;
    while (tmp != parent)
    {
        bDepth++;
        tmp = tmp->parent;
    }

    while (aDepth != bDepth)
    {
        if (aDepth > bDepth)
        {
            a = a->parent;
            aDepth--;
        }
        else
        {
            b = b->parent;
            bDepth--;
        }
    }

    while (a->lineNum != b->lineNum)
    {
        a = a->parent;
        b = b->parent;
    }

    return a;
}

// getFixedSubscriptsVector returns vector of fixed INT_VAL subscripts of arrayRef
// true - subscript is fixed, false - it isn't
static vector<FixedSubscript> getFixedSubscriptsVector(SgArrayRefExp* arrayRef)
{
    vector<FixedSubscript> subscriptsVector;
    for (int i = 0; i < arrayRef->numberOfSubscripts(); ++i)
    {
        if (arrayRef->subscript(i)->variant() == INT_VAL)
            subscriptsVector.push_back(FixedSubscript{ true, arrayRef->subscript(i)->valueInteger() });
        else
            subscriptsVector.push_back(FixedSubscript{ false, 0 });
    }

    return subscriptsVector;
}

// fillFullFixedSubscriptsVectorsOfAllVars return vector of pairs (name of var, its fixed subscripts vector)
// of all VAR_REF and ARRAY_REF vars in exp
static void fillFixedSubscriptsVectorsOfAllVars(SgExpression* exp,
                                                vector<pair<string, vector<FixedSubscript>>>& vec)
{
    if (exp == nullptr)
        return;

    if (exp->symbol() != nullptr)
    {
        if (exp->variant() == VAR_REF)
            vec.push_back(make_pair(exp->symbol()->identifier(), vector<FixedSubscript>{}));
        else if (exp->variant() == ARRAY_REF)
            vec.push_back(make_pair(exp->symbol()->identifier(),
                                    getFixedSubscriptsVector((SgArrayRefExp*)exp)));
        return;
    }

    fillFixedSubscriptsVectorsOfAllVars(exp->lhs(), vec);
    fillFixedSubscriptsVectorsOfAllVars(exp->rhs(), vec);
}

// isDifferentRefs checks if exp (var reference) is different from var. Refs are different 
// if they has at least one different fixed subscript: arr(i, 1) is different from arr(j, 2)
static bool isDifferentRefs(SgExpression* exp, const pair<string, vector<FixedSubscript>>& var)
{
    if (exp->symbol()->identifier() != var.first)
        return true;

    if (exp->variant() == VAR_REF)
        return false;

    vector<FixedSubscript> leftVec = getFixedSubscriptsVector((SgArrayRefExp*)exp);

    for (int i = 0; i < leftVec.size(); i++)
        if (leftVec[i].isFixed && var.second[i].isFixed && leftVec[i].value != var.second[i].value)
            return true;

    return false;
}

pair<SAPFOR::Argument*, set<int>> findVarInRDSet(map<SAPFOR::Argument*, set<int>> RD_In, string var)
{
    for (auto& RD_InElem : RD_In)
    {
        string elemName = RD_InElem.first->getValue();
        size_t pos = elemName.find('%');
        if (pos == string::npos)
            continue;

        if (var == elemName.substr(pos+1, elemName.length() - pos - 1))
            return RD_InElem;
    }

    return make_pair(nullptr, set<int>{});
}

// checkDefUsePair checks if def statement from pair can be substituted into use statement
// and creates messages
static bool checkDefUsePair(DefUseStmtsPair defUse, LoopGraph* loop, const CFG_Type& CFGraph,
                            const vector<bool>& fixedDimensions, vector<Messages>& messages)
{
    if (defUse.first->lineNumber() > defUse.second->lineNumber())
        return false;

    while (loop->perfectLoop != 1)
        loop = loop->children[0];

    vector<pair<string, vector<FixedSubscript>>> dependOnVars;

    SgArrayRefExp* defRef = (SgArrayRefExp*)defUse.first->expr(0);
    string varToRemoveName = defRef->symbol()->identifier();
    vector<SgArrayRefExp*> arrayUseRefs;
    fillArrayReferences(defUse.second->rhs(), defRef->symbol(), arrayUseRefs);
    for (auto useRef : arrayUseRefs)
    {
        map<SgSymbol*, SgExpression*> varToExpMap = getVarToExpMap(defRef, useRef, fixedDimensions);
        SgExpression* expToSubst = defUse.first->rhs()->copyPtr();
        expToSubst = replaceVarsWithExps(expToSubst, varToExpMap);

        fillFixedSubscriptsVectorsOfAllVars(expToSubst, dependOnVars);
    }

    auto defInsAndBlock = getInstructionAndBlockByStatement(CFGraph, defUse.first);
    const auto& defRD_In = defInsAndBlock.second->getRD_In();
    auto useInsAndBlock = getInstructionAndBlockByStatement(CFGraph, defUse.second);
    const auto& useRD_In = useInsAndBlock.second->getRD_In();
    for (const auto& var : dependOnVars) // checking scalar vars
    {
        if (var.second.size() == 0)
        {
            auto defArg = findVarInRDSet(defRD_In, var.first);
            if (defArg.first == nullptr) // there is no any RD for common vars or parameters
                continue;

            auto useArg = findVarInRDSet(useRD_In, var.first);
            if (defArg.second.size() != 1 || useArg.second.size() != 1 
                || *defArg.second.begin() != *useArg.second.begin())
            {
                addMessageDependOnNonInvariant(messages, varToRemoveName,
                    var.first, defUse.first->lineNumber());
                return false;
            }
        }
    }

    return true;
    // TODO: ^^^^^
    // проверка массивов отключена, поскольку является слишком консервативной и не позволяет
    // выполнить удаление массива RTMP в функции RHS теста LU.
    // требуется выполнять более сложный анализ, с определением границ циклов,
    // чтобы отличать, что все присваивания в переменную rsd в этом примере 
    // являются присваиваниями в разные элементы массива:
    // rsd(1,i,j,2) = ...
    // do  k = 4, nz - 3
    //    rsd(1, i, j, k) = ...
    // enddo
    // rsd(1, i, j, nz - 2) = ...

    // checking arrays:
    auto defLoopStmt = getScopeLoopStmt(defUse.first);
    auto useLoopStmt = getScopeLoopStmt(defUse.second);

    auto defLoop = findLoop(loop, defLoopStmt);
    auto useLoop = findLoop(loop, useLoopStmt);

    if (!defLoopStmt || !useLoopStmt || !defLoop || !useLoop)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
            
    int minCommonLoopLine = leastCommonAncestor(defLoop, useLoop, loop)->lineNum;
    if (defLoop->lineNum != minCommonLoopLine)
        while (defLoop->parent->lineNum != minCommonLoopLine)
            defLoop = defLoop->parent;

    if (useLoop->lineNum != minCommonLoopLine)
        while (useLoop->parent->lineNum != minCommonLoopLine)
            useLoop = useLoop->parent;

    int defLoopLine = defLoop->lineNum;
    int useLoopLine = useLoop->lineNum;
    SgStatement *startStmt = nullptr, *endStmt = nullptr;
    if (defLoopLine != minCommonLoopLine && useLoopLine != minCommonLoopLine)
    {
        startStmt = defLoop->loop;
        endStmt = useLoop->loop->lastNodeOfStmt();
    }
    else if (defLoopLine != minCommonLoopLine && useLoopLine == minCommonLoopLine)
    {
        startStmt = defLoop->loop;
        endStmt = defUse.second;
    }
    else if (defLoopLine == minCommonLoopLine && useLoopLine != minCommonLoopLine)
    {
        startStmt = defUse.first;
        endStmt = useLoop->loop->lastNodeOfStmt();
    }
    else if (defLoopLine == minCommonLoopLine && useLoopLine == minCommonLoopLine)
    {
        startStmt = defUse.first;
        endStmt = defUse.second;
    }
    else
        return false;

    for (const auto& var : dependOnVars)
    {
        for (SgStatement* st = startStmt; st != endStmt; st = st->lexNext())
        {
            if (st->variant() == ASSIGN_STAT && !isDifferentRefs(st->expr(0), var))
            {
                addMessageDependOnNonInvariant(messages, varToRemoveName,
                                               var.first, defUse.first->lineNumber());
                return false;
            }
        }
    }

    return true;
}

// getPrivateArraysFromUserDirs returns private arrays from user directives ANALYSIS PRIVATE
static vector<SgSymbol*> getPrivateArraysForLoop(LoopGraph* loop, const UsersDirectives& dirs)
{
    vector<SgSymbol*> privateArrays;

    auto loopDirectives = dirs.find(make_pair(loop->fileName.c_str(), loop->lineNum));
    if (loopDirectives == dirs.end()) // no directives for loop
        return privateArrays;

    set<Symbol*> privateVars;
    for (SgStatement* dir : loopDirectives->second)
    {
        Statement* dirStmt = new Statement(dir);
        fillPrivatesFromComment(dirStmt, privateVars);
        delete dirStmt;
    }

    for (Symbol* var : privateVars)
        if (var->type()->variant() == T_ARRAY)
            privateArrays.push_back(var->GetOriginal());

    return privateArrays;
}

void removePrivatesAnalysis(vector<LoopGraph*>& loopGraphs,
                            vector<Messages>& messages,
                            const UsersDirectives& usersDirectives,
                            const map<string, CommonBlock*>& commonBlocks,
                            const map<string, vector<FuncInfo*>>& allFuncInfo)
{
    for (LoopGraph* loop : loopGraphs)
    {
        if (!loop->isFor)
            continue;

        SgForStmt* loopStmt = (SgForStmt*)loop->loop->GetOriginal();
        vector<SgSymbol*> privateArrays = getPrivateArraysForLoop(loop, usersDirectives);
        if (privateArrays.empty())
            continue;

        set<SgSymbol*> arraysToCheck;
        for (SgSymbol* privateArray : privateArrays)
            arraysToCheck.insert(privateArray);

        while (true)
        {
            SgSymbol* arrayToRemove = nullptr;
            vector<SgArrayRefExp*> arrayRefs;

            // choose arrayToRemove:
            for (SgSymbol* arrayToCheck : arraysToCheck)
            {
                arrayRefs = getArrayReferences(loopStmt, arrayToCheck);
                if (arrayRefs.empty()) // no references to array
                    continue;

                set<string> dependOnVars = getVarsToDependOn(loopStmt, arrayToCheck);

                bool skip = false;
                for (string dependOnVar : dependOnVars)
                    for (SgSymbol* notChecked : arraysToCheck)
                        if (dependOnVar == notChecked->identifier() && !isEqSymbols(arrayToCheck, notChecked))
                            skip = true;

                if (skip) // check this array again later
                    continue;

                arraysToCheck.erase(arrayToCheck);
                arrayToRemove = arrayToCheck;
                break; // arrayToRemove is chosen
            }

            if (arrayToRemove == nullptr) // no array to remove
                break;

            vector<bool> fixedDimensionsMask = getFixedDimensionsMask(arrayRefs, messages, loop->lineNum);

            if (!matchesFixedDimensionsMask(arrayRefs, fixedDimensionsMask)
                || !defStmtRefsMatchesMask(loopStmt, fixedDimensionsMask, arrayToRemove))
            {
                addMessageDoesNotMachMask(messages, arrayToRemove->identifier(), loop->lineNum);
                continue;
            }

            arrayRefs = removeDuplicateArrayRefs(arrayRefs, fixedDimensionsMask);

            // inserting assignment to reduced array variables for getting reaching definitions analysis:
            SgStatement* scope = loopStmt->getScopeForDeclare();
            auto reducedArrayVars = getReducedArrayVars(arrayRefs, fixedDimensionsMask, scope);
            SgSymbol* receiverVar = makeTmpVar(arrayRefs[0]->type(), scope);
            auto insertedStmts = insertReducedArrayVarStmts(loopStmt, arrayToRemove, fixedDimensionsMask,
                                                            reducedArrayVars, receiverVar);

            // declare reduced array variables and receiver:
            insertedStmts.push_back(InsertedStatement(
                TypeOfInsertedStmt::DECLARATION,
                makeDeclaration(loopStmt, reducedArrayVars.getAllVars(), nullptr)
            ));
            insertedStmts.push_back(InsertedStatement(
                TypeOfInsertedStmt::DECLARATION,
                makeDeclaration(loopStmt, vector<SgSymbol*> {receiverVar}, nullptr)
            ));

            CFG_Type CFG_ForFunc = buildCFGforCurrentFunc(loopStmt, SAPFOR::CFG_Settings(true, true), commonBlocks, allFuncInfo);
            if (CFG_ForFunc.empty())
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            auto defUseStmtsPairs = buildDefUsePairs(CFG_ForFunc, insertedStmts, messages, arrayToRemove);
            if (!defUseStmtsPairs.empty()) // DEF-USE pairs were build successfully
            {
                vector<DefUseStmtsPair> resultDefUsePairs;
                for (auto& pair : defUseStmtsPairs)
                    if (checkDefUsePair(pair, loop, CFG_ForFunc, fixedDimensionsMask, messages))
                        resultDefUsePairs.push_back(pair);

                PrivateToRemove newPrivateToRemove;
                newPrivateToRemove.loop = loop;
                newPrivateToRemove.var = arrayToRemove;
                newPrivateToRemove.defUseStmtsPairs.swap(resultDefUsePairs);
                newPrivateToRemove.fixedDimensions.swap(fixedDimensionsMask);

                privatesToRemoveGlobal.push_back(newPrivateToRemove);
            }

            // delete inserted statements:
            for (auto& stmt : insertedStmts)
                stmt.insertedStmt->deleteStmt();

            deleteCFG(CFG_ForFunc);
        }
    }

    for (LoopGraph* loop : loopGraphs)
        removePrivatesAnalysis(loop->children, messages, usersDirectives, commonBlocks, allFuncInfo);    
}