// Path: src/main/java/ro/hasna/ts/math/distribution/DistributionDivider.java
// public interface DistributionDivider extends Serializable {
//     /**
//      * Get the breakpoints for dividing the distribution in equal areas of probability.
//      * NOTE: the breakpoints are in ascending order.
//      *
//      * @param areas the number of areas
//      * @return the list of breakpoints
//      * @throws NumberIsTooSmallException if areas is smaller than 2
//      */
//     double[] getBreakpoints(int areas);
// }
// 
// Path: src/main/java/ro/hasna/ts/math/distribution/NormalDistributionDivider.java
// public class NormalDistributionDivider implements DistributionDivider {
//     private static final long serialVersionUID = -909800668897655203L;
// 
//     @Override
//     public double[] getBreakpoints(int areas) {
//         if (areas < 2) {
//             throw new NumberIsTooSmallException(areas, 2, true);
//         }
// 
//         NormalDistribution normalDistribution = new NormalDistribution();
//         int len = areas - 1;
//         double[] result = new double[len];
//         double searchArea = 1.0 / areas;
//         for (int i = 0; i < len; i++) {
//             result[i] = normalDistribution.inverseCumulativeProbability(searchArea * (i + 1));
//         }
// 
//         return result;
//     }
// }
// 
// Path: src/main/java/ro/hasna/ts/math/normalization/Normalizer.java
// public interface Normalizer extends Serializable {
//     /**
//      * Normalize the sequence of values.
//      * NOTE: The length of the output array should be equal to the input array.
//      *
//      * @param values the sequence of values
//      * @return the normalized sequence
//      */
//     double[] normalize(double[] values);
// }
// 
// Path: src/main/java/ro/hasna/ts/math/normalization/ZNormalizer.java
// public class ZNormalizer implements Normalizer {
//     private static final long serialVersionUID = 6446811014325682141L;
//     private final Mean mean;
//     private final StandardDeviation standardDeviation;
// 
//     public ZNormalizer() {
//         this(new Mean(), new StandardDeviation(false));
//     }
// 
//     /**
//      * Creates a new instance of this class with the given mean and standard deviation algorithms.
//      *
//      * @param mean              the mean
//      * @param standardDeviation the standard deviation
//      */
//     public ZNormalizer(final Mean mean, final StandardDeviation standardDeviation) {
//         this.mean = mean;
//         this.standardDeviation = standardDeviation;
//     }
// 
//     /**
//      * {@inheritDoc}
//      */
//     @Override
//     public double[] normalize(double[] values) {
//         double m = mean.evaluate(values);
//         double sd = standardDeviation.evaluate(values, m);
// 
//         int length = values.length;
//         double[] normalizedValues = new double[length];
//         for (int i = 0; i < length; i++) {
//             normalizedValues[i] = (values[i] - m) / sd;
//         }
//         return normalizedValues;
//     }
// }
// 
// Path: src/main/java/ro/hasna/ts/math/type/SaxPair.java
// @Data
// public class SaxPair {
//     /**
//      * The symbol value for the segment.
//      */
//     private final int symbol;
//     /**
//      * The size of the alphabet used by the symbol.
//      */
//     private final int alphabetSize;
// }


import org.apache.commons.math3.exception.DimensionMismatchException;
import ro.hasna.ts.math.distribution.DistributionDivider;
import ro.hasna.ts.math.distribution.NormalDistributionDivider;
import ro.hasna.ts.math.normalization.Normalizer;
import ro.hasna.ts.math.normalization.ZNormalizer;
import ro.hasna.ts.math.type.SaxPair;
/*
 * 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;


/**
 * Implements the Indexable Symbolic Aggregate Approximation (iSAX) algorithm.
 * <p>
 * Reference:
 * Camerra A., Palpanas T., Shieh J., Keogh E. (2010)
 * <i>iSAX 2.0: Indexing and mining one billion time series</i>
 * </p>
 *
 * @since 0.7
 */
public class IndexableSymbolicAggregateApproximation implements GenericTransformer<double[], SaxPair[]> {
    private static final long serialVersionUID = -1652621695908903282L;
    private final PiecewiseAggregateApproximation paa;
    private final Normalizer normalizer;
    private final DistributionDivider distributionDivider;
    private final int[] alphabetSizes;

    /**
     * Creates a new instance of this class with a given number of segments and
     * the size of the alphabet.
     *
     * @param segments      the number of segments
     * @param alphabetSizes the size of the alphabet used to discretise the values for every segment
     */
    public IndexableSymbolicAggregateApproximation(int segments, int[] alphabetSizes) {
        this(new PiecewiseAggregateApproximation(segments), new ZNormalizer(), alphabetSizes, new NormalDistributionDivider());
    }


