package biz.k11i.xgboost.util;

import java.io.Serializable;
import java.util.Map;

/**
 * Interface of feature vector.
 */
public interface FVec extends Serializable {
    /**
     * Gets index-th value.
     *
     * @param index index
     * @return value
     */
    double fvalue(int index);

    class Transformer {
        private Transformer() {
            // do nothing
        }

        /**
         * Builds FVec from dense vector.
         *
         * @param values          float values
         * @return FVec
         */
        public static FVec fromArray(float[] values) {
            return new FVecArrayImpl.FVecFloatArrayImpl(values, true);  /* treat zero as N/A */
        }

        /**
         * Builds FVec from dense vector.
         *
         * @param values          float values
         * @param treatsValueAsNA treat specify value as N/A
         * @return FVec
         */
        public static FVec fromArray(float[] values, float treatsValueAsNA) {
            return new FVecArrayImpl.FVecFloatArrayImplement(values, treatsValueAsNA);
        }

        /**
         * Builds FVec from dense vector.
         *
         * @param values         double values
         * @return FVec
         */
        public static FVec fromArray(double[] values) {
            return new FVecArrayImpl.FVecDoubleArrayImpl(values, true);  /* treat zero as N/A */
        }

        /**
         * Builds FVec from dense vector.
         *
         * @param values          double values
         * @param treatsValueAsNA treat specify value as N/A
         * @return FVec
         */
        public static FVec fromArray(double[] values, double treatsValueAsNA) {
            return new FVecArrayImpl.FVecDoubleArrayImplement(values, treatsValueAsNA);
        }

        /**
         * Builds FVec from map.
         *
         * @param map map containing non-zero values
         * @return FVec
         */
        public static FVec fromMap(Map<Integer, ? extends Number> map) {
            return new FVecMapImpl(map);
        }
    }
}

class FVecMapImpl implements FVec {
    private final Map<Integer, ? extends Number> values;

    FVecMapImpl(Map<Integer, ? extends Number> values) {
        this.values = values;
    }

    @Override
    public double fvalue(int index) {
        Number number = values.get(index);
        if (number == null) {
            return Double.NaN;
        }

        return number.doubleValue();
    }
}

class FVecArrayImpl {
    static class FVecFloatArrayImpl implements FVec {
        private final float[] values;
        private final boolean treatsZeroAsNA;

        FVecFloatArrayImpl(float[] values, boolean treatsZeroAsNA) {
            this.values = values;
            this.treatsZeroAsNA = treatsZeroAsNA;
        }

        @Override
        public double fvalue(int index) {
            if (values.length <= index) {
                return Double.NaN;
            }

            double result = values[index];
            if (treatsZeroAsNA && result == 0) {
                return Double.NaN;
            }

            return result;
        }
    }

    static class FVecFloatArrayImplement implements FVec {
        private final float[] values;
        private final float treatsValueAsNA;

        FVecFloatArrayImplement(float[] values, float treatsValueAsNA) {
            this.values = values;
            this.treatsValueAsNA = treatsValueAsNA;
        }

        @Override
        public double fvalue(int index) {
            if (values.length <= index) {
                return Double.NaN;
            }

            double result = values[index];
            if (treatsValueAsNA == result) {
                return Double.NaN;
            }

            return result;
        }
    }

    static class FVecDoubleArrayImpl implements FVec {
        private final double[] values;
        private final boolean treatsZeroAsNA;

        FVecDoubleArrayImpl(double[] values, boolean treatsZeroAsNA) {
            this.values = values;
            this.treatsZeroAsNA = treatsZeroAsNA;
        }

        @Override
        public double fvalue(int index) {
            if (values.length <= index) {
                return Double.NaN;
            }

            double result = values[index];
            if (treatsZeroAsNA && result == 0) {
                return Double.NaN;
            }

            return values[index];
        }
    }

    static class FVecDoubleArrayImplement implements FVec {
        private final double[] values;
        private final double treatsValueAsNA;

        FVecDoubleArrayImplement(double[] values, double treatsValueAsNA) {
            this.values = values;
            this.treatsValueAsNA = treatsValueAsNA;
        }

        @Override
        public double fvalue(int index) {
            if (values.length <= index) {
                return Double.NaN;
            }

            double result = values[index];
            if (treatsValueAsNA == result) {
                return Double.NaN;
            }

            return values[index];
        }
    }
}

@@ -474,33 +474,32 @@
  values         
- 
 float values%0A   
@@ -487,32 +487,91 @@
    float values%0A
+         * @param treatsZeroAsNA treat zero as N/A if true%0A
          * @retu
@@ -633,32 +633,56 @@
 y(float%5B%5D values
+, boolean treatsZeroAsNA
 ) %7B%0A            
@@ -736,40 +736,25 @@
 ues,
- true);  /*
  treat
- zero as N/A */
+sZeroAsNA);
 %0A   
@@ -851,38 +851,38 @@
  values         
- float
+double
  values%0A        
@@ -889,37 +889,36 @@
  * @param treats
-Value
+Zero
 AsNA treat speci
@@ -912,36 +912,35 @@
 A treat 
-specify value as N/A
+zero as N/A if true
 %0A       
@@ -997,37 +997,38 @@
  FVec fromArray(
-float
+double
 %5B%5D values, float
@@ -1022,33 +1022,34 @@
 values, 
-float
+boolean
  treats
-Value
+Zero
 AsNA) %7B%0A
@@ -1081,37 +1081,38 @@
 ecArrayImpl.FVec
-Float
+Double
 ArrayImplement(v
@@ -1096,37 +1096,32 @@
 cDoubleArrayImpl
-ement
 (values, treatsV
@@ -1111,37 +1111,36 @@
 l(values, treats
-Value
+Zero
 AsNA);%0A        %7D
@@ -1235,37 +1235,98 @@
 ues         
-double values
+ float values%0A         * @param treatsValueAsNA treat specify value as N/A
 %0A         * 
@@ -1379,38 +1379,37 @@
  FVec fromArray(
-double
+float
 %5B%5D values) %7B%0A   
@@ -1397,24 +1397,47 @@
 oat%5B%5D values
+, float treatsValueAsNA
 ) %7B%0A        
@@ -1461,38 +1461,37 @@
 ecArrayImpl.FVec
-Double
+Float
 ArrayImpl(values
@@ -1475,32 +1475,37 @@
 ecFloatArrayImpl
+ement
 (values, true); 
@@ -1503,37 +1503,23 @@
 , tr
-ue);  /* treat zero as N/A */
+eatsValueAsNA);
 %0A   

Fix method signatures