DNA-sequences / include / seqpointer.h
seqpointer.h
Raw
#ifndef SEQPOINTER_H  // voorkom dat dit bestand meerdere keren
#define SEQPOINTER_H  // ge-include wordt

#include "variant.h"
#include "deletie.h"
#include "insertie.h"
#include "inversie.h"
#include "delinsertie.h"
#include "substitutie.h"

#include <iostream>
#include <cstdint>
#include <string>
#include <vector>

template <typename T>
class Vakje {
    public:
        Vakje* vorige;
        Vakje* volgende;
        T data;
        int rest;
        Vakje();
};

template <typename T>
Vakje<T>::Vakje():vorige(nullptr), volgende(nullptr), data(T()), rest(0) {
}

template <typename T>
class SeqPointer {
    private:
        // Pointer die naar het eerste Vakje van de SeqPointer wijst
        Vakje<T> *ingang;

        // Pointer die naar het laatste Vakje van de SeqPointer wijst
        Vakje<T> *uitgang;

        // Hoeveel Vakjes de SeqPointer bevat
        int aantalVakjes;

        // Maximaal aantal bits dat in T past
        int grootte;
        
        // Maximale aantal symbolen die in T past
        int maxGrootte;

        // Zet de string seq om in een variabele van type T en zet in
        // rest hoeveel rest er over is
        T vakjeMaken(std::string seq, int &rest) const;

        // Vult een Vakje "vakje" aan met een Vakje "nieuw"
        void aanvullen(Vakje<T> *vakje, Vakje<T> *nieuw) const;

        // Retourneert het Vakje van plek "position"
        Vakje<T>* vindVakje(int const position) const;

        // Voegt een nieuw Vakje achteraan de SeqPointer toe
        void voegachter(T const nieuwVakje, int const rest0);

        // Verwijdert het voorste Vakje van de SeqPointer
        void voorVerwijderen();
    
    public:
        // Constructor voor een lege SeqPointer
        SeqPointer();

        // Constructor die van een string een SeqPointer maakt
        SeqPointer(std::string const sequentie);

        // Copy constructor van SeqPointer
        SeqPointer(SeqPointer<T> const &seqpointer);

        // Destructor die alle Vakjes verwijderd
        ~SeqPointer();

        // Retourneert de waarde van de private variabele aantalVakjes
        int getaantalVakjes() const;

        // Retourneert de waarde van de private variabele ingang
        Vakje<T>* getIngang() const;

        // Verwijdert alle Vakjes uit de SeqPointer
        void vernietig();

        // Retourneert de SeqPointer waarmee de functie is aangeroepen
        SeqPointer<T> copy() const;

        // Kopieert de SeqPointer sequentie in de SeqPointer waarmee de functie
        // is aangeroepen
        void copy2(SeqPointer<T> const &sequentie);

        // Drukt de symbolen af die in SeqPointer zijn opgeslagen
        void drukAf() const;

        // Maakt de Vakjes uit een gegeven string
        void seqpointerMaken(std::string const &sequentie);

        // Retourneert hoeveel symbolen zijn opgeslagen in de SeqPointer
        int length() const;

        // Retourneert het character dat op position staat
        char char_at(int const position) const;

        // Kijkt of de meegegeven SeqPointer gelijk is aan de SeqPointer waarmee
        // de functie is aangeroepen
        bool equal(SeqPointer<T> const &sequentie) const;

        // Geeft een nieuwe SeqPointer met de symbolen [start, end)
        SeqPointer<T> slice(int const start, int const end) const;

        // Geeft een nieuwe SeqPointer waarbij de SeqPointer waarmee de functie is
        // aangeroepen en other zijn geconcateneerd
        SeqPointer<T> concat(SeqPointer<T> const &other) const;

        // Retourneert de reverse complement van inside
        SeqPointer<T> inverse(SeqPointer<T> const inside) const;

        // Past de meegegeven varianten toe op de SeqPointer waarmee de functie is
        // aangeroepen en retourneert de nieuwe SeqPointer
        SeqPointer<T> apply(std::vector<Variant const*> varianten) const;
};

// Constructor voor een lege SeqPointer
template <typename T>
SeqPointer<T>::SeqPointer():ingang(nullptr), uitgang(nullptr), aantalVakjes(0), grootte(sizeof(T)*8), maxGrootte(grootte / 2) {
} // SeqPointer<T>::SeqPointer

