/**
  * Get an upper bound of the fitted harmonic amplitude.
  *
  * @return upper bound of the fitted harmonic amplitude
  */
 public double getHarmonicAmplitude() {
   double amplitude = 0;
   for (int i = 0; i < pulsations.length; ++i) {
     amplitude +=
         FastMath.hypot(fitted[secularDegree + 2 * i + 1], fitted[secularDegree + 2 * i + 2]);
   }
   return amplitude;
 }
  /** Контрольная точка. */
  private ControlPoint(
      final double latDeg,
      final double lonDeg,
      @NotNull final Observation normal,
      @NotNull final Observation geodetic,
      @NotNull final GeodeticToDoubleFunction interpolator) {
    super(latDeg, lonDeg);
    this.interpolator = interpolator;

    geometric =
        new Observation(
            geodetic.value() - normal.value(), FastMath.hypot(geodetic.error(), normal.error()));
  }
 @Override
 public Observation signal() {
   return new Observation(
       geometric.value() - model().value(), FastMath.hypot(geometric.error(), model().error()));
 }
  /**
   * Calculates the compact Singular Value Decomposition of the given matrix.
   *
   * @param matrix Matrix to decompose.
   */
  public SingularValueDecomposition(final RealMatrix matrix) {
    final double[][] A;

    // "m" is always the largest dimension.
    if (matrix.getRowDimension() < matrix.getColumnDimension()) {
      transposed = true;
      A = matrix.transpose().getData();
      m = matrix.getColumnDimension();
      n = matrix.getRowDimension();
    } else {
      transposed = false;
      A = matrix.getData();
      m = matrix.getRowDimension();
      n = matrix.getColumnDimension();
    }

    singularValues = new double[n];
    final double[][] U = new double[m][n];
    final double[][] V = new double[n][n];
    final double[] e = new double[n];
    final double[] work = new double[m];
    // Reduce A to bidiagonal form, storing the diagonal elements
    // in s and the super-diagonal elements in e.
    final int nct = FastMath.min(m - 1, n);
    final int nrt = FastMath.max(0, n - 2);
    for (int k = 0; k < FastMath.max(nct, nrt); k++) {
      if (k < nct) {
        // Compute the transformation for the k-th column and
        // place the k-th diagonal in s[k].
        // Compute 2-norm of k-th column without under/overflow.
        singularValues[k] = 0;
        for (int i = k; i < m; i++) {
          singularValues[k] = FastMath.hypot(singularValues[k], A[i][k]);
        }
        if (singularValues[k] != 0) {
          if (A[k][k] < 0) {
            singularValues[k] = -singularValues[k];
          }
          for (int i = k; i < m; i++) {
            A[i][k] /= singularValues[k];
          }
          A[k][k] += 1;
        }
        singularValues[k] = -singularValues[k];
      }
      for (int j = k + 1; j < n; j++) {
        if (k < nct && singularValues[k] != 0) {
          // Apply the transformation.
          double t = 0;
          for (int i = k; i < m; i++) {
            t += A[i][k] * A[i][j];
          }
          t = -t / A[k][k];
          for (int i = k; i < m; i++) {
            A[i][j] += t * A[i][k];
          }
        }
        // Place the k-th row of A into e for the
        // subsequent calculation of the row transformation.
        e[j] = A[k][j];
      }
      if (k < nct) {
        // Place the transformation in U for subsequent back
        // multiplication.
        for (int i = k; i < m; i++) {
          U[i][k] = A[i][k];
        }
      }
      if (k < nrt) {
        // Compute the k-th row transformation and place the
        // k-th super-diagonal in e[k].
        // Compute 2-norm without under/overflow.
        e[k] = 0;
        for (int i = k + 1; i < n; i++) {
          e[k] = FastMath.hypot(e[k], e[i]);
        }
        if (e[k] != 0) {
          if (e[k + 1] < 0) {
            e[k] = -e[k];
          }
          for (int i = k + 1; i < n; i++) {
            e[i] /= e[k];
          }
          e[k + 1] += 1;
        }
        e[k] = -e[k];
        if (k + 1 < m && e[k] != 0) {
          // Apply the transformation.
          for (int i = k + 1; i < m; i++) {
            work[i] = 0;
          }
          for (int j = k + 1; j < n; j++) {
            for (int i = k + 1; i < m; i++) {
              work[i] += e[j] * A[i][j];
            }
          }
          for (int j = k + 1; j < n; j++) {
            final double t = -e[j] / e[k + 1];
            for (int i = k + 1; i < m; i++) {
              A[i][j] += t * work[i];
            }
          }
        }

        // Place the transformation in V for subsequent
        // back multiplication.
        for (int i = k + 1; i < n; i++) {
          V[i][k] = e[i];
        }
      }
    }
    // Set up the final bidiagonal matrix or order p.
    int p = n;
    if (nct < n) {
      singularValues[nct] = A[nct][nct];
    }
    if (m < p) {
      singularValues[p - 1] = 0;
    }
    if (nrt + 1 < p) {
      e[nrt] = A[nrt][p - 1];
    }
    e[p - 1] = 0;

    // Generate U.
    for (int j = nct; j < n; j++) {
      for (int i = 0; i < m; i++) {
        U[i][j] = 0;
      }
      U[j][j] = 1;
    }
    for (int k = nct - 1; k >= 0; k--) {
      if (singularValues[k] != 0) {
        for (int j = k + 1; j < n; j++) {
          double t = 0;
          for (int i = k; i < m; i++) {
            t += U[i][k] * U[i][j];
          }
          t = -t / U[k][k];
          for (int i = k; i < m; i++) {
            U[i][j] += t * U[i][k];
          }
        }
        for (int i = k; i < m; i++) {
          U[i][k] = -U[i][k];
        }
        U[k][k] = 1 + U[k][k];
        for (int i = 0; i < k - 1; i++) {
          U[i][k] = 0;
        }
      } else {
        for (int i = 0; i < m; i++) {
          U[i][k] = 0;
        }
        U[k][k] = 1;
      }
    }

    // Generate V.
    for (int k = n - 1; k >= 0; k--) {
      if (k < nrt && e[k] != 0) {
        for (int j = k + 1; j < n; j++) {
          double t = 0;
          for (int i = k + 1; i < n; i++) {
            t += V[i][k] * V[i][j];
          }
          t = -t / V[k + 1][k];
          for (int i = k + 1; i < n; i++) {
            V[i][j] += t * V[i][k];
          }
        }
      }
      for (int i = 0; i < n; i++) {
        V[i][k] = 0;
      }
      V[k][k] = 1;
    }

    // Main iteration loop for the singular values.
    final int pp = p - 1;
    int iter = 0;
    while (p > 0) {
      int k;
      int kase;
      // Here is where a test for too many iterations would go.
      // This section of the program inspects for
      // negligible elements in the s and e arrays.  On
      // completion the variables kase and k are set as follows.
      // kase = 1     if s(p) and e[k-1] are negligible and k<p
      // kase = 2     if s(k) is negligible and k<p
      // kase = 3     if e[k-1] is negligible, k<p, and
      //              s(k), ..., s(p) are not negligible (qr step).
      // kase = 4     if e(p-1) is negligible (convergence).
      for (k = p - 2; k >= 0; k--) {
        final double threshold =
            TINY + EPS * (FastMath.abs(singularValues[k]) + FastMath.abs(singularValues[k + 1]));

        // the following condition is written this way in order
        // to break out of the loop when NaN occurs, writing it
        // as "if (FastMath.abs(e[k]) <= threshold)" would loop
        // indefinitely in case of NaNs because comparison on NaNs
        // always return false, regardless of what is checked
        // see issue MATH-947
        if (!(FastMath.abs(e[k]) > threshold)) {
          e[k] = 0;
          break;
        }
      }

      if (k == p - 2) {
        kase = 4;
      } else {
        int ks;
        for (ks = p - 1; ks >= k; ks--) {
          if (ks == k) {
            break;
          }
          final double t =
              (ks != p ? FastMath.abs(e[ks]) : 0) + (ks != k + 1 ? FastMath.abs(e[ks - 1]) : 0);
          if (FastMath.abs(singularValues[ks]) <= TINY + EPS * t) {
            singularValues[ks] = 0;
            break;
          }
        }
        if (ks == k) {
          kase = 3;
        } else if (ks == p - 1) {
          kase = 1;
        } else {
          kase = 2;
          k = ks;
        }
      }
      k++;
      // Perform the task indicated by kase.
      switch (kase) {
          // Deflate negligible s(p).
        case 1:
          {
            double f = e[p - 2];
            e[p - 2] = 0;
            for (int j = p - 2; j >= k; j--) {
              double t = FastMath.hypot(singularValues[j], f);
              final double cs = singularValues[j] / t;
              final double sn = f / t;
              singularValues[j] = t;
              if (j != k) {
                f = -sn * e[j - 1];
                e[j - 1] = cs * e[j - 1];
              }

              for (int i = 0; i < n; i++) {
                t = cs * V[i][j] + sn * V[i][p - 1];
                V[i][p - 1] = -sn * V[i][j] + cs * V[i][p - 1];
                V[i][j] = t;
              }
            }
          }
          break;
          // Split at negligible s(k).
        case 2:
          {
            double f = e[k - 1];
            e[k - 1] = 0;
            for (int j = k; j < p; j++) {
              double t = FastMath.hypot(singularValues[j], f);
              final double cs = singularValues[j] / t;
              final double sn = f / t;
              singularValues[j] = t;
              f = -sn * e[j];
              e[j] = cs * e[j];

              for (int i = 0; i < m; i++) {
                t = cs * U[i][j] + sn * U[i][k - 1];
                U[i][k - 1] = -sn * U[i][j] + cs * U[i][k - 1];
                U[i][j] = t;
              }
            }
          }
          break;
          // Perform one qr step.
        case 3:
          {
            // Calculate the shift.
            final double maxPm1Pm2 =
                FastMath.max(
                    FastMath.abs(singularValues[p - 1]), FastMath.abs(singularValues[p - 2]));
            final double scale =
                FastMath.max(
                    FastMath.max(
                        FastMath.max(maxPm1Pm2, FastMath.abs(e[p - 2])),
                        FastMath.abs(singularValues[k])),
                    FastMath.abs(e[k]));
            final double sp = singularValues[p - 1] / scale;
            final double spm1 = singularValues[p - 2] / scale;
            final double epm1 = e[p - 2] / scale;
            final double sk = singularValues[k] / scale;
            final double ek = e[k] / scale;
            final double b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2.0;
            final double c = (sp * epm1) * (sp * epm1);
            double shift = 0;
            if (b != 0 || c != 0) {
              shift = FastMath.sqrt(b * b + c);
              if (b < 0) {
                shift = -shift;
              }
              shift = c / (b + shift);
            }
            double f = (sk + sp) * (sk - sp) + shift;
            double g = sk * ek;
            // Chase zeros.
            for (int j = k; j < p - 1; j++) {
              double t = FastMath.hypot(f, g);
              double cs = f / t;
              double sn = g / t;
              if (j != k) {
                e[j - 1] = t;
              }
              f = cs * singularValues[j] + sn * e[j];
              e[j] = cs * e[j] - sn * singularValues[j];
              g = sn * singularValues[j + 1];
              singularValues[j + 1] = cs * singularValues[j + 1];

              for (int i = 0; i < n; i++) {
                t = cs * V[i][j] + sn * V[i][j + 1];
                V[i][j + 1] = -sn * V[i][j] + cs * V[i][j + 1];
                V[i][j] = t;
              }
              t = FastMath.hypot(f, g);
              cs = f / t;
              sn = g / t;
              singularValues[j] = t;
              f = cs * e[j] + sn * singularValues[j + 1];
              singularValues[j + 1] = -sn * e[j] + cs * singularValues[j + 1];
              g = sn * e[j + 1];
              e[j + 1] = cs * e[j + 1];
              if (j < m - 1) {
                for (int i = 0; i < m; i++) {
                  t = cs * U[i][j] + sn * U[i][j + 1];
                  U[i][j + 1] = -sn * U[i][j] + cs * U[i][j + 1];
                  U[i][j] = t;
                }
              }
            }
            e[p - 2] = f;
            iter = iter + 1;
          }
          break;
          // Convergence.
        default:
          {
            // Make the singular values positive.
            if (singularValues[k] <= 0) {
              singularValues[k] = singularValues[k] < 0 ? -singularValues[k] : 0;

              for (int i = 0; i <= pp; i++) {
                V[i][k] = -V[i][k];
              }
            }
            // Order the singular values.
            while (k < pp) {
              if (singularValues[k] >= singularValues[k + 1]) {
                break;
              }
              double t = singularValues[k];
              singularValues[k] = singularValues[k + 1];
              singularValues[k + 1] = t;
              if (k < n - 1) {
                for (int i = 0; i < n; i++) {
                  t = V[i][k + 1];
                  V[i][k + 1] = V[i][k];
                  V[i][k] = t;
                }
              }
              if (k < m - 1) {
                for (int i = 0; i < m; i++) {
                  t = U[i][k + 1];
                  U[i][k + 1] = U[i][k];
                  U[i][k] = t;
                }
              }
              k++;
            }
            iter = 0;
            p--;
          }
          break;
      }
    }

    // Set the small value tolerance used to calculate rank and pseudo-inverse
    tol = FastMath.max(m * singularValues[0] * EPS, FastMath.sqrt(Precision.SAFE_MIN));

    if (!transposed) {
      cachedU = MatrixUtils.createRealMatrix(U);
      cachedV = MatrixUtils.createRealMatrix(V);
    } else {
      cachedU = MatrixUtils.createRealMatrix(V);
      cachedV = MatrixUtils.createRealMatrix(U);
    }
  }