pattern_recognition / source / automaton.cc
automaton.cc
Raw
#include "automaton.h"

#include <iostream>

// Default constructor die automaton initialiseert
Automaton::Automaton() {
    correctAutomaton = false;
} // Automaton::Automaton

// Setter voor de boolean die aangeeft of het object een correct automaat is
void Automaton::setCorrectAutomaton(bool const correctAutomaton) {
    this->correctAutomaton = correctAutomaton;
} // Automaton::setCorrectAutomaton

// Maakt de vector automaton leeg
void Automaton::clear() {
    automaton.clear();
} // Automaton::clear

// Geeft de size van de automaton vector
int Automaton::getSize() const {
    return automaton.size();
} // Automaton::GetSize

// Creeërt de DOT-notatie van deze automaton en stuurt deze naar std::cout
void Automaton::dotAutomaton() const {
    if(correctAutomaton) {
        // De toestanden de juiste vorm geven. S wordt gemaakt om een pijl naar de begintoestand te laten wijzen
        std::cout << "digraph {" << std::endl << "  rankdir=LR;" << std::endl << "  node [shape = point] S" << std::endl
                  << "  node [shape = doublecircle] " << automaton.size()-1 << std::endl << "  node [shape = circle]"
                  << std::endl << "  S -> 0" << std::endl;

        int const automatonSize = getSize();
        for(int i = 0; i < automatonSize; i++) {
            if(automaton.at(i).secondType != Transition::final) {
                std::cout << "  " << i << " -> " << automaton.at(i).first;
                if(automaton.at(i).secondType == Transition::state) {
                    std::cout << std::endl << "  " << i << " -> " << automaton.at(i).second.state << std::endl;
                } // Als de toestand i 2 uitgaande pijlen heeft
                else if(automaton.at(i).secondType == Transition::letter) {
                    std::cout << " [xlabel=\"" << automaton.at(i).second.letter << "\"]" << std::endl;
                } // Als de toestand i 1 uitgaande pijl heeft met een label
                else {
                    std::cout << std::endl;
                } // Als het type lambda is dan is er maar 1 transitie en moet je gewoon naar de volgende regel
            }
        }
        std::cout << "}";
        return;
    } // DOT wordt alleen uitgevoerd als een geldige automaat is ingelezen
    std::cerr << "Er is geen geldige automaat ingelezen dus deze functie wordt niet uitgevoerd" << std::endl;
} // Automaton::dotAutomaton

// Voegt een eerste transitie toe
void Automaton::firstTransition(char const letter) {
    if(!((letter >= 'a' && letter <= 'z') || letter == '$')) {
        std::cerr << "\nMeegegeven karakter is geen letter" << std::endl;
        return;
    } // geen letter
    if(getSize() > 0) {
        std::cerr << "\nKan geen eerste eerste transitie toevoegen bij een automaat groter dan 1" << std::endl;
    } // de automaat is niet leeg, dus geen eerste transitie

    if(letter != '$') {
        Transition transition1; // begin- en eindtoestand
        transition1.first = 1;
        transition1.secondType = Transition::letter; // letter type
        transition1.second.letter = letter;
        automaton.push_back(transition1);
    } // bij $ hebben we niet te maken met een letter

    Transition transition2;
    transition2.secondType = Transition::final; // final state
    automaton.push_back(transition2);
} // Automaton::firstTransition

// Geeft de transition op de meegegeven idnex in de automaton
Transition Automaton::getTransition(int const index) const {
    int const size = getSize(); // size of vector
    Transition transition = Transition(); // transitie
    if(index < 0 || index >= size) {
        std::cerr << "\nIndex zit buiten de vector size" << std::endl;
    }
    else {
        transition = automaton.at(index);
    }
    return transition;
} // Automaton::GetTransition