// Copy constructor van de klasse
template <typename T>
SeqPointer<T>::SeqPointer(SeqPointer<T> const &seqpointer): SeqPointer() {
    vernietig();
    Vakje<T> * hulp = seqpointer.ingang;
    while(hulp != nullptr) {
        voegachter(hulp->data, hulp->rest); 
        hulp = hulp->volgende;
    } // Kopieer de hele pointerlijst
} // SeqPointer<T>::SeqPointer

// Constructor voor als een string is meegegeven
template <typename T>
SeqPointer<T>::SeqPointer(std::string const sequentie): SeqPointer() {
    seqpointerMaken(sequentie);
} //SeqPointer<T>::SeqPointer

// Retourneert de private variabele aantalVakjes
template <typename T>
int SeqPointer<T>::getaantalVakjes() const {
    return aantalVakjes;
} // SeqPointer<T>::getaantalVakjes

// Retourneert de private variabele ingang
template <typename T>
Vakje<T>* SeqPointer<T>::getIngang() const {
    return ingang;
} // SeqPointer<T>::getIngang

// Retourneert de lengte in symbolen van de SeqPointer waarmee
// de functie is aangeroepen
template <typename T>
int SeqPointer<T>::length() const {
    Vakje<T> *hulp = ingang;
    int size = 0; // Hier komt de lengte in te staan
    while(hulp != nullptr) {
        if(hulp->rest == 0) {
            size += maxGrootte; // Vakje is vol
        }
        else {
            size += maxGrootte - (hulp->rest/2); // Rest moet eraf gehaald worden
        }
        hulp = hulp->volgende;
    }
    return size;
} // SeqPointer<T>::length


// Zoekt het Vakje waarin het symbool op plek positie zich bevindt en retourneert
// dat Vakje
template <typename T>
Vakje<T>* SeqPointer<T>::vindVakje(int const position) const {
    Vakje<T> *hulp = ingang;
    int vakje = position/maxGrootte; // Hoeveelste vakje het is
    if(position < 0 || position > length()) {
        vakje = -1;
        return nullptr;
    } // Positie is buiten de array
    for(int i = 0; i < vakje; i++) {
        if(hulp != nullptr) {
            hulp = hulp->volgende;
        }
        else {
            vakje = -1;
            return nullptr;
        }
    }
    return hulp;
} // SeqPointer<T>::vindVakje

// Retourneert uit de SeqPointer het symbool dat op position staat
template <typename T>
char SeqPointer<T>::char_at(int const position) const {
    Vakje<T> *hulp = vindVakje(position);
    int plek = 0;
    T extra = T(0);
    char kar = 'X';
    if(vindVakje == nullptr) {
        std::cerr << "Position bevindt zich buiten de sequentie" << std::endl;
        return kar;
    }
    if(hulp != nullptr) {
        plek = (position % maxGrootte)*2; // plek van position in het vakje
        extra = hulp->data >> (grootte - plek - 2);
        switch(extra & 0b11) {
            case 0b00: kar = 'A';
            break;
            case 0b01: kar = 'C';
            break;
            case 0b10: kar = 'G';
            break;
            case 0b11: kar = 'T';
        }
    }
    return kar;
} // SeqPointer<T>::char_at

// Deze functie retourneert true als de sequentie en de SeqPointer waarmee
// de functie is aangeroepen gelijk zijn en anders false
template <typename T>
bool SeqPointer<T>::equal(SeqPointer<T> const &sequentie) const {
    Vakje<T> *hulp = ingang;
    Vakje<T> *hulpSeq = sequentie.ingang;
    while(hulp != nullptr && hulpSeq != nullptr) {
        if(hulp->data ^ hulpSeq->data != T()) {
            return false;
        } // De sequenties zijn verschillend
        if(hulp->data ^ hulpSeq->data == T() && hulp->rest != hulpSeq->rest) {
            return false;
        } // De sequentie lijken op elkaar omdat de ene allemaal A's heeft aan het
          // einde en de ander korter is en met nullen is opgevuld maar ze zijn dan
          // wel verschillend
        hulp = hulp->volgende;
        hulpSeq = hulpSeq->volgende;
    }
    if(hulp != nullptr || hulpSeq != nullptr) {
        return false;
    } // Een van de sequenties is een of meer vakjes langer
    return true;
} // SeqPointer<T>::equal

// Voegt een nieuw Vakje toe achteraan de SeqPointer waarmee de functie
// is aangeroepen. In data komt nieuwVakje te staan en rest0 wordt de rest
// van het nieuwe Vakje
template <typename T>
void SeqPointer<T>::voegachter(T const nieuwVakje, int const rest0) {
    Vakje<T> *hulp = new Vakje<T>;
    hulp->data = nieuwVakje;
    if(ingang == nullptr) {
        ingang = hulp;
    } // De SeqPointer is dan leeg
    else {
        uitgang->volgende = hulp;
    }
    hulp->vorige = uitgang;
    uitgang = hulp;
    hulp->volgende = nullptr;
    aantalVakjes++;
    hulp->rest = rest0;
} // SeqPointer<T>::voegachter

