// Path: src/main/java/ro/hasna/ts/math/stat/BothWaySummaryStatistics.java
// public class BothWaySummaryStatistics implements StatisticalSummary, Serializable, Cloneable {
//     private static final long serialVersionUID = -8419769227216721345L;
//     private long n;
//     private double sum;
//     private double sumSquares;
//     private Max max;
//     private Min min;
//     private boolean removeMade;
// 
//     public BothWaySummaryStatistics() {
//         n = 0;
//         sum = 0;
//         sumSquares = 0;
//         max = new Max();
//         min = new Min();
//         removeMade = false;
//     }
// 
//     public void addValue(double d) {
//         sum += d;
//         sumSquares += d * d;
//         n++;
//         if (!removeMade) {
//             max.increment(d);
//             min.increment(d);
//         }
//     }
// 
//     public void removeValue(double d) {
//         if (n == 0) {
//             throw new InsufficientDataException();
//         }
// 
//         sum -= d;
//         sumSquares -= d * d;
//         n--;
//         removeMade = true;
//         max.clear();
//         min.clear();
//     }
// 
//     @Override
//     public double getMean() {
//         if (n == 0) return 0;
//         return sum / n;
//     }
// 
//     @Override
//     public double getVariance() {
//         if (n == 0) return Double.NaN;
//         if (n == 1) return 0;
//         double mean = getMean();
//         return sumSquares / n - mean * mean;
//     }
// 
//     @Override
//     public double getStandardDeviation() {
//         if (n == 0) return Double.NaN;
//         if (n == 1) return 0;
//         return FastMath.sqrt(getVariance());
//     }
// 
//     /**
//      * @return max value or Double.NaN if removeValue was called
//      */
//     @Override
//     public double getMax() {
//         return max.getResult();
//     }
// 
//     /**
//      * @return min value or Double.NaN if removeValue was called
//      */
//     @Override
//     public double getMin() {
//         return min.getResult();
//     }
// 
//     @Override
//     public long getN() {
//         return n;
//     }
// 
//     @Override
//     public double getSum() {
//         return sum;
//     }
// 
//     @Override
//     public BothWaySummaryStatistics clone() {
//         BothWaySummaryStatistics copy = new BothWaySummaryStatistics();
//         copy.n = n;
//         copy.sum = sum;
//         copy.sumSquares = sumSquares;
//         return copy;
//     }
// }
// 
// Path: src/main/java/ro/hasna/ts/math/type/MatrixProfile.java
// @Data
// public class MatrixProfile implements Cloneable {
//     private final double[] profile;
//     private final int[] indexProfile;
// 
//     public MatrixProfile(double[] profile, int[] indexProfile) {
//         if (profile.length != indexProfile.length) {
//             throw new DimensionMismatchException(profile.length, indexProfile.length);
//         }
// 
//         this.profile = profile;
//         this.indexProfile = indexProfile;
//     }
// 
//     public MatrixProfile(int size) {
//         profile = new double[size];
//         indexProfile = new int[size];
//         for (int j = 0; j < size; j++) {
//             profile[j] = Double.POSITIVE_INFINITY;
//             indexProfile[j] = -1;
//         }
//     }
// 
//     @Override
//     public MatrixProfile clone() {
//         int size = profile.length;
//         MatrixProfile copy = new MatrixProfile(size);
//         for (int i = 0; i < size; i++) {
//             copy.profile[i] = profile[i];
//             copy.indexProfile[i] = indexProfile[i];
//         }
//         return copy;
//     }
// }


import ro.hasna.ts.math.stat.BothWaySummaryStatistics;
import ro.hasna.ts.math.type.MatrixProfile;
import java.util.function.Predicate;
/*
 * Copyright 2015 Octavian Hasna
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ro.hasna.ts.math.representation.mp;




/**
 * Implements the STOMP algorithm to compute the self join matrix profile.
 * <p>
 * Reference:
 * Zhu Y., Zimmerman Z., Senobari N. S., Yeh C. C. M., Funning G., Mueen A., Keogh E. (2016)
 * <i>Exploiting a Novel Algorithm and GPUs to Break the One Hundred Million Barrier for Time Series Motifs and Joins</i>
 * </p>
 *
 * @since 0.17
 */
public class StompTransformer extends SelfJoinAbstractMatrixProfileTransformer {
    private static final long serialVersionUID = 2910211807207716085L;

    public StompTransformer(int window) {
        super(window);
    }

    public StompTransformer(int window, double exclusionZonePercentage, boolean useNormalization) {
        super(window, exclusionZonePercentage, useNormalization);
    }

