//Autor ALeksandr Rahacheuski 323631
//temat std::views

/* Widoki pozwalają operować na danych bez ich kopiowania.
Na przykład, widok filtrujący elementy z kolekcji pozwala pracować tylko na tych elementach,
które spełniają określone kryteria, nie tworząc nowej, zmodyfikowanej kolekcji.

Umożliwiają tworzenie złożonych operacji przez łączenie prostszych widoków.
Na przykład, można połączyć widoki do filtrowania, transformacji i odwracania kolekcji w jednym, spójnym ciągu operacji


Kod korzystający z widoków często jest bardziej zwięzły i czytelny, co ułatwia zrozumienie i utrzymanie.
Naprzykad zamiast skomplikowanych  metod uywajcych  kilka petli w petlach i if-ow mozna uzyc jednej linijki uzywajac std:: ranges i std::views


Widoki sa bardzo elastyczne, mogą być używane z różnymi rodzajami danych ( tablicami, kontenerami, mapami)
Widoki takie jak std::views::values lub std::views::keys pozwalają na łatwe iterowanie tylko po wartościach lub kluczach mapy.
Wiec to  czyni widoki uniwersalnym narzędziem w standardowej bibliotece C++.

std::views jest najbardziej efektywny w operacjach takich jak filtrowanie, transformacja czy łączenie danych, a niekoniecznie w bardziej skomplikowanych logikach biznesowych.


Dodatkowo istnie  widok dla std::string ktore ma takie same zalety co  i inne widoki, czyli

std::string_view pozwala na przeglądanie ciągów znaków bez konieczności ich kopiowania czy alokacji nowej pamięci.
Jest to szczególnie przydatne w funkcjach, które tylko odczytują ciąg znaków bez potrzeby jego modyfikacji.
Dodatkowo operacje na  std::string_view szybsze  niz na std::string, mozna porownac to uzywajac std::string.substr i std::string_view.substr
poniewaz zlozonosc czasowa dla std::string jest czasowa, a dla std::string_view const

*/

// std::string_view
/*size() i length() do pobierania długości widoku.
empty() do sprawdzania, czy widok jest pusty.
operator[], at(), front(), back() do dostępu do poszczególnych znaków.
data() do uzyskiwania wskaźnika na znaki.
substr() do tworzenia podwidoków.
remove_prefix() i remove_suffix() do modyfikowania widoku poprzez usunięcie znaków z początku lub końca.
compare() do porównywania z innymi ciągami.*/




#include <iostream>
#include <vector>
#include <string>
#include <string_view>
#include <cassert>
#include <ranges>
#include <map>

void* operator new(std::size_t count){
    std::cout << "   " << count << " bytes" << std::endl;
    return malloc(count);
}

void getString(const std::string& str){}

void getStringView(std::string_view strView){}


//dowod tego ze std::string_view nie allocuje pamieci
void testPamieci(){

    std::cout << std::endl;

    std::cout << "std::string" << std::endl;

    std::string large = "0123456789-123456789-123456789-123456789";
    std::string substr = large.substr(10);

    std::cout << std::endl;

    std::cout << "std::string_view" << std::endl;

    std::string_view largeStringView{large.c_str(), large.size()};
    largeStringView.remove_prefix(10);

    assert(substr == largeStringView);

    std::cout << std::endl;

    std::cout << "getString" << std::endl;

    getString(large);
    getString("0123456789-123456789-123456789-123456789");
    const char message []= "0123456789-123456789-123456789-123456789";
    getString(message);

    std::cout << std::endl;

    std::cout << "getStringView" << std::endl;

    getStringView(large);
    getStringView("0123456789-123456789-123456789-123456789");
    getStringView(message);

    std::cout << std::endl;

}


void printMap( const std::map<std::string, int>& mapa) {
    std::cout << "Printowanie mapy" << std::endl;
    for (const auto& pair : mapa) {
        std::cout << "Indeks: " << pair.first << ", Zawartosc " << pair.second << std::endl;
    }
}


//uzucie widokow w testowym zadaniu Fizzbuzz
auto generateFizzBuzz() {
    auto fizzbuzz = [](int i) {
        if (i % 3 == 0 && i % 5 == 0) return std::string{"FizzBuzz"};
        if (i % 3 == 0) return std::string{"Fizz"};
        if (i % 5 == 0) return std::string{"Buzz"};
        return std::to_string(i);
    };

    return std::views::iota(1, 22) | std::views::transform(fizzbuzz);
}

//tworzenie swego wasnego widoku

template<typename T>
class DoublingView : public std::ranges::view_base {
public:
    DoublingView(const std::vector<T>& vec) : data(vec) {}