    /**
     * Creates a new instance of this class with a given PAA and normalizer algorithm and
     * the breakpoints used to divide the distribution of the values.
     *
     * @param paa                 the Piecewise Aggregate Approximation algorithm
     * @param normalizer          the normalizer (it can be null if the values were normalized)
     * @param alphabetSizes       the size of the alphabet used to discretise the values for every segment
     * @param distributionDivider the divider that gets the list of breakpoints (values that divide a
     *                            distribution in equal areas of probability)
     * @throws DimensionMismatchException if the number of segments is different than the length of alphabetSizes
     */
// Path: src/main/java/ro/hasna/ts/math/distribution/DistributionDivider.java
// public interface DistributionDivider extends Serializable {
//     /**
//      * Get the breakpoints for dividing the distribution in equal areas of probability.
//      * NOTE: the breakpoints are in ascending order.
//      *
//      * @param areas the number of areas
//      * @return the list of breakpoints
//      * @throws NumberIsTooSmallException if areas is smaller than 2
//      */
//     double[] getBreakpoints(int areas);
// }
// 
// Path: src/main/java/ro/hasna/ts/math/distribution/NormalDistributionDivider.java
// public class NormalDistributionDivider implements DistributionDivider {
//     private static final long serialVersionUID = -909800668897655203L;
// 
//     @Override
//     public double[] getBreakpoints(int areas) {
//         if (areas < 2) {
//             throw new NumberIsTooSmallException(areas, 2, true);
//         }
// 
//         NormalDistribution normalDistribution = new NormalDistribution();
//         int len = areas - 1;
//         double[] result = new double[len];
//         double searchArea = 1.0 / areas;
//         for (int i = 0; i < len; i++) {
//             result[i] = normalDistribution.inverseCumulativeProbability(searchArea * (i + 1));
//         }
// 
//         return result;
//     }
// }
// 
// Path: src/main/java/ro/hasna/ts/math/normalization/Normalizer.java
// public interface Normalizer extends Serializable {
//     /**
//      * Normalize the sequence of values.
//      * NOTE: The length of the output array should be equal to the input array.
//      *
//      * @param values the sequence of values
//      * @return the normalized sequence
//      */
//     double[] normalize(double[] values);
// }
// 
// Path: src/main/java/ro/hasna/ts/math/normalization/ZNormalizer.java
// public class ZNormalizer implements Normalizer {
//     private static final long serialVersionUID = 6446811014325682141L;
//     private final Mean mean;
//     private final StandardDeviation standardDeviation;
// 
//     public ZNormalizer() {
//         this(new Mean(), new StandardDeviation(false));
//     }
// 
//     /**
//      * Creates a new instance of this class with the given mean and standard deviation algorithms.
//      *
//      * @param mean              the mean
//      * @param standardDeviation the standard deviation
//      */
//     public ZNormalizer(final Mean mean, final StandardDeviation standardDeviation) {
//         this.mean = mean;
//         this.standardDeviation = standardDeviation;
//     }
// 
//     /**
//      * {@inheritDoc}
//      */
//     @Override
//     public double[] normalize(double[] values) {
//         double m = mean.evaluate(values);
//         double sd = standardDeviation.evaluate(values, m);
// 
//         int length = values.length;
//         double[] normalizedValues = new double[length];
//         for (int i = 0; i < length; i++) {
//             normalizedValues[i] = (values[i] - m) / sd;
//         }
//         return normalizedValues;
//     }
// }
// 
// Path: src/main/java/ro/hasna/ts/math/type/SaxPair.java
// @Data
// public class SaxPair {
//     /**
//      * The symbol value for the segment.
//      */
//     private final int symbol;
//     /**
//      * The size of the alphabet used by the symbol.
//      */
//     private final int alphabetSize;
// }


// Path: src/main/java/ro/hasna/ts/math/representation/IndexableSymbolicAggregateApproximation.java
import org.apache.commons.math3.exception.DimensionMismatchException;
import ro.hasna.ts.math.distribution.DistributionDivider;
import ro.hasna.ts.math.distribution.NormalDistributionDivider;
import ro.hasna.ts.math.normalization.Normalizer;
import ro.hasna.ts.math.normalization.ZNormalizer;
import ro.hasna.ts.math.type.SaxPair;

/*
 * 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;


/**
 * Implements the Indexable Symbolic Aggregate Approximation (iSAX) algorithm.
 * <p>
 * Reference:
 * Camerra A., Palpanas T., Shieh J., Keogh E. (2010)
 * <i>iSAX 2.0: Indexing and mining one billion time series</i>
 * </p>
 *
 * @since 0.7
 */
public class IndexableSymbolicAggregateApproximation implements GenericTransformer<double[], SaxPair[]> {
    private static final long serialVersionUID = -1652621695908903282L;
    private final PiecewiseAggregateApproximation paa;
    private final Normalizer normalizer;
    private final DistributionDivider distributionDivider;
    private final int[] alphabetSizes;

    /**
     * Creates a new instance of this class with a given number of segments and
     * the size of the alphabet.
     *
     * @param segments      the number of segments
     * @param alphabetSizes the size of the alphabet used to discretise the values for every segment
     */
    public IndexableSymbolicAggregateApproximation(int segments, int[] alphabetSizes) {
        this(new PiecewiseAggregateApproximation(segments), new ZNormalizer(), alphabetSizes, new NormalDistributionDivider());
    }


    /**
     * Creates a new instance of this class with a given PAA and normalizer algorithm and
     * the breakpoints used to divide the distribution of the values.
     *
     * @param paa                 the Piecewise Aggregate Approximation algorithm
     * @param normalizer          the normalizer (it can be null if the values were normalized)
     * @param alphabetSizes       the size of the alphabet used to discretise the values for every segment
     * @param distributionDivider the divider that gets the list of breakpoints (values that divide a
     *                            distribution in equal areas of probability)
     * @throws DimensionMismatchException if the number of segments is different than the length of alphabetSizes
     */
    public IndexableSymbolicAggregateApproximation(PiecewiseAggregateApproximation paa, Normalizer normalizer,