
#include <vector>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <random>


using namespace std;

using Samples = vector<double>;


// /** brak transformacji, zwraca wejsciowa kolekcje */

// class TransformSamples {
// public:
// 	TransformSamples() { }

// 	virtual Samples transform(const Samples& in) {
// 		return Samples(in);
// 	}
// };

// /** transformacja każdego elementu w taki sposób, aby usuwać wartości ekstremalne, tzn. takie które są większe niż MAX_FACTOR * średnia,
// oraz które są mniejsze niż MIN_FACTOR * średnia.
// */

// class CutExtrema : public TransformSamples {
// public:
// 	CutExtrema(double min_factor, double max_factor) : MIN_FACTOR(min_factor), MAX_FACTOR(max_factor) {}
// 	virtual Samples transform(const Samples& in) {
// 		if(in.empty() ) return Samples();

// 		Samples out;
// 		out.reserve(in.size());

// 		double avg = accumulate(in.begin(), in.end(), 0.0) / static_cast<double>(in.size());
// 		for(double d : in ) {
// 			if(d > MAX_FACTOR * avg ) out.push_back(MAX_FACTOR * avg);
// 			else if (d < MIN_FACTOR * avg) out.push_back(MIN_FACTOR * avg);
// 			else out.push_back(d);
// 		}
// 		return out;
// 	}
// private:
// 	const double MIN_FACTOR;
// 	const double MAX_FACTOR;
// };


// class DelAvg : public TransformSamples {
// public:
// 	DelAvg() {}

// 	virtual Samples transform(const Samples& in) {
// 		if(in.empty() ) return Samples();

// 		Samples out;
// 		out.reserve(in.size());

// 		double avg = accumulate(in.begin(), in.end(), 0.0) / static_cast<double>(in.size());
// 		for(double d : in ) {
// 			out.push_back(d - avg);
// 		}
// 		return out;
// 	}
// };

/** rozwiązanie - wzorzec NVI */
class TransformSamples {
public:
	TransformSamples() { }

	Samples transform(const Samples& in) {
		if(in.empty() ) return Samples();

		Samples out;
		out.reserve(in.size());

		double avg = accumulate(in.begin(), in.end(), 0.0) / static_cast<double>(in.size());
		for(double d : in ) {
			out.push_back( doTransform(d, avg) );
		}
		return out;
	}
private:
	virtual double doTransform(double sample, double avg) { return sample; }
};

class CutExtrema : public TransformSamples {
public:
	CutExtrema(double min_factor, double max_factor) : MIN_FACTOR(min_factor), MAX_FACTOR(max_factor) {}
private:
	const double MIN_FACTOR;
	const double MAX_FACTOR;

	virtual double doTransform(double sample, double avg) {
		if(sample > MAX_FACTOR * avg ) return MAX_FACTOR * avg;
		else if (sample < MIN_FACTOR * avg) return MIN_FACTOR * avg;
		return sample;
	}
private:
	Samples samples;
};


class DelAvg : public TransformSamples {
public:
	DelAvg() {}
private:
	virtual double doTransform(double sample, double avg) {
		return sample - avg;
	}
};



void print(const Samples s) {
	copy( s.begin(), s.end(), ostream_iterator<double>(cout, " ") );
	cout << endl;
}

int main() {

	static const int N = 10;
	Samples s1(N);

	fill( s1.begin(), s1.end(), 1.0);
	print(s1);

	Samples s2(N);

	std::mt19937_64 gen; //Mersanne Twister random generator, default seed
	std::uniform_real_distribution<> distr(1.0, 9.0);
	generate( s2.begin(), s2.end(), [&](){ return distr(gen);});

	print(s2);

	TransformSamples t1;
	print ( t1.transform(s1) );
	print ( t1.transform(s2) );


	CutExtrema t2(-1, 1);
	print ( t2.transform(s1) );
	print ( t2.transform(s2) );


	DelAvg t3;
	print ( t3.transform(s1) );
	print ( t3.transform(s2) );

	return 0;
}