    class iterator {
    public:
        using iterator_category = std::forward_iterator_tag;
        using value_type = T;
        using difference_type = std::ptrdiff_t;
        using pointer = const T*;
        using reference = const T&;

        iterator(pointer ptr) : ptr(ptr) {}

        T operator*() const { return *ptr * 2; }
        pointer operator->() { return &(operator*()); }

        iterator& operator++() {
            ++ptr;
            return *this;
        }

        iterator operator++(int) {
            iterator temp = *this;
            ++(*this);
            return temp;
        }

        friend bool operator==(const iterator& a, const iterator& b) {
            return a.ptr == b.ptr;
        }

        friend bool operator!=(const iterator& a, const iterator& b) {
            return a.ptr != b.ptr;
        }

    private:
        pointer ptr;
    };

    iterator begin() const { return iterator(data.data()); }
    iterator end() const { return iterator(data.data() + data.size()); }

private:
    std::vector<T> data;
};

template<typename T>
inline constexpr bool std::ranges::enable_view<DoublingView<T>> = true;




//kod jest stworzony dla testowania std::views ale jest to podklasa ranges wiec tutaj tez beda opisane ranges
//ponizej stestowane poszczegolne metody znajdujace sie w  srd::views, opisane to wszystko w main, bo  tworzenie wlasnych metod w tym przypadku nie jest uzyteczne


int main() {

    std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};


    // Tworzenie widoku za pomocą std::views
    auto evenNumbers = numbers | std::views::filter([](int n) { return n > 2 ; })// zmniejsza to ilosc kodu  zamiast if tylko 1 linijka
                                | std::views::transform([](int n) { return n * n * n; });

    std::cout << "Liczby wieksze niz 2 do trzeciej potegi: ";
    for (auto n : evenNumbers) {
        std::cout << n << " ";
    }
    std::cout << std::endl;
    std::cout<<" "<< std::endl;

//Sprawdzenie funkcji filtrowania  i transformacji parzyste razy 5
    std::cout<<"Sprawdzenie funkcji filtrowania  i transformacji parzyste razy 5 "<< std::endl;
    auto check = [](int i) { return i % 2 == 0; };
    auto operation = [](int i) { return i *5 ; };

    std::vector<int> input = {100, 55, 80, 2, -1};
    auto output = numbers | std::views::filter(check) |
                 std::views::transform(operation);

    for (auto i : output) {
        std::cout << i << std::endl;
    }

    std::cout<<" "<<std::endl;



// widac ze vector nie zmienil sie, czyli po drugim uzyciu  vector zostaje sie taki sam
// w razie potrzeby mozna urzyc std::reverse, ktora odwraca kolekcje
    auto odwrocone = numbers | std::views::reverse;

    std::cout << "Odwrocona kolejnosc: ";
    for (auto n : odwrocone) {
        std::cout << n << " ";
    }
    std::cout << std::endl;

    std::cout<<" "<< std::endl;



 // w widoku mozna przechowywac nie  tylko   kolekcje,  rowniez mozna przechowywac tylko 1 element
    std::cout<<" Przechowywanie jednego elementu"<< std::endl;
    auto singleElementView = std::views::single(77);
    std::cout << *singleElementView.begin() << '\n'; // dostac sie do elementu mozna w taki sposob

    for (int n : singleElementView) {
        std::cout << n << '\n';
    }

    std::cout<<" "<<std::endl;



    //std::views pozwala na  latwe iterowanie po kolekcjach co upraszcza kod,, robi go bardziej czytelnym zmniejsza ilosc bledow(trudniej pogubic sie z i i j)
    const int days = 4;
    const int maked_tasks = 3;
    std::cout<<"testowanie iota"<< std::endl;
    for (int i : std::views::iota(0, days)) { //nie trzeba pisa kilka razy i i j
        std::cout << "Dzien " << i << std::endl;
        for (int j : std::views::iota(0, maked_tasks)) {
            std::cout << " Zrobienie zadania " << j << std::endl;
        }
    }

    std::cout<<" "<<std::endl;
    //to same zadanie tylko  bez uzycia std::views
    /*
    for (int i = 0; i < days; ++i) { bez urzycia widokow
        std::cout << "Dzien " << i << std::endl;
        for (int j = 0; j < maked_tasks; ++j) {
            std::cout << " Zrobienie zadania " << j << std::endl;
        }
    }
    */



//tworzenie  widoku ze wszystkimi elementami
auto all_numbers = std::views::all(numbers);
    std::cout << "Tworzenie widoku dla calej kolekcji: ";
    for (auto n : all_numbers) {
        std::cout << n << " ";
    }
    std::cout << "\n\n";
    std::cout<<" "<<std::endl;

