/**
* @param self $source
* @return self
*/
private function recursiveSolveInverse(self $source) : self
{
$size = $source->getRowCount();
if ($size === 1) {
return new static([[1 / $source->get(0, 0)]]);
}
$half = (int) ($size / 2);
// Partition source matrix.
$B = $source->sliceRows(0, $half)->sliceColumns(0, $half);
$CT = $source->sliceRows(0, $half)->sliceColumns($half);
$D = $source->sliceRows($half)->sliceColumns($half);
$C = $source->sliceRows($half)->sliceColumns(0, $half);
// Handle intermediate calculations.
$Binv = $this->recursiveSolveInverse($B);
$W = $C->multiplyMatrix($Binv);
$WT = $W->transpose();
$X = $W->multiplyMatrix($CT);
$S = $D->subtractMatrix($X);
$Sinv = $this->recursiveSolveInverse($S);
$V = $Sinv;
$Y = $Sinv->multiplyMatrix($W);
$YT = $Y->transpose();
$T = $YT->multiplyScalar(-1);
$U = $Y->multiplyScalar(-1);
$Z = $WT->multiplyMatrix($Y);
$R = $Binv->addMatrix($Z);
// Stitch together intermediate results into the final result
return $R->concatenateRight($T)->concatenateBottom($U->concatenateRight($V));
}