// Tomasz Dudziak, F1-ISI

//boost::optional - Biblioteka oferująca adapter rozszerzający dowolny typ o wartość niezdefiniowaną/niezainicjowaną.
//Słowo optional oznacza, że dany obiekt może zupełnie opcjonalnie przyjmować jakąkolwiek wartość, gdyż może jej równie dobrze nie mieć.
//Oczywiście możliwe jest sprawdzenie, czy dany obiekt jest zainicjowany, czy nie oraz uczynienie go w pewnym momencie niezainicjowanym. 
//Dzięki przeciążeniu operatorów obsługa obiektów optional jest zbliżona do obsługi zmiennych.
#include<boost\optional.hpp>
#include<iostream>
#include<math.h>

using namespace std;
using namespace boost;

//Klasa Mathematician pełni rolę klasy obiektu "czarnej skrzynki". Nie interesują nas szczegóły działania jej metod, wiemy natomiast,
//że możemy otrzymać albo sensowny wynik, albo nieokreśloną wartość (gdy obliczenie wyniku jest niemożliwe). W tym wypadku interesuje
//nas obliczenie pierwiastka kwadratowego zadanej liczby. Jak wiadomo, dla liczb nieujemnych istnieje poprawny wynik, a dla liczb ujemnych nie.
class Mathematician
{
public:
	//Metoda countSquareRoot oblicza pierwiastek dla nieujemnego argumentu. Dla argumentu ujemnego wynik pozostaje niezdefiniowany.
	void countSquareRoot(double argument)
	{
		opt_result_=optional<double>(); //unieważnienie wyniku na początku (przypisanie wartości niezdefiniowanej)
		//Funkcja make_optional pozwala na przekształcenie danej liczby w obiekt optional, przy spełnieniu określonego warunku.
		//Tutaj obliczenie pierwiastka liczby, a następnie przetłumaczenie go na obiekt optional jest uzależnione od tego, czy
		//liczba jest nieujemna.
		opt_result_=make_optional<double>((argument>=0),sqrt(argument));			
	}
	
	//Pobranie wyniku.
	optional<double> getResult()
	{
		return opt_result_;
	}

private:
	optional<double> opt_result_; //Składowa, w której przechowywany jest wynik (określona liczba, lub niezdefiniowany).
};


int main()
{
	char ch='t'; //litera podawana przez użytkownika umożliwiająca wyjście z pętli programu lub pozostanie w niej
	double liczba; //liczba podawana przez użytkownika
	optional<double> opt_wynik; //obiekt przechowujący wynik typu optional
	Mathematician mathematician; //obiekt wykonujący dla nas obliczenia
	
	while(ch!='n')
	{
		//Pobranie od użytkownika dowolnej liczby
		cout<<"Podaj jakas liczbe dodatnia lub ujemna: ";
		cin>>liczba;
		cout<<endl;
		fflush(stdin);

		//Dokonanie obliczeń przez obiekt matematyka
		mathematician.countSquareRoot(liczba);
		opt_wynik=mathematician.getResult(); //Pobranie wyniku od matematyka

		if(!opt_wynik) //Jeśli wynik jest niezdefiniowany, informacja o tym dla użytkownika
			cout<<"Nie istnieje pierwiastek dla tej liczby."<<endl;
		else //jeśli wynik ma określoną wartość - podanie wyniku (wartość można otrzymać przy użyciu operatora *)
			cout<<"Obliczono pierwiastek dla podanej liczby: "<<*opt_wynik<<endl;

		//Dla n wyjście z pętli, dla pozostałych liter kolejna iteracja
		cout<<endl<<"Aby powtorzyc dla nowej liczby, podaj inna litere niz 'n': ";
		cin.clear();
		cin>>ch;
		cout<<endl;
		fflush(stdin);
	}
	
	return 0;
}