﻿/* Grzegorz Krawczyk */
/* Boost Accumulators jest biblioteka ulatwiajaca przechowywanie i wykonywanie operacji matematycznych na danych zawartych w akumulatorach.
 * W bibliotece zdefiniowany jest szereg operacji matematycznych i statystycznych:
* count, covariance, density, error_of<mean>, extended_p_square, extended_p_square_quantile and variants, kurtosis,
* max, mean and variants, median and variants, min, moment, p_square_cumulative_distribution, p_square_quantile and variants
* peaks_over_threshold and variants, pot_quantile and variants, pot_tail_mean, skewness, sum and variants, tail, coherent_tail_mean
* non_coherent_tail_mean, tail_quantile, tail_variate, tail_variate_means and variants, variance and variants, weighted_covariance
* weighted_density, weighted_extended_p_square, weighted_kurtosis, weighted_mean and variants, weighted_median and variants, weighted_moment
* weighted_p_square_cumulative_distribution, weighted_p_square_quantile and variants, weighted_peaks_over_threshold and variants, weighted_skewness
* weighted_sum and variants, non_coherent_weighted_tail_mean, weighted_tail_quantile, weighted_tail_variate_means and variants, weighted_variance and variants 

Mozliwe jest definiowanie nowych akumulatorow, dodawanie funkcjonalnosci, operacji i wykorzystywanie ich w rownie prosty sposob jak te zdefiniowane w bibliotece.
Biblioteke mozna wykorzystywac zarowno dla typow wbudowanych i typow uzytkownika. 
W celu operacji na typach użytkownika niezbedne jest zdefiniowanie operatorow pozwalajacych np na porownanie 2 obiektow.
*/


#include <iostream>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
/* Korzystanie z operacji matematycznej wymaga dodania odpowiedniego naglowka */
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/moment.hpp>
#include <boost/accumulators/statistics/min.hpp>
/* Wykorzystywane w funkcji usingMyData()
 * aby nie dokonywac wszystkich danych z akumulatora uzytkownika
 * Wykorzystywane funkcje boost::bind() oraz boost::ref()*/
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <vector>
/* Obliczanie sum wazonej wymaga w funkcji weightExample 
 * wymaga dolaczenia naglowka*/
#include <boost/accumulators/statistics/weighted_sum.hpp>
/*niezbedne do tworzenia nowych akumulatorow*/
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/parameters/sample.hpp>


/*Przyklad akumulatora sumujacego dane
Tworzenie nowych akumulatorow powinno odbywac sie z zachowaniem wyznaczonych regul 
(dokumentacja: Accumulators Concept) i byc zdefiniowej w odpowiedniej przestrzeni nazw*/
namespace boost {           
    namespace accumulators {   
        namespace impl { 
template<typename Sample>
struct mean_accumulator : accumulator_base{
    typedef Sample result_type;
    mean_accumulator(dont_care) {}

    template<typename Args>
    result_type result(Args const &args) const{
        return sum(args[accumulator]) / count(args[accumulator]);
    }
};
}}}
/*Dodawanie nowej funkcjonalnosci (feature) dla powstalego akumulatora*/
namespace boost { 
    namespace accumulators { 
        namespace tag {
struct mean : depends_on< count, sum > {
    typedef impl::mean_accumulator< mpl::_1 > impl;
};
}}}

/*Dodawanie nowego extractora - pozwalajacego na uzyskanie wyniku dla danego extractora i jego feture'a*/
namespace boost {
    namespace accumulators {              
        namespace extract {              
    extractor< tag::mean > const mean = {};
                                        
}
using extract::mean;                    
}} 

/* deklaracja uzywania namespace - zwieksza przejrzystosc kodu */
using namespace boost::accumulators;

/* Funkcja pokazujaca jak wykorzystac biblioteke do wykonywania operacji matematyczne/statystycznych na naszym akumulatorze danych (np wektorze z std, ktory byl wykorzystany do  zbierania danych, a potrzebujemy wykorzystac dane w nim zgromadzone do obliczen). 
 * Przyklad ten demonstruje tworzenie obiektow typu boost::accumulators  bez potrzeby kopiowania duzych zbiorow danych */