// Voert conacetantie uit met auto1
void Automaton::concat(Automaton auto1) {
    int const auto1Size = auto1.getSize(); // aantal elementen in auto1
    int addIndex = getSize() - 1; // getal dat moet worden toegevoegd aan de indexen van auto1
    if(addIndex == -1) {
        addIndex = 0;
    }
    automaton.pop_back(); // verwijdert laatste element, zodat deze gereplaced kan worden met eerste element auto1

    for(int i = 0; i < auto1Size; i++) {
        Transition transition = auto1.getTransition(i);
        Transition copyTransition;
        copyTransition.first = transition.first + addIndex;
        copyTransition.secondType = transition.secondType;

        switch(copyTransition.secondType) {
            case Transition::state:
                copyTransition.second.state = transition.second.state + addIndex;
                break;
            case Transition::letter:
                copyTransition.second.letter = transition.second.letter;
                break;
            default:
                break;
        } // Zal het tweede type juist kopiëren
        automaton.push_back(copyTransition);
    } // kopiëert de transitie-elementen van auto1 en voegt deze toe aan automaton
} // Automaton::concat

// Voert de plus operatie uit met als opties auto1 of auto2
void Automaton::plus(Automaton auto1, Automaton auto2) {
    if(getSize() > 0) {
        std::cerr << "\nKan geen plus operatie doen op een automaat die al gevuld is" << std::endl;
    }
    Transition transition1;
    transition1.secondType = Transition::state;
    transition1.first = 1; // gaat wijzen naar copy van eerste element auto1
    automaton.push_back(transition1);
    automaton.push_back(Transition());
    concat(auto1);
    automaton.at(0).second.state = getSize(); // gaat wijzen naar copy van eerste element auto2
    automaton.push_back(Transition());
    concat(auto2);
    automaton.at(auto1.getSize()).secondType = Transition::lambda;
    automaton.at(auto1.getSize()).first = getSize(); // einde auto1 gaat wijzen naar eindtoestand
    automaton.at(getSize() - 1).secondType = Transition::lambda;
    automaton.at(getSize() - 1).first = getSize(); // einde auto2 gaat wijzen naar eindtoestand
    Transition endState;
    endState.secondType = Transition::final;
    automaton.push_back(endState);
} // Automaton::plus

// Voert de ster operatie uit op huidige automaton
void Automaton::star() {
    int const size = getSize();
    if(size == 0) {
        std::cerr << "\nKan geen star operatie doen op een automaat die niet gevuld is" << std::endl;
    }
    automaton.at(size - 1).first = size; // wijst naar komende nieuwe laatste transitie-element
    automaton.at(size - 1).secondType = Transition::state;
    automaton.at(size - 1).second.state = 0; // wijst naar huidige eerste transitie-element
    
    for(int i = 0; i < size; i++) {
        automaton.at(i).first++;
        if(automaton.at(i).secondType == Transition::state) {
            automaton.at(i).second.state++;
        }
    } // verhoogt alle transitie waarden, zodat er aan het begin een element kan worden toegevoegd

    Transition newLast;
    newLast.secondType = Transition::final;
    automaton.push_back(newLast); // nieuwe laatste transitie-element
    Transition newFirst;
    newFirst.first = 1; 
    newFirst.secondType = Transition::state;
    newFirst.second.state = size + 1; // wijst naar het nieuwe laatste transitie-element
    automaton.insert(automaton.begin(), newFirst); // voegt lambdaTransition toe aan het begin
} // Automaton::star

// Functie die alle waarden initialiseert en dan een andere functie aanroept om de
// e-afsluiting te berekenen
std::vector<int> Automaton::getEClosure(int state) const {
    std::vector<int> result; // Vector met de e-afsluiting van states
    std::vector<bool> visited; // Vector die bijhoudt welke states al bezocht zijn
    for(int i = 0; i < int(automaton.size()); i++) {
        visited.push_back(false);
    } // Alle states op false zetten want ze zijn nog niet bezocht
    getEClosureRec(state, result, visited);
    return result;
} // Automaton::getEClosure