//sprawdzenie czy kolekcja jest pusta
    bool is_empty = std::ranges::empty(numbers);
    std::cout << "Widok pusty? metoda empty " << (is_empty ? "Tak" : "Nie") << "\n";
    std::cout<<" "<<std::endl;
    std::cout<<" "<<std::endl;


    // take - bierzemy pierwsze n elementow z kolecki
    auto first_three = std::views::take(numbers, 3);
    std::cout << "pierwsze 3 liczby: ";
    for (auto n : first_three) {
        std::cout << n << " ";
    }
    std::cout << "\n\n";
    std::cout<<" "<<std::endl;


    // drop -  opuskamu pierwsze n elementow z kolekcji
    auto skip_first_three = std::views::drop(numbers, 3);
    std::cout << "Testowanie Drop vektor bez pierwszych trzexh liczb: ";
    for (auto n : skip_first_three) {
        std::cout << n << " ";
    }
    std::cout << "\n\n";
    std::cout<<" "<<std::endl;



    // prrzyklad z mapa
    std::map<std::string, int> map_example = {{"one", 1}, {"two", 2}, {"three", 3}};
    printMap(map_example);

    // keys - zwraca klucze mapy
    auto map_keys = std::views::keys(map_example);
    std::cout << "Klucze mapy: ";
    for (const auto& key : map_keys) {
        std::cout << key << " ";
    }
    std::cout << "\n\n";
    std::cout<<" "<<std::endl;

    // values - zwraca znaczenia z mapy
    auto map_values = std::views::values(map_example);
    std::cout << "znaczenia mapy: ";
    for (const auto& value : map_values) {
        std::cout << value << " ";
    }
    std::cout << "\n\n";
    std::cout<<" "<<std::endl;



    // mozliwosc elementy kiedy spelniony jest warunek ponizsze 2 metody latwo moga zamienic sobie
    // take_while
    auto less_than_three = std::views::take_while(numbers, [](int n){ return n < 3; });
    std::cout << "Liczby mniejsze od 3 testowanie take_while: ";
    for (auto n : less_than_three) {
        std::cout << n << " ";
    }
    std::cout << "\n\n";
    std::cout<<" "<<std::endl;

    //mozliwosc "usunac"  elementy kiedy spelniaja oni warynki
    // drop_while
    auto greater_than_six = std::views::drop_while(numbers, [](int n){ return n < 3 ; });
    std::cout << "Liczby wieksze od 2 drop_while ";
    for (auto n : greater_than_six) {
        std::cout << n << " ";
    }
    std::cout << "\n";
    std::cout<<" "<<std::endl;


    // join mozliwosc konkatenacji
    std::vector<std::vector<int>> test_numbers = {{1, 2}, {3, 4}, {5, 6}};
    auto joined_numbers = std::views::join(test_numbers);
    std::cout << "dodane do siebie vectory join: ";
    for (const auto& vec : test_numbers) { // Iteracja po wektorach
        for (auto n : vec) { // Iteracja po elementach każdego wektora
            std::cout << n << " ";
        }
    std::cout << std::endl;
    }
std::cout << "\n\n";

    // counted -mozliwosc tworzenia widoku na pierwsze n elementow od zadanego iteratoru troche podobne do takes

    auto first_five_numbers = std::views::counted(numbers.begin(), 5);
    std::cout << "pierwsze 5 liczb metoda counted: ";
    for (auto n : first_five_numbers) {
        std::cout << n << " ";
    }
    std::cout << "\n";



    //testowanie  metody fizzbuzz
    std::cout<<"FizzBuzz Test"<<std::endl;
    auto fizzBuzzSequence = generateFizzBuzz();
    for (const auto& value : fizzBuzzSequence) {
        std::cout << value << " ";
    }
    std::cout << std::endl;

    std::cout<<""<<std::endl;
    std::cout<<"sprawdzenie utworzonego widoku"<<std::endl;
    DoublingView<int> doubledNumbers(numbers);


    for (auto n : doubledNumbers) {
        std::cout << n << " ";
    }

    std::cout<<" "<<std::endl;
    std::cout<<" "<<std::endl;
    testPamieci();

}

//https://en.cppreference.com/w/cpp/ranges
//https://en.cppreference.com/w/cpp/ranges/view
//https://www.modernescpp.com/index.php/c-17-what-s-new-in-the-library/
//https://www.geeksforgeeks.org/class-stdstring_view-in-cpp-17/
//https://www.geeksforgeeks.org/cpp-20-ranges-library/
//https://medium.com/@chittaranjansethi/exploring-the-differences-std-string-vs-std-string-view-d1c7015162f0#:~:text=Performance%20and%20efficiency%3A%20If%20your,without%20copying%20or%20modifying%20it.
