/// @author Stanisław Aleksander Janikowski (stasiek.janikowski@gmail.com)
/* 
 * Program stworzony w ramach pracy domowej przedmiotu ZPR w celu 
 * demonstracji i przyblizenia algorytmu transform dostepnego w bibliotece
 * standardowej (stl)
 */

// Szablon funkcji transform dostępny jest w pliku nagłówkowym:
#include<algorithm>

// Pliki nagłówkowe, wykorzystywane pomocniczo w przykładach:
#include<iostream>
#include<vector>
#include<map>
#include<complex>
#include<cmath>

using namespace std;

/*
 * Algorytm transform służy do dokonywania tego samego przekształcenia na 
 * wielu elementach zawartych w kontenerze, a także do wykonania operacji
 * dwuargumentowych na odpowiadających sobie elementach z dwóch kontenerów
 * Wynik przekształcenia może być zapisywany w miejscu, nadpisując dane w
 * jednym z kontenerów wejściowych, lub do innego kontenera.
 */

/*
 * Wersja z operatorem jednoargumentowym:
 *	template < class InputIterator, class OutputIterator, class UnaryOperator >
 *		OutputIterator transform ( InputIterator first1, InputIterator last1,
 *					   OutputIterator result, UnaryOperator op );
 */

// Przykład 1 (wystawianie ocen)
// funkcja pomocnicza:
float wystaw_ocene(int punkty){
	punkty-=51;
	if (punkty < 0) return 2.0;
	return 3.0 + 0.5 * (punkty/10);		
}

// Nic nie stoi na przeszkodzie, aby transformację robić w miejscu (wyniki nadpisują argumenty)
// Przykład 2 (przeliczanie jednostek)
// funkcja pomocnicza:
float Cels_to_Fahr(float temp){
	return 32.0 + 9.0/5.0 * temp;
}

/*
 * Wersja z operatorem dwuargumentowym:
 * 	template < class InputIterator1, class InputIterator2,
 * 		   class OutputIterator, class BinaryOperator >
 * 		OutputIterator transform ( InputIterator1 first1, InputIterator1 last1, 
 * 					   InputIterator2 first2, OutputIterator result, 
 * 					   BinaryOperator binary_op );
 */

// Przykład 3 (widmo zespolone)
// Widmo amplitudowe i fazowe zapisane w oddzielnych kontenerach integrowane do widma zespolonego
typedef complex<double> dcomplex;

dcomplex licz_widmo(double ampl, double faza){
	return dcomplex(ampl * cos(faza), ampl * sin(faza) );
}

/*
 * Transform ma duże możliwości, lecz jego stosowalność jest ograniczona wymaganiem standardów, aby
 * dostarczona funkcja była pozbawiona efektów ubocznych. Np. nie możemy użyć transform do edycji
 * mapy ponieważ niedozwolone jest użycie operatora przypisania, a zmiana wartości przez funkcję
 * naruszałaby standard. Podobnie nie możemy używać transform aby usuwać lub przenosić elementy.
 * Kolejnym ograniczeniem jest brak gwarancji kolejności wykonywania operacji na elementach, więc
 * użycie transform z użyciem funkcji zależnej od kolejności wołania jest błędem. W wymienionych
 * przypadkach należy stosować funkcję for_each lub stworzyć własną, nie obarczoną tymi warunkami
 * wersję algorytmu transform.
 *	
 * UWAGA: W wielu kompilatorach kod ignorujący wspomniane wyżej założenia zadziała, jednak nie ma
 * 	  żadnej gwarancji jego przenośności, również pomiędzy wersjami tego samego kompilatora.
 * 	  Wspomniane ograniczenia są spowodowane dążeniem do umożliwienia kompilatorom optymalizacji.
 * 	  W szczególności operacje na poszczególnych elementach zakresu funkcji transform mogą być
 * 	  wykonywane w osobnych wątkach.
 */


int main(){

	// Przykład 1 (wystawianie ocen):
	cout.setf(ios::fixed,ios::floatfield);
	cout.precision(1);
	vector<int> punkty;
	vector<float> oceny;
	vector<int>::iterator it_p;
	vector<float>::iterator it_o;
	
	// Ustawienie i wypisanie przykładowych punktów uzyskanych przez studentów
	for (int i=1; i<10; i++) punkty.push_back (i*51%81 + 20);
	cout << "Przyklad 1:\nZdobyte punkty:";
	for (it_p=punkty.begin(); it_p!=punkty.end(); ++it_p)
		cout << "\t" << *it_p;
	cout << endl;

	//Rozszerzenie bufora docelowego (oceny), aby zmiescił wartosci:
	oceny.resize(punkty.size());
	// Użycie transform do przeliczenia punktów na oceny:
	transform(punkty.begin(), punkty.end(), oceny.begin(), wystaw_ocene);

	//Wypisanie ocen:
	cout << "Uzyskane Oceny:";
	for (it_o=oceny.begin(); it_o!=oceny.end(); ++it_o)
		cout << "\t" << *it_o;
	cout << endl;

/*****************************************************************************************************/

	// Przykład 2 (przeliczanie jednostek):
	cout.precision(2);
	vector<float> temperatura;
	vector<float>::iterator it;
	
	// Ustawienie i wypisanie przykładowych wartosci temperatury w stopniach Celsiusza
	temperatura.push_back(36.6);
	for (int i=1; i<9; i++) temperatura.push_back (i*249%611 /15.0);
	cout << "Przyklad 2:\nTemperatury w st.C:";
	for (it=temperatura.begin(); it!=temperatura.end(); ++it)
		cout << "\t" << *it;
	cout << endl;
	
	// Użycie transform do przeliczenia temperatur
	transform(temperatura.begin(), temperatura.end(), temperatura.begin(), Cels_to_Fahr);
	
	// Wypisanie temperatur w stopniach Fahrenheita
	cout << "Temperatury w st.F:";
	for (it=temperatura.begin(); it!=temperatura.end(); ++it)
		cout << "\t" << *it;
	cout << endl;

/*****************************************************************************************************/

	// Przykład 3 (widmo zespolone):
	cout.precision(3);
	vector<double> widmo_ampl;
	vector<double> widmo_faz;
	vector<dcomplex> widmo;
	vector<double>::iterator it1;
	vector<dcomplex>::iterator it2;


	// Ustawienie i wypisanie przykładowych wartosci widma:
	for (int i=0; i<9; i++){
		widmo_ampl.push_back(i%5?5.0/(i*i):0);
		widmo_faz.push_back(M_PI * sin(2 * i%5));
	}
	cout << "Przyklad 3:\nWidmo amplitudowe:";
	for (it1=widmo_ampl.begin(); it1!=widmo_ampl.end(); ++it1)
		cout << "\t" << *it1;
	cout << endl << "Widmo fazowe:	";
	for (it1=widmo_faz.begin(); it1!=widmo_faz.end(); ++it1)
		cout << "\t" << *it1;

	//Rozszerzenie bufora docelowego (widmo), aby zmiescił wartosci:
	widmo.resize(widmo_ampl.size());
	// Użycie transform do obliczenia widma zespolonego
	transform(widmo_ampl.begin(), widmo_ampl.end(), widmo_faz.begin(), widmo.begin(), licz_widmo);
	
	// Wypisanie widma zespolonego
	cout << endl << "Widmo zespolone:";
	for (it2=widmo.begin(); it2!=widmo.end(); ++it2)
		cout << "\t" << *it2;
	cout << endl;

	return 0;
}