// Verwijdert het voorste vakje van de SeqPointer
// als die er is
template <typename T>
void SeqPointer<T>::voorVerwijderen() {
    Vakje<T> *hulp = ingang;
    if(ingang != nullptr) {
        ingang = ingang->volgende;
        if(ingang != nullptr) {
            ingang->vorige = nullptr;
        } // Als er minstens twee vakjes zijn
        else {
            uitgang = nullptr;
        } // Als er één vakje is
        delete hulp;
        hulp = nullptr;
        aantalVakjes--;
    } // Er moet minstens één vakje zijn
} // SeqPointer<T>::voorVerwijderen

// Verwijdert alle vakjes uit de SeqPointer
template <typename T>
void SeqPointer<T>::vernietig() {
    while(ingang != nullptr) {
        voorVerwijderen();
    } // Zolang er nog vakjes zijn, doorgaan met verwijderen
} // SeqPointer<T>::vernietig

// Destructor voor SeqPointer
template <typename T>
SeqPointer<T>::~SeqPointer() {
    vernietig();
} // SeQPointer<T>::~SeqPointer

// Aan de hand van de string seq wordt bepaald wat de data van het Vakje
// moet zijn en wordt die data met type T geretourneerd. In rest komt te staan
// wat de rest van het vakje wordt
template <typename T>
T SeqPointer<T>::vakjeMaken(std::string const seq, int &rest) const {
    int seqgrootte = seq.length();
    int teller = 0;
    T info = T();
    while(teller < seqgrootte) {
        info = info << 2; // Opschuiven zodat het nieuwe symbool kan worden gezet
        switch(seq.at(teller)) {
            case 'A': info = info | 0b00; // A = 00
                break;
            case 'C': info = info | 0b01; // C = 01
                break;
            case 'G': info = info | 0b10; // G = 10
                break;
            case 'T': info = info | 0b11; // T = 11
        } // Het nieuwe symbool zetten door eerst te kijken welke het is
        teller++;
    }
    rest = grootte - seqgrootte*2;
    info = info << rest; // Ervoor zorgen dat de rest aan de rechterkant van info zit
    return info;
} // SeqPointer<T>::vakjeMaken

// Zet de string sequentie om in de Vakjes die worden toegevoegd aan de
// SeqPointer waarmee de functie is aangeroepen
template <typename T>
void SeqPointer<T>::seqpointerMaken(std::string const &sequentie) {
    int sequentieSize = sequentie.size();
    char kar;
    int teller = 0; // Telt hoeveel symbolen voorbij zijn gekomen
    int rest = 0;
    std::string seq = ""; // Hier komt de sequentie die in het Vakje zal worden gestopt
    T info = T();
    for(int i = 0; i < sequentieSize; i++) {
        kar = sequentie.at(i);
        if(teller < maxGrootte) {
            if(kar == 'A' || kar == 'C' || kar == 'G' || kar == 'T') {
                seq += kar;
                teller++;
            } // Controleren of het wel een toegestaan symbool is
            else {
                std::cerr << "Geen geldige DNA-type in de string" << std::endl;
                vernietig();
                return; 
            }
        }
        if(teller == maxGrootte) {
            info = vakjeMaken(seq, rest);
            voegachter(info, rest);
            seq = "";
            teller = 0;
        } // Het maximaal aantal symbolen om het Vakje te vullen, is dan bereikt
    }
    if(teller != 0) {
        info = vakjeMaken(seq, rest);
        voegachter(info, rest);
    } // Als de laatste Vakjes nog niet zijn toegevoegd is omdat het niet
      // een vol Vakje maakt
} // SeqPointer<T>::seqpointerMaken

template <typename T>
void SeqPointer<T>::drukAf() const {
    Vakje<T> *hulp = ingang;
    T temp = T();
    while(hulp != nullptr) {
        int tempsize = grootte-2;
        for(int i = tempsize; i >= 0; i--) {
            temp = hulp->data >> i;
            i--;
            switch(temp & 0b11) {
                case 0b00: std::cout << "A";
                break;
                case 0b01: std::cout << "C";
                break;
                case 0b10: std::cout << "G";
                break;
                case 0b11:
                default: std::cout << "T";
                break;
            }
        }
        hulp = hulp->volgende;
    }
    std::cout << std::endl;
}

