/**
  *    Symmetric tri-diagonal QL algorithm.
  *
  *    This is derived from the Algol procedures tql2, by
  *    Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
  *    Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
  *    Fortran subroutine in EISPACK.
  *
  * @access private
  */
 private function tql2()
 {
     for ($i = 1; $i < $this->n; ++$i) {
         $this->e[$i - 1] = $this->e[$i];
     }
     $this->e[$this->n - 1] = 0.0;
     $f = 0.0;
     $tst1 = 0.0;
     $eps = pow(2.0, -52.0);
     for ($l = 0; $l < $this->n; ++$l) {
         // Find small sub-diagonal element
         $tst1 = max($tst1, abs($this->d[$l]) + abs($this->e[$l]));
         $m = $l;
         while ($m < $this->n) {
             if (abs($this->e[$m]) <= $eps * $tst1) {
                 break;
             }
             ++$m;
         }
         // If m == l, $this->d[l] is an eigenvalue,
         // otherwise, iterate.
         if ($m > $l) {
             $iterator = 0;
             do {
                 // Could check iteration count here.
                 $iterator += 1;
                 // Compute implicit shift
                 $g = $this->d[$l];
                 $p = ($this->d[$l + 1] - $g) / (2.0 * $this->e[$l]);
                 $r = Matrix::hypo($p, 1.0);
                 if ($p < 0) {
                     $r *= -1;
                 }
                 $this->d[$l] = $this->e[$l] / ($p + $r);
                 $this->d[$l + 1] = $this->e[$l] * ($p + $r);
                 $dl1 = $this->d[$l + 1];
                 $h = $g - $this->d[$l];
                 for ($i = $l + 2; $i < $this->n; ++$i) {
                     $this->d[$i] -= $h;
                 }
                 $f += $h;
                 // Implicit QL transformation.
                 $p = $this->d[$m];
                 $c = 1.0;
                 $c2 = $c3 = $c;
                 $el1 = $this->e[$l + 1];
                 $s = $s2 = 0.0;
                 for ($i = $m - 1; $i >= $l; --$i) {
                     $c3 = $c2;
                     $c2 = $c;
                     $s2 = $s;
                     $g = $c * $this->e[$i];
                     $h = $c * $p;
                     $r = Matrix::hypo($p, $this->e[$i]);
                     $this->e[$i + 1] = $s * $r;
                     $s = $this->e[$i] / $r;
                     $c = $p / $r;
                     $p = $c * $this->d[$i] - $s * $g;
                     $this->d[$i + 1] = $h + $s * ($c * $g + $s * $this->d[$i]);
                     // Accumulate transformation.
                     for ($k = 0; $k < $this->n; ++$k) {
                         $h = $this->V[$k][$i + 1];
                         $this->V[$k][$i + 1] = $s * $this->V[$k][$i] + $c * $h;
                         $this->V[$k][$i] = $c * $this->V[$k][$i] - $s * $h;
                     }
                 }
                 $p = -$s * $s2 * $c3 * $el1 * $this->e[$l] / $dl1;
                 $this->e[$l] = $s * $p;
                 $this->d[$l] = $c * $p;
                 // Check for convergence.
             } while (abs($this->e[$l]) > $eps * $tst1);
         }
         $this->d[$l] = $this->d[$l] + $f;
         $this->e[$l] = 0.0;
     }
     // Sort eigenvalues and corresponding vectors.
     for ($i = 0; $i < $this->n - 1; ++$i) {
         $k = $i;
         $p = $this->d[$i];
         for ($j = $i + 1; $j < $this->n; ++$j) {
             if ($this->d[$j] < $p) {
                 $k = $j;
                 $p = $this->d[$j];
             }
         }
         if ($k != $i) {
             $this->d[$k] = $this->d[$i];
             $this->d[$i] = $p;
             for ($j = 0; $j < $this->n; ++$j) {
                 $p = $this->V[$j][$i];
                 $this->V[$j][$i] = $this->V[$j][$k];
                 $this->V[$j][$k] = $p;
             }
         }
     }
 }
 /**
  * @param $Matrix Matrix
  */
 public function __construct($Matrix)
 {
     $this->m = $Matrix->getRowDimension();
     $this->n = $Matrix->getColumnDimension();
     $this->U = $Matrix->matrix;
     $this->V = $Matrix->getMatrix($this->n)->matrix;
     $eps = 2.22045E-16;
     // Decompose Phase
     // Householder reduction to bi-diagonal form.
     $g = $scale = $aNorm = 0.0;
     $l = 0;
     $rv1 = [];
     for ($i = 0; $i < $this->n; $i++) {
         $l = $i + 2;
         $rv1[$i] = $scale * $g;
         $g = $s = $scale = 0.0;
         if ($i < $this->m) {
             for ($k = $i; $k < $this->m; $k++) {
                 $scale += abs($this->U[$k][$i]);
             }
             if ($scale != 0.0) {
                 for ($k = $i; $k < $this->m; $k++) {
                     $this->U[$k][$i] /= $scale;
                     $s += $this->U[$k][$i] * $this->U[$k][$i];
                 }
                 $f = $this->U[$i][$i];
                 $g = -Matrix::sameSign(sqrt($s), $f);
                 $h = $f * $g - $s;
                 $this->U[$i][$i] = $f - $g;
                 for ($j = $l - 1; $j < $this->n; $j++) {
                     for ($s = 0.0, $k = $i; $k < $this->m; $k++) {
                         $s += $this->U[$k][$i] * $this->U[$k][$j];
                     }
                     $f = $s / $h;
                     for ($k = $i; $k < $this->m; $k++) {
                         $this->U[$k][$j] += $f * $this->U[$k][$i];
                     }
                 }
                 for ($k = $i; $k < $this->m; $k++) {
                     $this->U[$k][$i] *= $scale;
                 }
             }
         }
         $this->W[$i] = $scale * $g;
         $g = $s = $scale = 0.0;
         if ($i + 1 <= $this->m && $i + 1 != $this->n) {
             for ($k = $l - 1; $k < $this->n; $k++) {
                 $scale += abs($this->U[$i][$k]);
             }
             if ($scale != 0.0) {
                 for ($k = $l - 1; $k < $this->n; $k++) {
                     $this->U[$i][$k] /= $scale;
                     $s += $this->U[$i][$k] * $this->U[$i][$k];
                 }
                 $f = $this->U[$i][$l - 1];
                 $g = -Matrix::sameSign(sqrt($s), $f);
                 $h = $f * $g - $s;
                 $this->U[$i][$l - 1] = $f - $g;
                 for ($k = $l - 1; $k < $this->n; $k++) {
                     $rv1[$k] = $this->U[$i][$k] / $h;
                 }
                 for ($j = $l - 1; $j < $this->m; $j++) {
                     for ($s = 0.0, $k = $l - 1; $k < $this->n; $k++) {
                         $s += $this->U[$j][$k] * $this->U[$i][$k];
                     }
                     for ($k = $l - 1; $k < $this->n; $k++) {
                         $this->U[$j][$k] += $s * $rv1[$k];
                     }
                 }
                 for ($k = $l - 1; $k < $this->n; $k++) {
                     $this->U[$i][$k] *= $scale;
                 }
             }
         }
         $aNorm = max($aNorm, abs($this->W[$i]) + abs($rv1[$i]));
     }
     // Accumulation of right-hand transformations.
     for ($i = $this->n - 1; $i >= 0; $i--) {
         if ($i < $this->n - 1) {
             if ($g != 0.0) {
                 for ($j = $l; $j < $this->n; $j++) {
                     // Double division to avoid possible underflow.
                     $this->V[$j][$i] = $this->U[$i][$j] / $this->U[$i][$l] / $g;
                 }
                 for ($j = $l; $j < $this->n; $j++) {
                     for ($s = 0.0, $k = $l; $k < $this->n; $k++) {
                         $s += $this->U[$i][$k] * $this->V[$k][$j];
                     }
                     for ($k = $l; $k < $this->n; $k++) {
                         $this->V[$k][$j] += $s * $this->V[$k][$i];
                     }
                 }
             }
             for ($j = $l; $j < $this->n; $j++) {
                 $this->V[$i][$j] = $this->V[$j][$i] = 0.0;
             }
         }
         $this->V[$i][$i] = 1.0;
         $g = $rv1[$i];
         $l = $i;
     }
     // Accumulation of left-hand transformations.
     for ($i = min($this->m, $this->n) - 1; $i >= 0; $i--) {
         $l = $i + 1;
         $g = $this->W[$i];
         for ($j = $l; $j < $this->n; $j++) {
             $this->U[$i][$j] = 0.0;
         }
         if ($g != 0.0) {
             $g = 1.0 / $g;
             for ($j = $l; $j < $this->n; $j++) {
                 for ($s = 0.0, $k = $l; $k < $this->m; $k++) {
                     $s += $this->U[$k][$i] * $this->U[$k][$j];
                 }
                 $f = $s / $this->U[$i][$i] * $g;
                 for ($k = $i; $k < $this->m; $k++) {
                     $this->U[$k][$j] += $f * $this->U[$k][$i];
                 }
             }
             for ($j = $i; $j < $this->m; $j++) {
                 $this->U[$j][$i] *= $g;
             }
         } else {
             for ($j = $i; $j < $this->m; $j++) {
                 $this->U[$j][$i] = 0.0;
             }
         }
         ++$this->U[$i][$i];
     }
     // Diagonalization of the bi-diagonal form
     // Loop over singular values, and over allowed iterations.
     $nm = 0;
     for ($k = $this->n - 1; $k >= 0; $k--) {
         for ($its = 0; $its < 30; $its++) {
             $flag = true;
             for ($l = $k; $l >= 0; $l--) {
                 $nm = $l - 1;
                 if ($l == 0 || abs($rv1[$l]) <= $eps * $aNorm) {
                     $flag = false;
                     break;
                 }
                 if (abs($this->W[$nm]) <= $eps * $aNorm) {
                     break;
                 }
             }
             if ($flag) {
                 $c = 0.0;
                 // Cancellation of rv1[l], if l > 0.
                 $s = 1.0;
                 for ($i = $l; $i < $k + 1; $i++) {
                     $f = $s * $rv1[$i];
                     $rv1[$i] = $c * $rv1[$i];
                     if (abs($f) <= $eps * $aNorm) {
                         break;
                     }
                     $g = $this->W[$i];
                     $h = Matrix::hypo($f, $g);
                     $this->W[$i] = $h;
                     $h = 1.0 / $h;
                     $c = $g * $h;
                     $s = -$f * $h;
                     for ($j = 0; $j < $this->m; $j++) {
                         $y = $this->U[$j][$nm];
                         $z = $this->U[$j][$i];
                         $this->U[$j][$nm] = $y * $c + $z * $s;
                         $this->U[$j][$i] = $z * $c - $y * $s;
                     }
                 }
             }
             $z = $this->W[$k];
             if ($l == $k) {
                 if ($z < 0.0) {
                     $this->W[$k] = -$z;
                     // Singular value is made nonnegative.
                     for ($j = 0; $j < $this->n; $j++) {
                         $this->V[$j][$k] = -$this->V[$j][$k];
                     }
                 }
                 break;
             }
             if ($its == 29) {
                 print "no convergence in 30 svd iterations";
             }
             $x = $this->W[$l];
             // Shift from bottom 2-by-2 minor.
             $nm = $k - 1;
             $y = $this->W[$nm];
             $g = $rv1[$nm];
             $h = $rv1[$k];
             $f = (($y - $z) * ($y + $z) + ($g - $h) * ($g + $h)) / (2.0 * $h * $y);
             $g = Matrix::hypo($f, 1.0);
             $f = (($x - $z) * ($x + $z) + $h * ($y / ($f + Matrix::sameSign($g, $f)) - $h)) / $x;
             $c = $s = 1.0;
             for ($j = $l; $j <= $nm; $j++) {
                 $i = $j + 1;
                 $g = $rv1[$i];
                 $y = $this->W[$i];
                 $h = $s * $g;
                 $g = $c * $g;
                 $z = Matrix::hypo($f, $h);
                 $rv1[$j] = $z;
                 $c = $f / $z;
                 $s = $h / $z;
                 $f = $x * $c + $g * $s;
                 $g = $g * $c - $x * $s;
                 $h = $y * $s;
                 $y *= $c;
                 for ($jj = 0; $jj < $this->n; $jj++) {
                     $x = $this->V[$jj][$j];
                     $z = $this->V[$jj][$i];
                     $this->V[$jj][$j] = $x * $c + $z * $s;
                     $this->V[$jj][$i] = $z * $c - $x * $s;
                 }
                 $z = Matrix::hypo($f, $h);
                 $this->W[$j] = $z;
                 // Rotation can be arbitrary if z = 0.
                 if ($z) {
                     $z = 1.0 / $z;
                     $c = $f * $z;
                     $s = $h * $z;
                 }
                 $f = $c * $g + $s * $y;
                 $x = $c * $y - $s * $g;
                 for ($jj = 0; $jj < $this->m; $jj++) {
                     $y = $this->U[$jj][$j];
                     $z = $this->U[$jj][$i];
                     $this->U[$jj][$j] = $y * $c + $z * $s;
                     $this->U[$jj][$i] = $z * $c - $y * $s;
                 }
             }
             $rv1[$l] = 0.0;
             $rv1[$k] = $f;
             $this->W[$k] = $x;
         }
     }
     // Reorder Phase
     // Sort. The method is Shell's sort.
     // (The work is negligible as compared to that already done in decompose phase.)
     $inc = 1;
     do {
         $inc *= 3;
         $inc++;
     } while ($inc <= $this->n);
     $su = [];
     $sv = [];
     do {
         $inc /= 3;
         for ($i = $inc; $i < $this->n; $i++) {
             $sw = $this->W[$i];
             for ($k = 0; $k < $this->m; $k++) {
                 $su[$k] = $this->U[$k][$i];
             }
             for ($k = 0; $k < $this->n; $k++) {
                 $sv[$k] = $this->V[$k][$i];
             }
             $j = $i;
             while ($this->W[$j - $inc] < $sw) {
                 $this->W[$j] = $this->W[$j - $inc];
                 for ($k = 0; $k < $this->m; $k++) {
                     $this->U[$k][$j] = $this->U[$k][$j - $inc];
                 }
                 for ($k = 0; $k < $this->n; $k++) {
                     $this->V[$k][$j] = $this->V[$k][$j - $inc];
                 }
                 $j -= $inc;
                 if ($j < $inc) {
                     break;
                 }
             }
             $this->W[$j] = $sw;
             for ($k = 0; $k < $this->m; $k++) {
                 $this->U[$k][$j] = $su[$k];
             }
             for ($k = 0; $k < $this->n; $k++) {
                 $this->V[$k][$j] = $sv[$k];
             }
         }
     } while ($inc > 1);
     for ($k = 0; $k < $this->n; $k++) {
         $s = 0;
         for ($i = 0; $i < $this->m; $i++) {
             if ($this->U[$i][$k] < 0.0) {
                 $s++;
             }
         }
         for ($j = 0; $j < $this->n; $j++) {
             if ($this->V[$j][$k] < 0.0) {
                 $s++;
             }
         }
         if ($s > ($this->m + $this->n) / 2) {
             for ($i = 0; $i < $this->m; $i++) {
                 $this->U[$i][$k] = -$this->U[$i][$k];
             }
             for ($j = 0; $j < $this->n; $j++) {
                 $this->V[$j][$k] = -$this->V[$j][$k];
             }
         }
     }
     // calculate the rank
     $rank = 0;
     for ($i = 0; $i < count($this->W); $i++) {
         if (round($this->W[$i], 4) > 0) {
             $rank += 1;
         }
     }
     // Low-Rank Approximation
     $q = 0.9;
     $k = 0;
     $fRobA = 0;
     $fRobAk = 0;
     for ($i = 0; $i < $rank; $i++) {
         $fRobA += $this->W[$i];
     }
     do {
         for ($i = 0; $i <= $k; $i++) {
             $fRobAk += $this->W[$i];
         }
         $clt = $fRobAk / $fRobA;
         $k++;
     } while ($clt < $q);
     // prepare S matrix as n*n daigonal matrix of singular values
     for ($i = 0; $i < $this->n; $i++) {
         //                for ($j = 0; $j < $n; $j++) {
         //                    $this->s[$i][$j] = 0;
         $this->s[$i] = $this->W[$i];
         //                }
     }
     $this->K = $k;
     $this->Rank = $rank;
 }