/*
Opis algorytmu accumulate z biblioteki standardowej C++.
Autor: Piotr Kulik
_______________________________________________________________________________

Algorytm: accumulate
Plik nagłowkowy: numeric

Opis:
Algorytm występuje w dwóch wersjach - ogólnej i specjalizowanej.
 * Wersja ogólna:
	template<class InputIterator, class Type, class BinaryOperation>
	   Type accumulate(
		  InputIterator _First,
		  InputIterator _Last,
		  Type _Val,
		  BinaryOperation _Binary_op
	   );

	- algorytm kumuluje kolekcję elementów w jedną wartość licząc
	  kolejne wyniki operacji dwuargumentowej # z argumentami:
	  dotychczas zakumulowanej wartości i kolejnego elementu
	- ściśle: wynik powstaje w wyniku operacji
	       wynik = ((((_Val#e1)#e2)#e3)#...)#eN
	  gdzie e1,...,eN jest zakresem elementów wskazywanym przez parę iteratorów
	  _First i _Last, # jest operacją dwuargumentową _Binary_op,
	  Val jest wartością początkową

 * Wersja specjalizowana:
	template<class InputIterator, class Type>
	   Type accumulate(
		  InputIterator _First,
		  InputIterator _Last,
		  Type _Val
	   );

	- używa dodawania (operatora +) jako operacji dwuargumentowej
	- wynikiem działania jest suma wszystkich elementów

Uwagi:
- algorytm ma złożoność liniową O(n)
- parametr _Binary_op rerezentujący operator dwuargumentowy może być obiektem
  funkcyjnym i, jak pokazano w przykładzie 4a, korzystać z rachunku lambda

 */

#include <iostream>
#include <vector>
#include <string>
#include <numeric>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace std;
using namespace boost::lambda;

/*
 * Prosta klasa figury wykorzystywana w przykładzie 4.
 * Figura ma pole i atrybut określający widoczność na ekranie.
 */
class Shape {
private:
    float area;
    bool visible;
public:
    Shape() : area(0.0f), visible(true) {}
    Shape(float area, bool visible) : area(area), visible(visible) {}
    void setArea(float area) { this->area = area; }
    float getArea() const { return area; }
    void setVisible(bool visible) { this->visible = visible; }
    bool isVisible() { return visible; }
};

/*
 * Funkcja wykorzystywana w przykładzie 4.
 * Dodaje do zakumulowanej wartości pole figury, jeśli jest ona widoczna.
 */
float sumVisibleArea(float totalArea, Shape shape) {
    if(shape.isVisible())
        return totalArea + shape.getArea();
    return totalArea;
}

int main() {
	/*
	 * Przykłady użycia wersji specjalizowanej.
	 * Możemy ją wykorzystać dla każdej kolekcji elementów posiadających
	 * zdefiniowany operator dodawania.
	 *
	 * Przykład 1. Suma liczb.
	 * Najprostszy możliwy przykład ukazuje sumowanie liczb z wektora i dodanie
	 * do sumy wartości początkowej.
	 */
	vector<int> numbers = vector<int>(5, 10);
	int summed = accumulate(numbers.begin(), numbers.end(), 5);
	cout << summed << endl; // summed = ((((5+10)+10)+10)+10)+10 = 5+5*10 = 55

	/*
	 * Przykład 2. Konkatenacja stringów.
	 * Przykład wykorzystuje przeciążony operator + klasy string.
	 */
	vector<string> words;
    words.push_back("To ");
    words.push_back("jest ");
    words.push_back("jedno ");
    words.push_back("zdanie.");
    string concatenated = accumulate(words.begin(), words.end(), string(""));
    cout << concatenated << endl; // concatenated =
                                  // (((""+"To ")+"jest ")+"jedno ")+"zdanie." =
                                  // "To jest jedno zdanie."

	/*
	 * Przykłady użycia wersji ogólnej.
	 *
	 * Przykład 3. Mnożenie liczb.
	 * Mnożenie liczb z wektora i wartości początkowej.
	 * Możliwe jest wykorzystanie także innych obiektów funkcyjnych
	 * z stl function.
	 */
	vector<int> numbers2 = vector<int>(5, 10);
	int multiplied = accumulate(numbers2.begin(), numbers2.end(), 5, multiplies<int>());
	cout << multiplied << endl; // multiplied = ((((5*10)*10)*10)*10)*10 = 5*10^5 = 50000

	/*
	 * Przykład 4. Pola widocznych figur.
	 * Sumuje pola widocznych figur. Przykład pokazuje użycie bardziej
	 * skomplikowanej funkcji w roli operatora dwuargumentowego.
	 */
	vector<Shape> shapes;
	// drugi parametr konstruktora - czy figura jest widoczna
    Shape s1(12.0f, true);
    Shape s2(5.5f, false);
    Shape s3(10.5f, true);
    shapes.push_back(s1);
    shapes.push_back(s2);
    shapes.push_back(s3);
    float visibleArea = accumulate(shapes.begin(), shapes.end(), 0.0f, sumVisibleArea);
    cout << visibleArea << endl; // visibleArea = (0+12)+10.5 = 22.5

    /*
     * Przykład 4a. Pola figur.
     * Do przystosowywania funkcji (do postaci op. dwuargumentowego)
     * i konstruowania złożonych funkcji można się także posłużyć narzędziami
     * rachunku lambda, np. biblioteką boost::bind (więcej o bibliotece na
     * stronie http://www.boost.org/doc/libs/1_37_0/libs/bind/bind.html)
     */
    float totalArea = accumulate (shapes.begin(), shapes.end(), 0.0f,
    		bind(plus<float>(), _1, bind(&Shape::getArea, _2)));
		// totalArea = ((0+12)+5.5)+10.5 = 28
    cout << totalArea << endl;

}