// Vult de data van Vakje "vakje" aan met de data van Vakje "nieuw". De data van "nieuw"
// die niet in "vakje" pas wordt naar links in de integer geschoven. De rest van beide Vakjes
// wordt ook aangepast
template <typename T>
void SeqPointer<T>::aanvullen(Vakje<T> *vakje, Vakje<T> *nieuw) const {
    if(nieuw == nullptr) {
        return;
    } // Er hoeft dan niks te gebeuren
    else if(vakje == nullptr) {
        vakje = nieuw;
        nieuw->data = T(0);
        nieuw->rest = grootte;
        return;
    } // Als vakje een nullptr is, wordt alles van nieuw in vakje gezet
    int opschuiven = (grootte - vakje->rest); // Hoeveel de data in nieuw moet opschuiven
    T hulp = nieuw->data >> opschuiven; // Data van nieuw dat bij vakje gaat worden toegevoegd
    vakje->data |= hulp;
    vakje->rest -= (grootte - nieuw->rest);
    if(vakje->rest < 0) {
        nieuw->data = nieuw->data << (grootte - opschuiven);
        nieuw->rest = grootte + vakje->rest;
        vakje->rest = 0;
    } // Dan zijn er nog symbolen over die in nieuw blijven staan
    else {
        nieuw->data = T(0);
        nieuw->rest = grootte;
    }
} // SeqPointer<T>::aanvullen

// Retourneert de SeqPointer waarmee de functie is aangeroepen
template <typename T>
SeqPointer<T> SeqPointer<T>::copy() const {
    Vakje<T> *hulp = ingang;
    SeqPointer<T> resultaat;
    while(hulp != nullptr) {
        resultaat.voegachter(hulp->data, hulp->rest);
        hulp = hulp->volgende;
    }
    return resultaat;
} // SeqPointer<T>::copy

// De meegegeven sequentie wordt gekopieerd in de SeqPointer waarmee de functie is aangeroepen
template <typename T>
void SeqPointer<T>::copy2(SeqPointer<T> const &sequentie) {
    Vakje<T> *hulp = sequentie.ingang; // Houdt de Vakjes van sequentie bij
    vernietig(); // Als er nog iets in zat dan moet dat eerst weg
    while(hulp != nullptr) {
        voegachter(hulp->data, hulp->rest);
        hulp = hulp->volgende;
    }
} // SeqPointer<T>::copy

// Maakt een nieuwe SeqPointer resultaat waarbij de de SeqPointer other wordt geplakt
// achter de SeqPointer waarmee de functie is aangeroepen
template <typename T>
SeqPointer<T> SeqPointer<T>::concat(SeqPointer<T> const &other) const {
    SeqPointer<T> resultaat = copy();
    SeqPointer<T> ander(other); // Zodat other niet wordt aangepast
    Vakje<T> *hulp = ander.ingang;
    while(resultaat.uitgang != nullptr && hulp != nullptr) {
        aanvullen(resultaat.uitgang, hulp);
        if(hulp->rest != grootte) {
            resultaat.voegachter(hulp->data, hulp->rest);
        }
        hulp = hulp->volgende;
    }
    return resultaat;
} // SeqPointer<T>::concat

// Retouneert een nieuwe SeqPointer resultaat met een sequentie die van start
// tot end loopt van de SeqPointer waarmee de functie is aangeroepen
template <typename T>
SeqPointer<T> SeqPointer<T>::slice(int const start, int const end) const {
    SeqPointer<T> resultaat;
    SeqPointer<T> temp = copy();
    int laatste = 0;
    Vakje<T> *eersteVakje = temp.vindVakje(start);
    Vakje<T> *endVakje = temp.vindVakje(end);
    Vakje<T> *hulp = eersteVakje;
    T info = T(0);
    int plekEerst = (start % maxGrootte)*2; // Plek in het Vakje in resultaat van start
    int plekLaatst = ((end - start - 1) % maxGrootte)*2; // Plek in het Vakje in resultaat van end
    int rest = 0;

    laatste = (end - start - 1)/maxGrootte; // Wordt het laatste Vakje in resultaat
    if(eersteVakje == nullptr || endVakje == nullptr || start > end) {
        std::cerr << "Start en eind voldoen niet aan de eisen." << std::endl;
        return SeqPointer<T>();
    }
    if(start == end) {
        return SeqPointer<T>();

    } // Dan zitten er geen symbolen in de SeqPointer
    info = eersteVakje->data << plekEerst;
    rest = eersteVakje->rest + plekEerst;
    resultaat.voegachter(info, rest); // Eerste Vakje alvast toevoegen
    hulp = hulp->volgende;

    for(int i = 0; i < laatste; i++) {
        aanvullen(resultaat.uitgang, hulp);
        resultaat.voegachter(hulp->data, hulp->rest);
        hulp = hulp->volgende;
    }
    if((resultaat.length() % maxGrootte)*2 <= plekLaatst) {
        aanvullen(resultaat.uitgang, hulp);
    }
    info = resultaat.uitgang->data >> (grootte - plekLaatst - 2); // Vanaf end
    resultaat.uitgang->data = info << (grootte - plekLaatst - 2); // alles weghalen
    resultaat.uitgang->rest = grootte - ((end - start) % maxGrootte)*2;

    if(resultaat.uitgang->rest == grootte) {
        resultaat.uitgang->rest = 0;
    } // Anders zou rest = grootte zijn bij een vol Vakje
    while(resultaat.uitgang->rest < 0) {
        resultaat.uitgang->rest += grootte;
    } // Rest kan negatief zijn maar door herhaald grootte op te tellen
      // krijg je de goede rest
    return resultaat;
} // SeqPointer<T>::slice


