package process;

import javafx.util.Pair;
import jeigen.DenseMatrix;

import java.util.ArrayList;
import java.util.Objects;

public class DecisionStump { // == stumpRule

    public static double positiveTotalWeights = 0.5;

    // Values that will be used to find the best DecisionStump
    private int featureIndex;
    private double error;
    private double threshold;
    private double margin;
    private boolean toggle; // = polarity {-1; 1}


    private double rPos;
    private double rNeg;
    private double lPos;
    private double lNeg;

    private ArrayList<Pair<Integer, Boolean>> features;
    private DenseMatrix weights;


    // Initialisation
    public DecisionStump(ArrayList<Pair<Integer, Boolean>> features, DenseMatrix w, int featureIndex) {
        this.featureIndex = featureIndex;
        this.error = 2;
        this.threshold = this.features.get(0).getKey() - 1; // FIXME with organized features
        this.margin = -1; // Like that in the paper implem...
        this.toggle = false;

        this.rPos = positiveTotalWeights;
        this.rNeg = 1 - positiveTotalWeights;

        this.lPos = 0;
        this.lNeg = 0;

        // Should be arranged in ascending order
        this.features = (ArrayList<Pair<Integer, Boolean>>) features.clone();
        this.weights =  w;

    }

    public void compute() {

        int num_features = this.features.size() - 1;
        int iter = -1;


        double tmp_threshold = this.threshold;
        double tmp_margin = this.margin;
        double small_error = 0;
        boolean tmp_togle = false;

        while (true) {
            double errorPlus = lPos + rNeg;
            double errorMin = rPos + lNeg;

            if (errorPlus < errorMin) {
                small_error = errorPlus;
                tmp_togle = true;
            } else {
                small_error = errorMin;
                tmp_togle = false;
            }

            if (small_error < this.error || small_error == this.error && tmp_margin > this.margin) {
                this.error = small_error;
                this.threshold = tmp_threshold;
                this.margin = tmp_margin;
                this.toggle = tmp_togle;
            }

            iter++;

            // Pas sur si c'est toutes les features ou pas... mais ça doit etre bon...
            if (iter == num_features)
                break;

            while (true) {

                if (!this.features.get(iter).getValue()) {
                    this.lNeg += this.weights.get(0, iter);
                    this.rNeg -= this.weights.get(0, iter);
                } else {
                    this.lPos += this.weights.get(0, iter);
                    this.rPos -= this.weights.get(0, iter);
                }

                if (iter == num_features || Objects.equals(this.features.get(iter).getKey(), this.features.get(iter + 1).getKey()))
                    break;

                iter++;
            }

            if (iter < num_features - 1) {
                tmp_threshold = (this.features.get(iter).getKey() + this.features.get(iter + 1).getKey()) / 2;
                tmp_margin = this.features.get(iter + 1).getKey() - this.features.get(iter).getKey();
            } else {
                tmp_threshold = this.features.get(iter).getKey() + 1;
                tmp_margin = 0;
            }
        }
    }

    /**
     * <Integer i, Boolean b> indicates whether feature i is a face (b=true) or not (b=false)
     */
    public static DecisionStump bestStump(ArrayList<Pair<Integer, Boolean>> candidatesFeatures, DenseMatrix w) {
        // STATE: OK & CHECKED 16/26/08

        // FIXME
        DecisionStump best = new DecisionStump(candidatesFeatures, w, 0);
        best.compute();

        // FIXME
//        DecisionStump decisionStump = new DecisionStump(candidatesFeatures, w, i);
//        decisionStump.compute();
//
//        if (decisionStump.error < best.error || decisionStump.error == best.error && decisionStump.margin > best.margin) {
//            best = decisionStump;
//        }

        if (best.error >= 0.5)
            System.err.println("Failed best stump, error : " + best.error + " >= 0.5 ! (not good but we still continue)");

        return best;
    }

    public double getMargin() {
        return margin;
    }

    public double getThreshold() {
        return threshold;
    }

    public boolean getToggle() {
        return toggle;
    }

    public double getError() {
        return error;
    }

    public int getFeatureIndex() {
        return featureIndex;
    }
}


@@ -446,17 +446,16 @@
 -1; 1%7D%0A%0A
-%0A
     priv
@@ -1337,16 +1337,34 @@
 compute(
+DenseMatrix labels
 ) %7B%0A%0A   
@@ -2414,24 +2414,135 @@
 e (true) %7B%0A%0A
+                // int exampleIndex = getExampleIndex();%0A                // int label = labels%5BexampleIndex%5D;%0A%0A
             
@@ -3765,24 +3765,121 @@
 IXME%0A       
+ // Recuperer les features tri%C3%A9es%0A        // Pour les mettre dans une arrayList de int%0A        //
  DecisionStu
@@ -3906,34 +3906,31 @@
 onStump(
-candidatesFeatures
+features.get(0)
 , w, 0);
@@ -3933,24 +3933,27 @@
  0);%0A       
+ //
  best.comput
@@ -3954,16 +3954,22 @@
 compute(
+labels
 );%0A%0A    
@@ -3979,20 +3979,85 @@
  // 
-FIXME%0A//    
+for (int i = 0/* maybe opti with 1... */; i %3C featureCount; i++) %7B%0A        //
     
@@ -4104,34 +4104,31 @@
 onStump(
-candidatesFeatures
+features.get(i)
 , w, i);
@@ -4128,22 +4128,26 @@
  w, i);%0A
-//
     
+    //
     deci
@@ -4168,23 +4168,28 @@
 ute(
-);%0A//%0A//
+labels);%0A%0A 
        
+//
  if 
@@ -4300,22 +4300,26 @@
 rgin) %7B%0A
-//
+  
     
+  //
         
@@ -4340,25 +4340,42 @@
 nStump;%0A
+        
 //    
+%7D%0A
    
+     //
  %7D%0A%0A    
@@ -4373,24 +4373,27 @@
 / %7D%0A%0A       
+ //
  if (best.er
@@ -4407,24 +4407,27 @@
 0.5)%0A       
+ //
      System.
@@ -4530,32 +4530,35 @@
 nue)%22);%0A%0A       
+ //
  return best;%0A  
@@ -4555,16 +4555,37 @@
 n best;%0A
+        return null;%0A
     %7D%0A%0A 

fix labels in decision stump