#pragma once
#ifndef TRANSFORMERS_H
#define TRANSFORMERS_H


#include <vector>

#include "abc.h"
#include "common.h"


#define SCALER_EPSILON 1E-12

/*
Min-Max normalization
*/
class MinMaxScaler : public TransformerABC {
private:
	//features stats
	vector<data_t> _min;
	vector<data_t> _max;
	//new scale
	data_t _new_min;
	data_t _new_max;

protected:

	void _fit(const Matrix<data_t>& data, const vector<label_t>& targets);
	void _fit(const Matrix<data_t>& data);
	Matrix<data_t> _transform(const Matrix<data_t>& data) const;
	//one sample
	vector<data_t> _transform(const vector<data_t>& sample) const;

public:

	const vector<data_t>& min() const { return _min; }
	const vector<data_t>& max() const { return _max; }

	//c'tor
	MinMaxScaler(data_t new_min = 0.0, data_t new_max = 1.0);

};

/*
Feature scaling
*/
class Weight : public TransformerABC {
    //feature weights
    vector<data_t> _weights;

protected:

    void _fit(const Matrix<data_t>& data, const vector<label_t>& targets);
    void _fit(const Matrix<data_t>& data);
    Matrix<data_t> _transform(const Matrix<data_t>& data) const;
    //one sample
    vector<data_t> _transform(const vector<data_t>& sample) const;

public:
    const vector<data_t>& weights() const { return _weights; }
    Weight& set_weights(const vector<double>& weights) { _weights = weights; return *this; }
    //c'tor
	Weight() {}
    Weight(const vector<data_t>& weights) : _weights(weights) {};

};

//class FeatureSelector : public TransformerABC {
//	//features to select
//	vector<bool> _features_mask;
//
//protected:
//	void _fit(const Matrix<data_t>& data, const vector<label_t>& targets);
//	void _fit(const Matrix<data_t>& data);
//	Matrix<data_t> _transform(const Matrix<data_t>& data) const;
//	//one sample
//	vector<data_t> _transform(const vector<data_t>& sample) const;
//
//public:
//	const vector<bool>& features_mask() const { return _features_mask; }
//	Weight& set_mask(const vector<double>& weights) { _weights = weights; return *this; }
//	//c'tor
//	FeatureSelector() {}
//	FeatureSelector() (const vector<data_t>& weights) : _weights(weights) {};
//};


/*
analog sklearn class with the same name http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html
"Generate a new feature matrix consisting of all polynomial combinations of 
the features with degree less than or equal to the specified degree. 
For example, if an input sample is two dimensional and of the form [a, b], 
the degree-2 polynomial features are [1, a, b, a^2, ab, b^2]."
*/
class PolynomialFeatures : public TransformerABC {
    unsigned _degree;
    bool _include_bias;
    size_t _n_out_features;
    vector<vector<size_t>> _features_comb; 
    
protected:

	void _fit(const Matrix<data_t>& data, const vector<label_t>&);
	void _fit(const Matrix<data_t>& data);
	Matrix<data_t> _transform(const Matrix<data_t>& data) const;
	vector<data_t> _transform(const vector<data_t>& sample) const;

public:
    PolynomialFeatures(unsigned short degree, bool include_bias=true) {
        _degree = degree;
        _include_bias = include_bias;
    }    
};

#endif
