package com.jivesoftware.os.miru.sea.anomaly.plugins;

import com.google.common.math.LongMath;
import com.jivesoftware.os.miru.plugin.bitmap.MiruBitmaps;
import com.jivesoftware.os.miru.sea.anomaly.plugins.SeaAnomalyAnswer.Waveform;
import com.jivesoftware.os.mlogger.core.MetricLogger;
import com.jivesoftware.os.mlogger.core.MetricLoggerFactory;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;

/**
 *
 */
public class SeaAnomaly {

    private static final MetricLogger LOG = MetricLoggerFactory.getLogger();

    private final SplineInterpolator interpolator = new SplineInterpolator();

    public <BM extends IBM, IBM> Waveform metricingSum(MiruBitmaps<BM, IBM> bitmaps,
        BM rawAnswer,
        List<BM> answers,
        int[] indexes,
        int numBits)
        throws Exception {

        LOG.debug("Get metricing for answers={}", answers);

        long[] waveform = sum(indexes, numBits, answers, bitmaps);

        return new Waveform(waveform);
    }

    public <BM extends IBM, IBM> Waveform metricingAvg(MiruBitmaps<BM, IBM> bitmaps,
        BM rawAnswer,
        List<BM> answers,
        int[] indexes,
        int numBits)
        throws Exception {

        LOG.debug("Get metricing for answers={}", answers);

        long[] rawCardinalities = new long[indexes.length - 1];
        bitmaps.boundedCardinalities(rawAnswer, new int[][] { indexes }, new long[][] { rawCardinalities });

        long[] waveform = sum(indexes, numBits, answers, bitmaps);

        double[] x = new double[waveform.length];
        double[] y = new double[waveform.length];
        int count = 0;
        for (int i = 0; i < waveform.length; i++) {
            if (rawCardinalities[i] > 0) {
                x[count] = i;
                y[count] /= rawCardinalities[i];
                count++;
            }
        }

        if (count == waveform.length) {
            for (int i = 0; i < waveform.length; i++) {
                if (rawCardinalities[i] > 0) {
                    waveform[i] /= rawCardinalities[i];
                }
            }
        } else {
            double[] ix = new double[count];
            double[] iy = new double[count];
            System.arraycopy(x, 0, ix, 0, count);
            System.arraycopy(y, 0, iy, 0, count);

            PolynomialSplineFunction splineFunction = interpolator.interpolate(ix, iy);

            long[] interpolated = new long[waveform.length];
            for (int i = 0; i < waveform.length; i++) {
                interpolated[i] = (long) splineFunction.value((double) i);
            }

            waveform = interpolated;
        }
        return new Waveform(waveform);
    }


    static <BM extends IBM, IBM> long[] sum(int[] indexes, int numBits, List<BM> answers, MiruBitmaps<BM, IBM> bitmaps) {
        long[] waveform = null;
        long[] rawCardinalities = new long[indexes.length - 1];

        for (int i = 0; i < numBits; i++) {
            BM answer = answers.get(i);
            if (answer != null) {
                Arrays.fill(rawCardinalities, 0);
                bitmaps.boundedCardinalities(answer, new int[][] { indexes }, new long[][] { rawCardinalities });
                if (waveform == null) {
                    waveform = new long[rawCardinalities.length];
                }
                long multiplier = (1L << i);
                for (int j = 0; j < waveform.length; j++) {
                    if (rawCardinalities[j] > 0) {
                        long add = rawCardinalities[j] * multiplier;
                        try {
                            waveform[j] = LongMath.checkedAdd(waveform[j], add);
                        } catch (Exception x) {
                            waveform[j] = Long.MAX_VALUE;
                            LOG.inc("overflows");
                        }
                    }
                }
            }
        }
        return waveform;

    }
}

@@ -1748,16 +1748,73 @@
 nt = 0;%0A
+        int firstIndex = -1;%0A        int lastIndex = -1;%0A
         
@@ -1892,32 +1892,158 @@
 ities%5Bi%5D %3E 0) %7B%0A
+                if (firstIndex == -1) %7B%0A                    firstIndex = i;%0A                %7D%0A                lastIndex = i;%0A%0A
                 
@@ -2400,16 +2400,31 @@
   %7D else
+ if (count %3E 0)
  %7B%0A     
@@ -2452,32 +2452,36 @@
 new double%5Bcount
+ + 2
 %5D;%0A            d
@@ -2509,16 +2509,20 @@
 le%5Bcount
+ + 2
 %5D;%0A     
@@ -2555,17 +2555,17 @@
  0, ix, 
-0
+1
 , count)
@@ -2605,17 +2605,17 @@
  0, iy, 
-0
+1
 , count)
@@ -2613,24 +2613,172 @@
 1, count);%0A%0A
+            ix%5B0%5D = -1;%0A            iy%5B0%5D = y%5BfirstIndex%5D;%0A            ix%5Bix.length - 1%5D = x.length;%0A            iy%5Biy.length - 1%5D = y%5BlastIndex%5D;%0A%0A
             
@@ -2963,32 +2963,168 @@
 .length; i++) %7B%0A
+                if (rawCardinalities%5Bi%5D %3E 0) %7B%0A                    interpolated%5Bi%5D /= rawCardinalities%5Bi%5D;%0A                %7D else %7B%0A    
                 
@@ -3178,24 +3178,42 @@
 double) i);%0A
+                %7D%0A
             

fix interpolation for literal edge case hee hee