int usingMyData();

/* Przyklad wykorzystania opcjonalnego parametru weight z szablony accumulator_set*/
int weightExample();

/* Zastosowanie opcji droppable- pozwala na pominiecie wykonywania wybranych operacji
 * podczas dodawania kolejnych danych do akumulatora
 *
 * Stosowanie tej opcji zmienia zachowanie akumulatora, w przypadku korzystania zalecane jest zapoznanie sie z sekcja Droppable Accumulators, w ktorej wypunktowane zostaly reguly 
 * zaleznosci miedzy funkcjami droppable i non-droppable w akumulatorach
 * */
int droppableAccumulators();

/*Funkcja wykorzystujaca akumulator uzytkownika*/
int userAccumulator();

int main()
{
/* Definicja akumulatora przechowujacego dane typu double,
   dla ktorych obliczane beda: wartosc min,mean,2nd moment

   Parametry accumulator_set:   
   accumulator_set< typename data, typename Features, typename Weight = void >
   struct accumulator_set;

   accumulator_set<> : szablon akumulatora
   typename data     : typ przechowywanych danych
   typename Features : wektor operacji wykonywanych na danych
   typename Weight   : (opcjonalne) wykorzystywane w pewnych typach akumulatorow, przyklad w funkcji weightExample(); 
   struct accumulator: nazwa wyjsciowego akumulatora  
*/
    accumulator_set<double, stats<tag::min, tag::mean, tag::moment<2> > > acc;

    /* Dodanie danych do akumulatora */
    acc(1.2);
    acc(2.3);
    acc(3.4);
    acc(4.5);

    /* Wyswietlanie danych */
    std::cout << "Mean:   " << mean(acc) << std::endl;
    
    /* Inny sposob*/
    std::cout << "Min: " << extract_result < tag::min > ( acc ) << std::endl;
   
    /* definiowanie wlasnych ekstraktorow */ 
    extractor<tag::min> min_;
    std::cout << "Min: "  << min_(acc) << std::endl;
   
    /* Ponizsza funkcja pokazuje sposob wykorzystania funkcji statystycznych z biblioteki accumulators dla danych zgromadzonych we wlasnym akumulatorze 
     * bez potrzeby kopiowania danych
     * Aby korzystac z tej metody nalezy zalaczyc naglowki:
     * boost/bind.hpp oraz boost/ref.hpp
     **/
    usingMyData();
    weightExample();
    droppableAccumulators();

    return 0;
}

int usingMyData(void){
    std::vector<int> data;
    data.push_back(3);
    data.push_back(5);
    data.push_back(9);

    /*Definicja akumulatora do obliczania wartosci sredniej */
    accumulator_set< int, features< tag::mean > > acc;

    std::for_each( data.begin(), data.end(), boost::bind<void>( boost::ref(acc), _1 ) );

    /* Nie ma potrzeby konwersji int do typu double aby obliczyc srednia (double) */
    std::cout << "Wartosc srednia z 3 int: " << mean(acc) << std::endl;
    
    return 0;    
}

int weightExample(){
    accumulator_set< int, features< tag::weighted_sum >, int > acc;
    acc(1, weight = 2); //   1 * 2
    acc(2, weight = 4); //   2 * 4
    acc(3, weight = 6); // + 3 * 6
    std::cout << sum(acc) << std::endl;
}

int droppableAccumulators(){
    accumulator_set< double, features< tag::count, droppable<tag::sum> > > acc;
    acc(3.0);
    acc(2.0);
    
    // Ponizsza instrukcja powoduje wstrzymanie sumowania nowych danych, pomimo dodawania ich do akumulatora
    acc.drop<tag::sum>();
    
    // dodanie kolejnej danej do akumulatora
    acc(1.0);

    // Wyswietlenie wyniku - potwierdzenie dzialania
    std::cout << "Przyklad droppable (funkcja count oraz sum dla liczb 3.0, 2.0, 1.0)" << "\n" << count(acc) << ' ' << sum(acc)<< std::endl;

    return 0;
}

int userAccumulator(){
    impl::mean_accumulator<int> acc;
}