// Hulpfunctie voor inversie.
// Maakt van de meegegeven SeqPointer een reverse complement SeqPointer.
template <typename T>
SeqPointer<T> SeqPointer<T>::inverse(SeqPointer<T> const inside) const{
    SeqPointer<T> resultaat;
    Vakje<T> *nieuw = inside.uitgang;
    int teller = 0; // Teller voor de lengte van hoe veel er in een vakje is gestopt
    T hulp = T(); // Hulp T, waarin de nieuwe vakjes worden opgebouwd
    int tempSize = grootte - nieuw->rest; // Grootte van laatste vakje (houdt rekening met rest)
    int rest = 0;

    while(nieuw != nullptr) {
        T info = nieuw->data;
        if(nieuw == inside.uitgang) {
            info = info >> nieuw->rest;
        }
        for(int j = 0; j < tempSize; j += 2) {
            T temp = info >> j;
            hulp = hulp << 2;
            teller++;
            switch(temp & 0b11) {
                case 0b00: hulp = hulp | 0b11;
                break;
                case 0b01: hulp = hulp | 0b10;
                break;
                case 0b10: hulp = hulp | 0b01;
                break;
                case 0b11:
                default: hulp = hulp | 0b00;
                break;
            } // Complement van temp komt in hulp
            if(teller == maxGrootte) {
                resultaat.voegachter(hulp, 0);
                hulp = T(0);
                teller = 0;
            } // Hulp zit vol, dus komt in de vector en wordt gerest
        } // Gaat door elke vakje van achter naar voor in de sequentie (oftewel van voor naar achter)
        tempSize = grootte; // reset tempSize
        nieuw = nieuw->volgende;
    } // Gaat door elk vakje van achter naar voor

    if(teller != 0) {
        rest = (maxGrootte-teller)*2;
        hulp = hulp << rest;
        resultaat.voegachter(hulp, rest);
    } // Teller is nog niet 0, dus er is nog een rest over in hulp
    return resultaat; 
} // SeqPointer<T>::inverse

// Past de varianten in de meegegeven vector één voor één toe aan de sequentie
// en retourneert uiteindelijk de resultaat sequentie-pointer
template <typename T>
SeqPointer<T> SeqPointer<T>::apply(std::vector<Variant const*> varianten) const {
    int vectorSize = varianten.size();
    SeqPointer<T> product = copy();
    for(int k = 0; k < vectorSize; k++) {
        Variant const* var = varianten.at(k); // Variant op plek k
        if(var->get_eind() > product.length()) {
            std::cerr << "Deze variant komt buiten de grenzen van de sequentie" << std::endl;
            return SeqPointer<T>();
        }
        SeqPointer<T> const prefix = product.slice(0, var->get_start());
        SeqPointer<T> const suffix = product.slice(var->get_eind(), product.length());

        if(var->get_type() == "inv") {
            SeqPointer<T> temp(prefix.concat(inverse(product.slice(var->get_start(), var->get_eind())))); // prefix + inverse
            product.copy2(temp.concat(suffix)); // temp + suffix
        } // inversie
        else {
            SeqPointer<T> const insert(var->get_inserted());
            SeqPointer<T> temp = prefix.concat(insert); // prefix + insert
            product.copy2(temp.concat(suffix)); // temp + suffix
        } // Deletie, Deletie-Insertie, Insertie: kan allemaal methetzelfde algoritme 
        delete var;      
    }
    varianten.clear();
    return product;
} // SeqPointer<T>::apply

#endif