    @Override
    protected MatrixProfile computeNormalizedMatrixProfile(double[] input, int skip, Predicate<MatrixProfile> callback) {
        int n = input.length - window + 1;
        MatrixProfile mp = new MatrixProfile(n);
        double[] distanceProfile = new double[n];

        double[] productSums = new double[n];
        BothWaySummaryStatistics first = new BothWaySummaryStatistics();
        BothWaySummaryStatistics second = new BothWaySummaryStatistics();
        for (int i = 0; i < window; i++) {
            first.addValue(input[i]);
            second.addValue(input[i + skip]);
        }

        computeFirstNormalizedDistanceProfile(input, first, input, second, skip, n, productSums, distanceProfile);
        updateMatrixProfileFromDistanceProfile(distanceProfile, 0, skip, n, mp, callback);
        for (int i = 1; i < n - skip; i++) {
            computeNextNormalizedDistanceProfile(input, skip, n, distanceProfile, productSums, first, second, i);
            updateMatrixProfileFromDistanceProfile(distanceProfile, i, skip, n, mp, callback);
        }
// Path: src/main/java/ro/hasna/ts/math/stat/BothWaySummaryStatistics.java
// public class BothWaySummaryStatistics implements StatisticalSummary, Serializable, Cloneable {
//     private static final long serialVersionUID = -8419769227216721345L;
//     private long n;
//     private double sum;
//     private double sumSquares;
//     private Max max;
//     private Min min;
//     private boolean removeMade;
// 
//     public BothWaySummaryStatistics() {
//         n = 0;
//         sum = 0;
//         sumSquares = 0;
//         max = new Max();
//         min = new Min();
//         removeMade = false;
//     }
// 
//     public void addValue(double d) {
//         sum += d;
//         sumSquares += d * d;
//         n++;
//         if (!removeMade) {
//             max.increment(d);
//             min.increment(d);
//         }
//     }
// 
//     public void removeValue(double d) {
//         if (n == 0) {
//             throw new InsufficientDataException();
//         }
// 
//         sum -= d;
//         sumSquares -= d * d;
//         n--;
//         removeMade = true;
//         max.clear();
//         min.clear();
//     }
// 
//     @Override
//     public double getMean() {
//         if (n == 0) return 0;
//         return sum / n;
//     }
// 
//     @Override
//     public double getVariance() {
//         if (n == 0) return Double.NaN;
//         if (n == 1) return 0;
//         double mean = getMean();
//         return sumSquares / n - mean * mean;
//     }
// 
//     @Override
//     public double getStandardDeviation() {
//         if (n == 0) return Double.NaN;
//         if (n == 1) return 0;
//         return FastMath.sqrt(getVariance());
//     }
// 
//     /**
//      * @return max value or Double.NaN if removeValue was called
//      */
//     @Override
//     public double getMax() {
//         return max.getResult();
//     }
// 
//     /**
//      * @return min value or Double.NaN if removeValue was called
//      */
//     @Override
//     public double getMin() {
//         return min.getResult();
//     }
// 
//     @Override
//     public long getN() {
//         return n;
//     }
// 
//     @Override
//     public double getSum() {
//         return sum;
//     }
// 
//     @Override
//     public BothWaySummaryStatistics clone() {
//         BothWaySummaryStatistics copy = new BothWaySummaryStatistics();
//         copy.n = n;
//         copy.sum = sum;
//         copy.sumSquares = sumSquares;
//         return copy;
//     }
// }
// 
// Path: src/main/java/ro/hasna/ts/math/type/MatrixProfile.java
// @Data
// public class MatrixProfile implements Cloneable {
//     private final double[] profile;
//     private final int[] indexProfile;
// 
//     public MatrixProfile(double[] profile, int[] indexProfile) {
//         if (profile.length != indexProfile.length) {
//             throw new DimensionMismatchException(profile.length, indexProfile.length);
//         }
// 
//         this.profile = profile;
//         this.indexProfile = indexProfile;
//     }
// 
//     public MatrixProfile(int size) {
//         profile = new double[size];
//         indexProfile = new int[size];
//         for (int j = 0; j < size; j++) {
//             profile[j] = Double.POSITIVE_INFINITY;
//             indexProfile[j] = -1;
//         }
//     }
// 
//     @Override
//     public MatrixProfile clone() {
//         int size = profile.length;
//         MatrixProfile copy = new MatrixProfile(size);
//         for (int i = 0; i < size; i++) {
//             copy.profile[i] = profile[i];
//             copy.indexProfile[i] = indexProfile[i];
//         }
//         return copy;
//     }
// }


// Path: src/main/java/ro/hasna/ts/math/representation/mp/StompTransformer.java
import ro.hasna.ts.math.stat.BothWaySummaryStatistics;
import ro.hasna.ts.math.type.MatrixProfile;
import java.util.function.Predicate;

/*
 * Copyright 2015 Octavian Hasna
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ro.hasna.ts.math.representation.mp;




/**
 * Implements the STOMP algorithm to compute the self join matrix profile.
 * <p>
 * Reference:
 * Zhu Y., Zimmerman Z., Senobari N. S., Yeh C. C. M., Funning G., Mueen A., Keogh E. (2016)
 * <i>Exploiting a Novel Algorithm and GPUs to Break the One Hundred Million Barrier for Time Series Motifs and Joins</i>
 * </p>
 *
 * @since 0.17
 */
public class StompTransformer extends SelfJoinAbstractMatrixProfileTransformer {
    private static final long serialVersionUID = 2910211807207716085L;

    public StompTransformer(int window) {
        super(window);
    }

    public StompTransformer(int window, double exclusionZonePercentage, boolean useNormalization) {
        super(window, exclusionZonePercentage, useNormalization);
    }

    @Override
    protected MatrixProfile computeNormalizedMatrixProfile(double[] input, int skip, Predicate<MatrixProfile> callback) {
        int n = input.length - window + 1;
        MatrixProfile mp = new MatrixProfile(n);
        double[] distanceProfile = new double[n];

        double[] productSums = new double[n];
        BothWaySummaryStatistics first = new BothWaySummaryStatistics();
        BothWaySummaryStatistics second = new BothWaySummaryStatistics();
        for (int i = 0; i < window; i++) {
            first.addValue(input[i]);
            second.addValue(input[i + skip]);
        }

        computeFirstNormalizedDistanceProfile(input, first, input, second, skip, n, productSums, distanceProfile);
        updateMatrixProfileFromDistanceProfile(distanceProfile, 0, skip, n, mp, callback);
        for (int i = 1; i < n - skip; i++) {
            computeNextNormalizedDistanceProfile(input, skip, n, distanceProfile, productSums, first, second, i);
            updateMatrixProfileFromDistanceProfile(distanceProfile, i, skip, n, mp, callback);
        }
        updateMatrixProfileWithSqrt(mp);