//#include <iostream>
//#include "matrix.h"
//#include "neighbors.h"
//#include "transformers.h"
//#include "cross_validation.h"
//#include "optimization.h"
//#include "pipeline.h"
//#include "loop_classifier.h"
//
//
//using namespace optimization;
//using namespace common;
//using namespace neighbors;
//
//namespace experimental {
//
//
//    class LTransformer : public TransformerABC {
//        Matrix<data_t> _L;
//
//    protected:
//        void _fit(const Matrix<data_t>& data) {
//            if(data.n_cols() != _L.n_cols()) {
//                throw std::invalid_argument("Wrong number of features");;
//            }
//        }
//        void _fit(const Matrix<data_t>& data, const vector<label_t>& /*targets */ ) {
//            _fit(data);
//        }
//
//        Matrix<data_t> _transform(const Matrix<data_t>& data) const {
//            Matrix<data_t> result(data.n_rows(), data.n_cols());
//            for(size_t i = 0; i < data.n_rows(); i++) {
//                result.set_row(_transform(data(i)), i);
//            }
//            return result;
//        }
//
//        vector<data_t> _transform(const vector<data_t>& sample) const {
//            vector<data_t> result(sample.size(), 0.0);
//            for(size_t i = 0; i < _L.n_rows(); i++) {
//                for(size_t j = 0; i < _L.n_cols(); j++){
//                    result[i] += _L(i, j) * sample[j];
//                }
//            }
//            return result;
//        }
//
//    public:
//        const Matrix<data_t>& L() const { return _L; }
//        LTransformer& set_L(const Matrix<data_t>& L) { _L = L; return *this; }
//        //c'tor
//        LTransformer(const Matrix<data_t>& L) : _L(L) {};
//        LTransformer(size_t n_features) : _L(n_features, n_features) {
//            for(size_t i = 0; i < n_features; i++)
//                _L(i, i) = 1.0;
//        }
//    };
//
//
//    class PseudoMetricKNN : public ClassifierABC {
//        MinMaxScaler _minmax;
//        LTransformer _transformer;
//        KNeighborsClassifier _knn;
//    protected:
//        //classifierabc
//        void _fit(const Matrix<data_t>& data, const vector<label_t>& targets) {
//            Matrix<data_t> tdata = data;
//            _minmax.fit(tdata, targets);
//            tdata = _minmax.transform(tdata);
//            _transformer.fit(tdata);
//            tdata = _transformer.transform(tdata);
//            _knn.fit(tdata, targets);
//        }
//        vector<label_t> _predict(const Matrix<data_t>& data) const {
//            vector<label_t> targets_pred(data.n_rows());
//#ifdef PPL_USE_PARALLEL_FOR
//            concurrency::parallel_for(std::size_t(0), targets_pred.size(), [&](size_t  idx) {
//                targets_pred[idx] = _predict(data(idx));
//            });
//
//#else
//            for(size_t idx = 0; idx < targets_pred.size(); idx++) {
//                targets_pred[idx] = _predict(data(idx));
//            }
//#endif
//            return targets_pred;
//        }
//        label_t _predict(const vector<data_t>& sample) const {
//            vector<data_t> tsample = sample;
//            tsample = _minmax.transform(tsample);
//            tsample = _transformer.transform(tsample);
//            return _knn.predict(tsample);
//        }
//    public:
//        PseudoMetricKNN(size_t n_features) : _knn(1), _transformer(n_features) {}
//        PseudoMetricKNN& set_L(const vector<data_t>& vec, size_t n_features) {
//            _transformer.set_L(Matrix<data_t>(vec, n_features));
//            return *this;
//        }
//    };
//
//    struct LFitness : public FitnessABC {
//        CrossValidationABC* skf = nullptr;
//        PseudoMetricKNN* clf = nullptr;
//        metrics::classification::Score score_type;
//        score_t func(const vector<data_t>& x) {
//            return -cross_val_score(&(clf->set_L(x, 27)), skf, metrics::classification::get_score_func(score_type));
//        }
//    };
//
//    score_t run_l_fitness() {
//        LFitness* fitness = nullptr;
//        try {
//            LooperClassifier loop_clf("..\\.\\XMLData\\looper_clf.xml");
//            auto Xy = loop_clf.loop_clf().dataset().samples_to_Xy(loop_clf.loop_clf().dataset().get_enabled_samples());
//            size_t n = loop_clf.n_features();
//            fitness = new LFitness();
//            fitness->clf = new PseudoMetricKNN(n);
//            StratifiedKFold* skf = new StratifiedKFold(StratifiedKFoldParameters());
//            InteropNegotiator::message = [](void*, const char* msg) { std::cout << msg << std::endl; };
//            skf->cmp_folds(Xy.first, Xy.second);
//            fitness->skf = skf;
//            fitness->score_type = metrics::classification::F1_MACRO;
//            CoordinateDescentSettings cdSettings(n * n);
//            for(size_t i = 0; i < n * n; i++)
//                cdSettings.x_init[i] = 0.0;
//            for(size_t i = 0; i < n; i++)
//                cdSettings.x_init[i * n + i] = 1.0;
//            optimization::CoordinateDescent optimizer(cdSettings);
//			InteropNegotiator::report_progress = [](void*, float progress) {std::cout << progress << std::endl; };
//            optimizer.run(fitness);
//
//            delete fitness->clf;
//            delete fitness->skf;
//            delete fitness;
//
//            return optimizer.fopt();
//        }
//        catch(...) {
//            if(fitness != nullptr) {
//                if(fitness->skf != nullptr) delete fitness->skf;
//                if(fitness->clf != nullptr) delete fitness->clf;
//            }
//            delete fitness;
//            return -1.0;
//        }
//    }
//
//};
