void ObservationTable::_buildLexicalRules(ContextFreeGrammar& G,
    map<NonTerminal, StringSet>& binaryRulesData,
    map<ContextSet, NonTerminal>& distributionNonTerminal)
{
    EquivalenceClasses equivalenceClassesK = this->_getEquivalenceClasses();
    EquivalenceClasses equivalenceClassesKK = this->_getEquivalenceClasses(false);

    // Let V be the set of these equivalence classes - set of non terminals
    for (EquivalenceClass equivalenceClassK : equivalenceClassesK) {
        //TWEAK Avoid unnecessary processing
        if (equivalenceClassK.first.size() == 0) {
            continue;
        }

        // Check if this equivalence class can generate a start symbol
        bool isStart = true;
        for (string w : equivalenceClassK.second) {
            if (this->D.find(w) == this->D.end()) { // Not found
                isStart = false;
            }
        }

        // Create the non-terminal for this equivalence class
        NonTerminal nonTerminal = isStart ? G.getStartSymbol() : G.getNonTerminalSymbol();
        if (isStart) {
            G.I.insert(nonTerminal);
        }
        G.V.insert(nonTerminal);
        distributionNonTerminal.insert({ equivalenceClassK.first, nonTerminal });

        // Save equivalence classes
        auto equivalenceClassesKKlocal = equivalenceClassesKK.find(equivalenceClassK.first);
        if (equivalenceClassesKKlocal == equivalenceClassesKK.end()) { // Not found
            G.getEquivalenceClasses().insert({ nonTerminal, equivalenceClassK.second });
        } else {
            // Insert equivalence classes from KK
            G.getEquivalenceClasses().insert({ nonTerminal, equivalenceClassesKKlocal->second });
        }

        // Compute lexical rules
        for (string w : equivalenceClassK.second) {
            if (w == this->_lambda || this->_alphabet.in(w)) { // terminal or lambda
                Production p(nonTerminal, new Terminal(w));
                G.P.insert(p);
                G.Sigma.insert(Terminal(w));
                G.getLexicalRules().insert({ w, nonTerminal });
                continue;
            }

            // Remember the strings that will become binary rules
            auto binaryRuleData = binaryRulesData.find(nonTerminal);
            if (binaryRuleData != binaryRulesData.end()) {
                binaryRuleData->second.insert(w);
            } else {
                binaryRulesData.insert({ nonTerminal, { w } });
            }
        }
    }
}
ObservationTable::EquivalenceClasses ObservationTable::_getEquivalenceClasses(bool ofK)
{
    EquivalenceClasses equivalenceClasses;
    StringSet& localK = ofK ? this->K : this->_KK;

    // Divide K into equivalence classes according to \equiv F
    for (string k : localK) {
        ContextSet distribution = this->_getDistributionByK(k);
        auto equivalenceClass = equivalenceClasses.find(distribution);

        if (equivalenceClass != equivalenceClasses.end()) { // Found
            equivalenceClass->second.insert(k);
        } else { // Not found
            StringSet ks; // set of k strings
            ks.insert(k);
            equivalenceClasses.insert({ distribution, ks });
        }
    }
    return equivalenceClasses;
}