/* Jakub Skierbiszewski 4I2
 * Opis kontenera slist
 * Slist jest listą jednokierunkową, tzn taką, w której element jest połączony z następnym
 * elementem ale nie z poprzednim. Zwiększa to wydajność w porównaniu z listą dwukierunkową
 * zwłaszcza jeżeli w liście znajduje si dużo małych obiektów.
 *
 */

#include <iostream>

//tutaj znajduje sie definicja slist
#include <slist.h>

using namespace std;


//funkcja pomocnicza dla slist<int> wyświetlająca jej zawartość
void printList(slist<int>& L)
{
    slist<int>::iterator ite;
    for( ite=L.begin(); ite!=L.end(); ite++)
        cout << *ite << " ";   // Wyswietlenie elementow listy na standartowe wyjscie
    cout<<endl;
}

int main()
{
    cout << "Program demonstrujacy dzialanie slisty" << endl;

    /* Zmienne pomocnicze */
    int i;

    /* deklaracja slisty
     * Slista jest parametryzowana typem, który przechowuje.
     */
    slist<int> L;

    /* deklaracja iteratora dla slisty
     * Iterator slisty jest jednokierunkowy.
     * Iterator służy do poruszania się po liście
     * i do dostępu do elementow listy
     */
    slist<int>::iterator ite;

    /* dodawanie elementow do listy */
    L.push_front(10);   // element 10 zostanie dodany na początek listy

    for (i=0;i<20;i++)  // dodawanie elementow w pętli
        L.push_front(i);

    L.front();          // Dostęp do wartości przechowywanej w pierwszym elemencie listy

    /* Operacje na iteratorach */
    ite = L.begin();    // ite wskazuje na pierwszy element listy
    ite = L.end();      // ite wskazuje na element za ostatnim elementem listy

    ite = L.begin();
    ite++;              // ite wskazuje na nastepny po pierwszym element listy

    *ite;               // dostep do wartosci wskazywanej przez ite

    //przejscie przez wszystkie elementy listy z uzyciem iteratora
    cout << "Zawartosc slisty:" << endl;
    for( ite=L.begin(); ite!=L.end(); ite++)
        cout << *ite << " ";   // Wyswietlenie elementow listy na standartowe wyjscie
    cout<<endl;

    /* Wykorzystanie iteratorow do wykonania operacji na elementach listy */
    //Ustawienie ite na pozycji pierwszego wystapienia elementu w liscie o wartosci 13
    //lub na koncu listy jezeli element nie wystapil
    cout << "Przeszukiwanie listy:" << endl;
    for( ite=L.begin(); !(*ite==13 || ite==L.end()); ite++);

    if(ite == L.end())  //nie znaleziono elementu:
        cout << "Lista nie zawiera elementu 13" << endl;

    else                // ite jest ustawiony na pozycji pierwszego wystapienia 13:
    {
        cout << "Znaleziony element: " << *ite << endl;

        //Wstawianie elementow do listy za elementem wskazywanym przez iterator
        ite = L.insert_after(ite, 66);  //metoda zwraca iterator wskazujacy na element wstawiony
        cout << "element wstawiony: " << *ite <<endl;

        L.insert_after(ite, 16, 1);     //metoda wstawia 16 elementow o wartosci 1 za elementem wskazywanym przez ite

            cout << "Zawartosc slisty po dodaniu elementow:" << endl;
            printList(L);

        //Usuwanie elementow z listy za elementem wskazywanym przez iterator
        L.erase_after(ite); //metoda zwraca iterator wskazujacy nastepny element za elementem usunietym
                            //Iterator pozostaje ważny po takiej pperacji

            cout << "Zawartosc slisty po usunieciu elementu:" << endl;
            printList(L);

        L.erase_after(ite, L.end());    //usuniecie wszystkich elementów od elemenu nastepnego za ite az do elementu L.end()

            cout << "Zawartosc slisty po usunieciu zakresu elementow:" << endl;
            printList(L);


        /* Istnieją również metody insert i erase (odpowiednio wstawiające i usuwające elementy przed
         * elementem wskazywanym przez iterator), sa oblugiwane w sliscie natomiast sa malo wydajne;
         * optymalnym rozwiązaniem jest stosowanie insert_after i erase_after. Jezeli nie
         * mozna uniknac uzywania insert lub delete nalezy zweryfikowac trafnosc wyboru kontenera
         * Prawdopodobnie lepszym rozwiazaniem w takim przypadku byloby zastosowanie kontenera list.
         *
         * L.insert(L.end());
         * L.erase(L.end());
         */
    }

    /* Usuwanie elemetow o zadanej wartosci */
    L.remove(21);   //Usuwa wszystkie elementy o wartosci 66

    for(i=0; i<10;i++)  //Wstawienie 10 jednakowych wpisow:
        L.push_front(199);
        cout << "Zawartosc slisty po dodaniu 10 jednakowych elementow:" << endl;
        printList(L);

    L.unique();     //Usuwa wszystkie duplikujące sie wpisy z listy pozostawiając jedną kopię
                    //W liscie pozostanie tylko jeden element o wartosci 199.

        cout << "Zawartosc slisty po wykonaniu unique():" << endl;
        printList(L);

    L.empty();      //Sprawdzenie czy lista byla pusta, true jesli tak.
    cout << "Lista jest pusta: " << L.empty() << endl;

    L.pop_front();  //Usuniecie elementu z początku listy

    cout << "Usuniecie wszystkich elementow listy:" << endl;
    L.clear();      //Usunięcie wszystkich elementów w liście

    cout << "Lista jest pusta: " << L.empty() << endl;


    // Ponowne zapełnienie listy
    for (i=0;i<20;i++)              // dodawanie elementow w petli
        L.push_front(rand()%100);   // elementy losowe

    /* Dynamiczne tworzenie slist: */
    slist<int>* Lptr;           //wskaznik na liste
    Lptr = new slist<int> ();   //Utworzenie listy
    delete(Lptr);               //Usuniecie dynamicznie utworzonej listy

    Lptr = new slist<int> (L);  //Utworzona lista jest kopią listy L

        cout << "Lista przed sortowaniem:" << endl;
        printList(*Lptr);

    Lptr -> sort();             //Sortowanie stabilne listy, na podstawie operatora<.
                                //Wszystkie iteratory zachowują ważność po takim sortowaniu
        cout << "Lista po sortowaniu:" << endl;
        printList(*Lptr);

    /* Łączenie list */
    slist<int>::iterator begin,end;     //pomocnicze iteratory
    begin = L.begin();
    end = L.previous(L.end());          // end wskazuje na ostatni element a nie na element za ostatnim.

    Lptr->splice_after(Lptr->begin(),begin,end);    //Dołączenie elementów listy L na początku listy *Lptr
        //Pierwszy argument jest iteratorem *this wskazującym na elemet za którym nastąpi doklejenie
        //Dwa następne argumenty określają zakres elemetów do doklejenia (mogą wskazywać na inną listę)
        //Wszystkie iteratory muszą być ważne (tzn end nie może wskazywac na element za ostatnim)

        cout << "Lista po łączeniu:" << endl;
        printList(*Lptr);

    delete (Lptr);  //Usunięcie dynamicznie utworzonej listy
    L.clear();      //Usunięcie elementów z listy L


    /* slist jest wydajną kolejką FIFO: */

    L.push_front(); //Dodawanie elementów do kolejki

    //Pobranie elementu z kolejki:
    L.front();      //Dostęp do wartości w pierwszym elemencie
    L.pop_front();  //Usunięcie pierwszego elementu

    return 0;
}