// Berekent recursief de e-afsluiting van een knoop waarbij in de gaten wordt gehouden
// of knopen niet al bezocht zijn en er daardoor geen oneindinge loop ontstaat
void Automaton::getEClosureRec(int const state, std::vector<int> &eClosure, std::vector<bool> &visited) const {
    visited.erase(visited.begin() + state);           // Aangeven dat de
    visited.insert((visited.begin() + state), true);  // knoop is bezocht
    eClosure.push_back(state);

    if(automaton.at(state).secondType == Transition::state || automaton.at(state).secondType == Transition::lambda) {
        if(!visited.at(automaton.at(state).first)) {
            getEClosureRec(automaton.at(state).first, eClosure, visited);
        } // Als de knoop nog niet bezocht is dan wordt de e-afsluiting van die knoop berekent 
        if(automaton.at(state).secondType == Transition::state && !visited.at(automaton.at(state).second.state)) {
            getEClosureRec(automaton.at(state).second.state, eClosure, visited);
        } // Alleen als de knoop twee e-transities heeft en de tweede knoop is nog niet bezocht
    } // Als de knoop twee uitgaande e-transities heeft
} // Automaton::getEClosure

// Functie die controleert of je vanuit een van de toestanden van vector states
// in de accepterende toestand terecht komt
bool Automaton::checkFinal(std::vector<int> const states) const {
    std::vector<int> eClosure; // Vector die de e-afsluitng van een toestand bevat

    for(int i = 0; i < int(states.size()); i++) {
        eClosure = getEClosure(states.at(i));
        for(int j = 0; j < int(eClosure.size()); j++) {
            if(automaton.at(eClosure.at(j)).secondType == Transition::final) {
                return true;
            }
        } // Alle toestanden in de e-afsluiting van de desbetreffende toestand afgaan
    } // Alle toestanden in states afgaan

    return false;
} // Automaton::checkFinal

// Controleert eerst of state al in de vector states zit en als dat niet zo is dan wordt die toegevoegd.
// Als die er wel in zit dan gebeurd er niks
void Automaton::addState(std::vector<int> &states, int const state) const {
    for(int i = 0; i < int(states.size()); i++) {
        if(states.at(i) == state) {
            return;
        }
    }
    states.push_back(state);
} // Automaton::addState

// Roept de private functie match aan met de juiste parameters en retourneert een string
// die aangeeft of de expressie wordt geaccepteerd door de automaat
std::string Automaton::match(std::string const expressie) const {
    if(correctAutomaton) {
        std::vector<int> states;
        states.push_back(0); // Alleen 0 zit in states omdat dat de begintoestand is

        if(match(expressie, 0, states)) {
            return "match";
        } // Als de expressie door de automaat geaccepteerd wordt
        return "geen match";
    } // Alleen match uitvoeren bij een geldige automaat

    return "Er is geen geldige automaat ingelezen dus deze functie wordt niet uitgevoerd";
} // Automaton::match

// Bekijkt of de string expressie wordt geaccepteerd door de automaat door er recursief met een pointer
// position langs te gaan en in states bij te houden waar je zou kunnen zijn
bool Automaton::match(std::string const expressie, int const position, std::vector<int> &states) const {
    if(position == int(expressie.size())) {
        return checkFinal(states);
    }
    std::vector<int> eClosure; // Vector die de e-afsluiting bijhoudt
    std::vector<int> oldStates(states); // Kopieer de oude states naar de nieuwe states
    states.clear();

    for(int i = 0; i < int(oldStates.size()); i++) {
        eClosure = getEClosure(oldStates.at(i));

        for(int j = 0; j < int(eClosure.size()); j++) {
            if(automaton.at(eClosure.at(j)).secondType == Transition::letter &&
              automaton.at(eClosure.at(j)).second.letter == expressie.at(position)) {
                addState(states, automaton.at(eClosure.at(j)).first);
            } // Als de letter van de transitie overeenkomt met de letter op plek position in expressie
            else if(expressie.at(position) == '$') {
                addState(states, eClosure.at(j));
            } // Als de lege string de expressie is of erin voorkomt
        } // Elke knoop in de e-afsluiting van de state afgaan

    } // Alle meegegeven states afgaan
    return match(expressie, position+1, states);
} // Automaton::match