package edu.ucr.impl;

/*
 * Copyright 2020 Renjie Wu and Sara Alaee
 *
 * 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.
 */

// Rewrite into Java from Sara's C++ code
public class DtwUpd {
    /**
     * Calculate DTW distance. A and B must have same length.
     * @param A the time series array 1
     * @param B the time series array 2
     * @param radius the size of Sakoe-Chiba warping band
     * @return the DTW distance
     */
    public static double computeDtw(double[] A, double[] B, int radius) {
        var m = A.length;
        var r = radius;

        int i, j, k;
        double x, y, z;

        // Instead of using matrix of size O(m^2) or O(mr), we will reuse two arrays of size O(r).
        var cost = new double[2 * r + 1];
        var costPrev = new double[2 * r + 1];

        for (k = 0; k < 2 * r + 1; k++) {
            cost[k] = Double.POSITIVE_INFINITY;
            costPrev[k] = Double.POSITIVE_INFINITY;
        }

        for (i = 0; i < m; i++) {
            k = Integer.max(0, r - i);

            for (j = Integer.max(0, i - r); j <= Integer.min(m - 1, i + r); j++, k++) {
                // Initialize all row and column
                if (i == 0 && j == 0) {
                    var c = A[0] - B[0];
                    cost[k] = c * c;
                    continue;
                }
                if (j - 1 < 0 || k - 1 < 0) {
                    y = Double.POSITIVE_INFINITY;
                } else {
                    y = cost[k - 1];
                }
                if (i < 1 || k > 2 * r - 1) {
                    x = Double.POSITIVE_INFINITY;
                } else {
                    x = costPrev[k + 1];
                }
                if (i < 1 || j < 1) {
                    z = Double.POSITIVE_INFINITY;
                } else {
                    z = costPrev[k];
                }

                // Classic DTW calculation
                var d = A[i] - B[j];
                cost[k] = Double.min(Double.min(x, y), z) + d * d;
            }

            // Move current array to previous array
            var tmp = cost;
            cost = costPrev;
            costPrev = tmp;
        }
        k--;

        // The DTW distance is in the last cell in the matrix of size O(m^2) or at the middle of our array.
        return costPrev[k];
    }
}
