content: 
          /*
           * Copyright (C) 2017 The Android Open Source Project
           *
           * 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 androidx.motionlayout.cycleEditor;
  
  /**
           * Provides spline interpolation code.
           * Currently not used but it is anticipated that we will be using it in the
           * KeyMotion
           *
           * @DoNotShow
           */
  
          public class HyperSpline extends Interpolator {
              int mPoints;
              Cubic[][] mCurve;
              int mDimensionality;
              double[] mCurveLength;
              double mTotalLength;
              double[][] mCtl;
  
              /**
               * Spline in N dimensions
               *
               * @param points [mPoints][dimensionality]
               */
              public HyperSpline(double[][] points) {
                  setup(points);
              }
  
              public HyperSpline() {
              }
  
              public HyperSpline(double[] time, double[][] y) {
                  double[][] all = new double[y.length + 1][];
                  all[0] = time;
                  for (int i = 1; i < all.length; i++) {
                      all[i] = y[i];
                  }
                  setup(all);
              }
  
              public void setup(double[][] points) {
                  mDimensionality = points[0].length;
                  mPoints = points.length;
                  mCtl = new double[mDimensionality][mPoints];
                  mCurve = new Cubic[mDimensionality][];
                  for (int d = 0; d < mDimensionality; d++) {
                      for (int p = 0; p < mPoints; p++) {
                          mCtl[d][p] = points[p][d];
                      }
                  }
  
                  for (int d = 0; d < mDimensionality; d++) {
                      mCurve[d] = calcNaturalCubic(mCtl[d].length, mCtl[d]);
                  }
  
                  mCurveLength = new double[mPoints - 1];
                  mTotalLength = 0;
                  Cubic[] temp = new Cubic[mDimensionality];
                  for (int p = 0; p < mCurveLength.length; p++) {
                      for (int d = 0; d < mDimensionality; d++) {
  
                          temp[d] = mCurve[d][p];
  
                      }
                      mTotalLength += mCurveLength[p] = approxLength(temp);
                  }
              }
  
              public void getVelocity(double p, double[] v) {
                  double pos = p * mTotalLength;
                  double sum = 0;
                  int k = 0;
                  for (; k < mCurveLength.length - 1 && mCurveLength[k] < pos; k++) {
                      pos -= mCurveLength[k];
                  }
                  for (int i = 0; i < v.length; i++) {
                      v[i] = mCurve[i][k].vel(pos / mCurveLength[k]);
                  }
              }
  
              public void getPos(double p, double[] x) {
                  double pos = p * mTotalLength;
                  double sum = 0;
                  int k = 0;
                  for (; k < mCurveLength.length - 1 && mCurveLength[k] < pos; k++) {
                      pos -= mCurveLength[k];
                  }
                  for (int i = 0; i < x.length; i++) {
                      x[i] = mCurve[i][k].eval(pos / mCurveLength[k]);
                  }
              }
  
              public void getPos(double p, float[] x) {
                  double pos = p * mTotalLength;
                  double sum = 0;
                  int k = 0;
                  for (; k < mCurveLength.length - 1 && mCurveLength[k] < pos; k++) {
                      pos -= mCurveLength[k];
                  }
                  for (int i = 0; i < x.length; i++) {
                      x[i] = (float) mCurve[i][k].eval(pos / mCurveLength[k]);
                  }
              }
  
              public double getPos(double p, int splineNumber) {
                  double pos = p * mTotalLength;
                  double sum = 0;
                  int k = 0;
                  for (; k < mCurveLength.length - 1 && mCurveLength[k] < pos; k++) {
                      pos -= mCurveLength[k];
                  }
                  return mCurve[splineNumber][k].eval(pos / mCurveLength[k]);
              }
  
              @Override
              void getSlope(double t, double[] v) {
  
  }
  
              @Override
              double getSlope(double t, int j) {
                  return 0;
              }
  
              @Override
              double[] getTimePoints() {
                  return new double[0];
              }
  
              public double approxLength(Cubic[] curve) {
                  double sum = 0;
  
                  int N = curve.length;
                  double[] old = new double[curve.length];
                  for (double i = 0; i < 1; i += .1) {
                      double s = 0;
                      for (int j = 0; j < curve.length; j++) {
                          double tmp = old[j];
                          tmp -= old[j] = curve[j].eval(i);
                          s += tmp * tmp;
                      }
                      if (i > 0) {
                          sum += Math.sqrt(s);
                      }
  
                  }
                  double s = 0;
                  for (int j = 0; j < curve.length; j++) {
                      double tmp = old[j];
                      tmp -= old[j] = curve[j].eval(1);
                      s += tmp * tmp;
                  }
                  sum += Math.sqrt(s);
                  return sum;
              }
  
              static Cubic[] calcNaturalCubic(int n, double[] x) {
                  double[] gamma = new double[n];
                  double[] delta = new double[n];
                  double[] D = new double[n];
                  n -= 1;
  
                  gamma[0] = 1.0f / 2.0f;
                  for (int i = 1; i < n; i++) {
                      gamma[i] = 1 / (4 - gamma[i - 1]);
                  }
                  gamma[n] = 1 / (2 - gamma[n - 1]);
  
                  delta[0] = 3 * (x[1] - x[0]) * gamma[0];
                  for (int i = 1; i < n; i++) {
                      delta[i] = (3 * (x[i + 1] - x[i - 1]) - delta[i - 1]) * gamma[i];
                  }
                  delta[n] = (3 * (x[n] - x[n - 1]) - delta[n - 1]) * gamma[n];
  
                  D[n] = delta[n];
                  for (int i = n - 1; i >= 0; i--) {
                      D[i] = delta[i] - gamma[i] * D[i + 1];
                  }
  
                  Cubic[] C = new Cubic[n];
                  for (int i = 0; i < n; i++) {
                      C[i] = new Cubic((float) x[i], D[i], 3 * (x[i + 1] - x[i]) - 2
                              * D[i] - D[i + 1], 2 * (x[i] - x[i + 1]) + D[i] + D[i + 1]);
                  }
                  return C;
              }
  
              public static class Cubic {
                  double mA, mB, mC, mD;
  
                  public Cubic(double a, double b, double c, double d) {
                      mA = a;
                      mB = b;
                      mC = c;
                      mD = d;
                  }
  
                  public static final double THIRD = 1 / 3.0;
                  public static final double HALF = 1 / 2.0;
  
                  public double eval(double u) {
                      return (((mD * u) + mC) * u + mB) * u + mA;
                  }
  
                  public double vel(double v) {
                      //  (((mD * u) + mC) * u + mB) * u + mA
                      //  =  "mA + u*mB + u*u*mC+u*u*u*mD" a cubic expression
                      // diff with respect to u = mB + u*mC/2+ u*u*mD/3
                      // made efficent  (mD*u/3+mC/2)*u+mB;
  
                      return (mD * THIRD * v + mC * HALF) * v + mB;
                  }
              }
          }
  