composer package updates
[openemr.git] / vendor / phpoffice / phpspreadsheet / src / PhpSpreadsheet / Shared / JAMA / Matrix.php
blob8ab9cfb359e286040e7ba0abed4552f999fa4389
1 <?php
3 namespace PhpOffice\PhpSpreadsheet\Shared\JAMA;
5 use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
6 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
7 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
9 /**
10 * Matrix class.
12 * @author Paul Meagher
13 * @author Michael Bommarito
14 * @author Lukasz Karapuda
15 * @author Bartek Matosiuk
17 * @version 1.8
19 * @see http://math.nist.gov/javanumerics/jama/
21 class Matrix
23 const POLYMORPHIC_ARGUMENT_EXCEPTION = 'Invalid argument pattern for polymorphic function.';
24 const ARGUMENT_TYPE_EXCEPTION = 'Invalid argument type.';
25 const ARGUMENT_BOUNDS_EXCEPTION = 'Invalid argument range.';
26 const MATRIX_DIMENSION_EXCEPTION = 'Matrix dimensions are not equal.';
27 const ARRAY_LENGTH_EXCEPTION = 'Array length must be a multiple of m.';
28 const MATRIX_SPD_EXCEPTION = 'Can only perform operation on symmetric positive definite matrix.';
30 /**
31 * Matrix storage.
33 * @var array
35 public $A = [];
37 /**
38 * Matrix row dimension.
40 * @var int
42 private $m;
44 /**
45 * Matrix column dimension.
47 * @var int
49 private $n;
51 /**
52 * Polymorphic constructor.
54 * As PHP has no support for polymorphic constructors, we use tricks to make our own sort of polymorphism using func_num_args, func_get_arg, and gettype. In essence, we're just implementing a simple RTTI filter and calling the appropriate constructor.
56 public function __construct(...$args)
58 if (count($args) > 0) {
59 $match = implode(',', array_map('gettype', $args));
61 switch ($match) {
62 //Rectangular matrix - m x n initialized from 2D array
63 case 'array':
64 $this->m = count($args[0]);
65 $this->n = count($args[0][0]);
66 $this->A = $args[0];
68 break;
69 //Square matrix - n x n
70 case 'integer':
71 $this->m = $args[0];
72 $this->n = $args[0];
73 $this->A = array_fill(0, $this->m, array_fill(0, $this->n, 0));
75 break;
76 //Rectangular matrix - m x n
77 case 'integer,integer':
78 $this->m = $args[0];
79 $this->n = $args[1];
80 $this->A = array_fill(0, $this->m, array_fill(0, $this->n, 0));
82 break;
83 //Rectangular matrix - m x n initialized from packed array
84 case 'array,integer':
85 $this->m = $args[1];
86 if ($this->m != 0) {
87 $this->n = count($args[0]) / $this->m;
88 } else {
89 $this->n = 0;
91 if (($this->m * $this->n) == count($args[0])) {
92 for ($i = 0; $i < $this->m; ++$i) {
93 for ($j = 0; $j < $this->n; ++$j) {
94 $this->A[$i][$j] = $args[0][$i + $j * $this->m];
97 } else {
98 throw new CalculationException(self::ARRAY_LENGTH_EXCEPTION);
101 break;
102 default:
103 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
105 break;
107 } else {
108 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
113 * getArray.
115 * @return array Matrix array
117 public function getArray()
119 return $this->A;
123 * getRowDimension.
125 * @return int Row dimension
127 public function getRowDimension()
129 return $this->m;
133 * getColumnDimension.
135 * @return int Column dimension
137 public function getColumnDimension()
139 return $this->n;
143 * get.
145 * Get the i,j-th element of the matrix.
147 * @param int $i Row position
148 * @param int $j Column position
150 * @return mixed Element (int/float/double)
152 public function get($i = null, $j = null)
154 return $this->A[$i][$j];
158 * getMatrix.
160 * Get a submatrix
162 * @param int $i0 Initial row index
163 * @param int $iF Final row index
164 * @param int $j0 Initial column index
165 * @param int $jF Final column index
167 * @return Matrix Submatrix
169 public function getMatrix(...$args)
171 if (count($args) > 0) {
172 $match = implode(',', array_map('gettype', $args));
174 switch ($match) {
175 //A($i0...; $j0...)
176 case 'integer,integer':
177 list($i0, $j0) = $args;
178 if ($i0 >= 0) {
179 $m = $this->m - $i0;
180 } else {
181 throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
183 if ($j0 >= 0) {
184 $n = $this->n - $j0;
185 } else {
186 throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
188 $R = new self($m, $n);
189 for ($i = $i0; $i < $this->m; ++$i) {
190 for ($j = $j0; $j < $this->n; ++$j) {
191 $R->set($i, $j, $this->A[$i][$j]);
195 return $R;
197 break;
198 //A($i0...$iF; $j0...$jF)
199 case 'integer,integer,integer,integer':
200 list($i0, $iF, $j0, $jF) = $args;
201 if (($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0)) {
202 $m = $iF - $i0;
203 } else {
204 throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
206 if (($jF > $j0) && ($this->n >= $jF) && ($j0 >= 0)) {
207 $n = $jF - $j0;
208 } else {
209 throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
211 $R = new self($m + 1, $n + 1);
212 for ($i = $i0; $i <= $iF; ++$i) {
213 for ($j = $j0; $j <= $jF; ++$j) {
214 $R->set($i - $i0, $j - $j0, $this->A[$i][$j]);
218 return $R;
220 break;
221 //$R = array of row indices; $C = array of column indices
222 case 'array,array':
223 list($RL, $CL) = $args;
224 if (count($RL) > 0) {
225 $m = count($RL);
226 } else {
227 throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
229 if (count($CL) > 0) {
230 $n = count($CL);
231 } else {
232 throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
234 $R = new self($m, $n);
235 for ($i = 0; $i < $m; ++$i) {
236 for ($j = 0; $j < $n; ++$j) {
237 $R->set($i - $i0, $j - $j0, $this->A[$RL[$i]][$CL[$j]]);
241 return $R;
243 break;
244 //A($i0...$iF); $CL = array of column indices
245 case 'integer,integer,array':
246 list($i0, $iF, $CL) = $args;
247 if (($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0)) {
248 $m = $iF - $i0;
249 } else {
250 throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
252 if (count($CL) > 0) {
253 $n = count($CL);
254 } else {
255 throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
257 $R = new self($m, $n);
258 for ($i = $i0; $i < $iF; ++$i) {
259 for ($j = 0; $j < $n; ++$j) {
260 $R->set($i - $i0, $j, $this->A[$RL[$i]][$j]);
264 return $R;
266 break;
267 //$RL = array of row indices
268 case 'array,integer,integer':
269 list($RL, $j0, $jF) = $args;
270 if (count($RL) > 0) {
271 $m = count($RL);
272 } else {
273 throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
275 if (($jF >= $j0) && ($this->n >= $jF) && ($j0 >= 0)) {
276 $n = $jF - $j0;
277 } else {
278 throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
280 $R = new self($m, $n + 1);
281 for ($i = 0; $i < $m; ++$i) {
282 for ($j = $j0; $j <= $jF; ++$j) {
283 $R->set($i, $j - $j0, $this->A[$RL[$i]][$j]);
287 return $R;
289 break;
290 default:
291 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
293 break;
295 } else {
296 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
301 * checkMatrixDimensions.
303 * Is matrix B the same size?
305 * @param Matrix $B Matrix B
307 * @return bool
309 public function checkMatrixDimensions($B = null)
311 if ($B instanceof self) {
312 if (($this->m == $B->getRowDimension()) && ($this->n == $B->getColumnDimension())) {
313 return true;
316 throw new CalculationException(self::MATRIX_DIMENSION_EXCEPTION);
319 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
322 // function checkMatrixDimensions()
325 * set.
327 * Set the i,j-th element of the matrix.
329 * @param int $i Row position
330 * @param int $j Column position
331 * @param mixed $c Int/float/double value
333 * @return mixed Element (int/float/double)
335 public function set($i = null, $j = null, $c = null)
337 // Optimized set version just has this
338 $this->A[$i][$j] = $c;
341 // function set()
344 * identity.
346 * Generate an identity matrix.
348 * @param int $m Row dimension
349 * @param int $n Column dimension
351 * @return Matrix Identity matrix
353 public function identity($m = null, $n = null)
355 return $this->diagonal($m, $n, 1);
359 * diagonal.
361 * Generate a diagonal matrix
363 * @param int $m Row dimension
364 * @param int $n Column dimension
365 * @param mixed $c Diagonal value
367 * @return Matrix Diagonal matrix
369 public function diagonal($m = null, $n = null, $c = 1)
371 $R = new self($m, $n);
372 for ($i = 0; $i < $m; ++$i) {
373 $R->set($i, $i, $c);
376 return $R;
380 * getMatrixByRow.
382 * Get a submatrix by row index/range
384 * @param int $i0 Initial row index
385 * @param int $iF Final row index
387 * @return Matrix Submatrix
389 public function getMatrixByRow($i0 = null, $iF = null)
391 if (is_int($i0)) {
392 if (is_int($iF)) {
393 return $this->getMatrix($i0, 0, $iF + 1, $this->n);
396 return $this->getMatrix($i0, 0, $i0 + 1, $this->n);
399 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
403 * getMatrixByCol.
405 * Get a submatrix by column index/range
407 * @param int $j0 Initial column index
408 * @param int $jF Final column index
410 * @return Matrix Submatrix
412 public function getMatrixByCol($j0 = null, $jF = null)
414 if (is_int($j0)) {
415 if (is_int($jF)) {
416 return $this->getMatrix(0, $j0, $this->m, $jF + 1);
419 return $this->getMatrix(0, $j0, $this->m, $j0 + 1);
422 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
426 * transpose.
428 * Tranpose matrix
430 * @return Matrix Transposed matrix
432 public function transpose()
434 $R = new self($this->n, $this->m);
435 for ($i = 0; $i < $this->m; ++$i) {
436 for ($j = 0; $j < $this->n; ++$j) {
437 $R->set($j, $i, $this->A[$i][$j]);
441 return $R;
444 // function transpose()
447 * trace.
449 * Sum of diagonal elements
451 * @return float Sum of diagonal elements
453 public function trace()
455 $s = 0;
456 $n = min($this->m, $this->n);
457 for ($i = 0; $i < $n; ++$i) {
458 $s += $this->A[$i][$i];
461 return $s;
465 * uminus.
467 * Unary minus matrix -A
469 * @return Matrix Unary minus matrix
471 public function uminus()
476 * plus.
478 * A + B
480 * @param mixed $B Matrix/Array
482 * @return Matrix Sum
484 public function plus(...$args)
486 if (count($args) > 0) {
487 $match = implode(',', array_map('gettype', $args));
489 switch ($match) {
490 case 'object':
491 if ($args[0] instanceof self) {
492 $M = $args[0];
493 } else {
494 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
497 break;
498 case 'array':
499 $M = new self($args[0]);
501 break;
502 default:
503 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
505 break;
507 $this->checkMatrixDimensions($M);
508 for ($i = 0; $i < $this->m; ++$i) {
509 for ($j = 0; $j < $this->n; ++$j) {
510 $M->set($i, $j, $M->get($i, $j) + $this->A[$i][$j]);
514 return $M;
517 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
521 * plusEquals.
523 * A = A + B
525 * @param mixed $B Matrix/Array
527 * @return Matrix Sum
529 public function plusEquals(...$args)
531 if (count($args) > 0) {
532 $match = implode(',', array_map('gettype', $args));
534 switch ($match) {
535 case 'object':
536 if ($args[0] instanceof self) {
537 $M = $args[0];
538 } else {
539 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
542 break;
543 case 'array':
544 $M = new self($args[0]);
546 break;
547 default:
548 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
550 break;
552 $this->checkMatrixDimensions($M);
553 for ($i = 0; $i < $this->m; ++$i) {
554 for ($j = 0; $j < $this->n; ++$j) {
555 $validValues = true;
556 $value = $M->get($i, $j);
557 if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
558 $this->A[$i][$j] = trim($this->A[$i][$j], '"');
559 $validValues &= StringHelper::convertToNumberIfFraction($this->A[$i][$j]);
561 if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
562 $value = trim($value, '"');
563 $validValues &= StringHelper::convertToNumberIfFraction($value);
565 if ($validValues) {
566 $this->A[$i][$j] += $value;
567 } else {
568 $this->A[$i][$j] = Functions::NAN();
573 return $this;
576 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
580 * minus.
582 * A - B
584 * @param mixed $B Matrix/Array
586 * @return Matrix Sum
588 public function minus(...$args)
590 if (count($args) > 0) {
591 $match = implode(',', array_map('gettype', $args));
593 switch ($match) {
594 case 'object':
595 if ($args[0] instanceof self) {
596 $M = $args[0];
597 } else {
598 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
601 break;
602 case 'array':
603 $M = new self($args[0]);
605 break;
606 default:
607 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
609 break;
611 $this->checkMatrixDimensions($M);
612 for ($i = 0; $i < $this->m; ++$i) {
613 for ($j = 0; $j < $this->n; ++$j) {
614 $M->set($i, $j, $M->get($i, $j) - $this->A[$i][$j]);
618 return $M;
621 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
625 * minusEquals.
627 * A = A - B
629 * @param mixed $B Matrix/Array
631 * @return Matrix Sum
633 public function minusEquals(...$args)
635 if (count($args) > 0) {
636 $match = implode(',', array_map('gettype', $args));
638 switch ($match) {
639 case 'object':
640 if ($args[0] instanceof self) {
641 $M = $args[0];
642 } else {
643 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
646 break;
647 case 'array':
648 $M = new self($args[0]);
650 break;
651 default:
652 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
654 break;
656 $this->checkMatrixDimensions($M);
657 for ($i = 0; $i < $this->m; ++$i) {
658 for ($j = 0; $j < $this->n; ++$j) {
659 $validValues = true;
660 $value = $M->get($i, $j);
661 if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
662 $this->A[$i][$j] = trim($this->A[$i][$j], '"');
663 $validValues &= StringHelper::convertToNumberIfFraction($this->A[$i][$j]);
665 if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
666 $value = trim($value, '"');
667 $validValues &= StringHelper::convertToNumberIfFraction($value);
669 if ($validValues) {
670 $this->A[$i][$j] -= $value;
671 } else {
672 $this->A[$i][$j] = Functions::NAN();
677 return $this;
680 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
684 * arrayTimes.
686 * Element-by-element multiplication
687 * Cij = Aij * Bij
689 * @param mixed $B Matrix/Array
691 * @return Matrix Matrix Cij
693 public function arrayTimes(...$args)
695 if (count($args) > 0) {
696 $match = implode(',', array_map('gettype', $args));
698 switch ($match) {
699 case 'object':
700 if ($args[0] instanceof self) {
701 $M = $args[0];
702 } else {
703 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
706 break;
707 case 'array':
708 $M = new self($args[0]);
710 break;
711 default:
712 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
714 break;
716 $this->checkMatrixDimensions($M);
717 for ($i = 0; $i < $this->m; ++$i) {
718 for ($j = 0; $j < $this->n; ++$j) {
719 $M->set($i, $j, $M->get($i, $j) * $this->A[$i][$j]);
723 return $M;
726 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
730 * arrayTimesEquals.
732 * Element-by-element multiplication
733 * Aij = Aij * Bij
735 * @param mixed $B Matrix/Array
737 * @return Matrix Matrix Aij
739 public function arrayTimesEquals(...$args)
741 if (count($args) > 0) {
742 $match = implode(',', array_map('gettype', $args));
744 switch ($match) {
745 case 'object':
746 if ($args[0] instanceof self) {
747 $M = $args[0];
748 } else {
749 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
752 break;
753 case 'array':
754 $M = new self($args[0]);
756 break;
757 default:
758 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
760 break;
762 $this->checkMatrixDimensions($M);
763 for ($i = 0; $i < $this->m; ++$i) {
764 for ($j = 0; $j < $this->n; ++$j) {
765 $validValues = true;
766 $value = $M->get($i, $j);
767 if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
768 $this->A[$i][$j] = trim($this->A[$i][$j], '"');
769 $validValues &= StringHelper::convertToNumberIfFraction($this->A[$i][$j]);
771 if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
772 $value = trim($value, '"');
773 $validValues &= StringHelper::convertToNumberIfFraction($value);
775 if ($validValues) {
776 $this->A[$i][$j] *= $value;
777 } else {
778 $this->A[$i][$j] = Functions::NAN();
783 return $this;
786 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
790 * arrayRightDivide.
792 * Element-by-element right division
793 * A / B
795 * @param Matrix $B Matrix B
797 * @return Matrix Division result
799 public function arrayRightDivide(...$args)
801 if (count($args) > 0) {
802 $match = implode(',', array_map('gettype', $args));
804 switch ($match) {
805 case 'object':
806 if ($args[0] instanceof self) {
807 $M = $args[0];
808 } else {
809 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
812 break;
813 case 'array':
814 $M = new self($args[0]);
816 break;
817 default:
818 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
820 break;
822 $this->checkMatrixDimensions($M);
823 for ($i = 0; $i < $this->m; ++$i) {
824 for ($j = 0; $j < $this->n; ++$j) {
825 $validValues = true;
826 $value = $M->get($i, $j);
827 if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
828 $this->A[$i][$j] = trim($this->A[$i][$j], '"');
829 $validValues &= StringHelper::convertToNumberIfFraction($this->A[$i][$j]);
831 if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
832 $value = trim($value, '"');
833 $validValues &= StringHelper::convertToNumberIfFraction($value);
835 if ($validValues) {
836 if ($value == 0) {
837 // Trap for Divide by Zero error
838 $M->set($i, $j, '#DIV/0!');
839 } else {
840 $M->set($i, $j, $this->A[$i][$j] / $value);
842 } else {
843 $M->set($i, $j, Functions::NAN());
848 return $M;
851 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
855 * arrayRightDivideEquals.
857 * Element-by-element right division
858 * Aij = Aij / Bij
860 * @param mixed $B Matrix/Array
862 * @return Matrix Matrix Aij
864 public function arrayRightDivideEquals(...$args)
866 if (count($args) > 0) {
867 $match = implode(',', array_map('gettype', $args));
869 switch ($match) {
870 case 'object':
871 if ($args[0] instanceof self) {
872 $M = $args[0];
873 } else {
874 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
877 break;
878 case 'array':
879 $M = new self($args[0]);
881 break;
882 default:
883 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
885 break;
887 $this->checkMatrixDimensions($M);
888 for ($i = 0; $i < $this->m; ++$i) {
889 for ($j = 0; $j < $this->n; ++$j) {
890 $this->A[$i][$j] = $this->A[$i][$j] / $M->get($i, $j);
894 return $M;
897 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
901 * arrayLeftDivide.
903 * Element-by-element Left division
904 * A / B
906 * @param Matrix $B Matrix B
908 * @return Matrix Division result
910 public function arrayLeftDivide(...$args)
912 if (count($args) > 0) {
913 $match = implode(',', array_map('gettype', $args));
915 switch ($match) {
916 case 'object':
917 if ($args[0] instanceof self) {
918 $M = $args[0];
919 } else {
920 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
923 break;
924 case 'array':
925 $M = new self($args[0]);
927 break;
928 default:
929 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
931 break;
933 $this->checkMatrixDimensions($M);
934 for ($i = 0; $i < $this->m; ++$i) {
935 for ($j = 0; $j < $this->n; ++$j) {
936 $M->set($i, $j, $M->get($i, $j) / $this->A[$i][$j]);
940 return $M;
943 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
947 * arrayLeftDivideEquals.
949 * Element-by-element Left division
950 * Aij = Aij / Bij
952 * @param mixed $B Matrix/Array
954 * @return Matrix Matrix Aij
956 public function arrayLeftDivideEquals(...$args)
958 if (count($args) > 0) {
959 $match = implode(',', array_map('gettype', $args));
961 switch ($match) {
962 case 'object':
963 if ($args[0] instanceof self) {
964 $M = $args[0];
965 } else {
966 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
969 break;
970 case 'array':
971 $M = new self($args[0]);
973 break;
974 default:
975 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
977 break;
979 $this->checkMatrixDimensions($M);
980 for ($i = 0; $i < $this->m; ++$i) {
981 for ($j = 0; $j < $this->n; ++$j) {
982 $this->A[$i][$j] = $M->get($i, $j) / $this->A[$i][$j];
986 return $M;
989 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
993 * times.
995 * Matrix multiplication
997 * @param mixed $n Matrix/Array/Scalar
999 * @return Matrix Product
1001 public function times(...$args)
1003 if (count($args) > 0) {
1004 $match = implode(',', array_map('gettype', $args));
1006 switch ($match) {
1007 case 'object':
1008 if ($args[0] instanceof self) {
1009 $B = $args[0];
1010 } else {
1011 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
1013 if ($this->n == $B->m) {
1014 $C = new self($this->m, $B->n);
1015 for ($j = 0; $j < $B->n; ++$j) {
1016 for ($k = 0; $k < $this->n; ++$k) {
1017 $Bcolj[$k] = $B->A[$k][$j];
1019 for ($i = 0; $i < $this->m; ++$i) {
1020 $Arowi = $this->A[$i];
1021 $s = 0;
1022 for ($k = 0; $k < $this->n; ++$k) {
1023 $s += $Arowi[$k] * $Bcolj[$k];
1025 $C->A[$i][$j] = $s;
1029 return $C;
1032 throw new CalculationException(self::MATRIX_DIMENSION_EXCEPTION);
1033 case 'array':
1034 $B = new self($args[0]);
1035 if ($this->n == $B->m) {
1036 $C = new self($this->m, $B->n);
1037 for ($i = 0; $i < $C->m; ++$i) {
1038 for ($j = 0; $j < $C->n; ++$j) {
1039 $s = '0';
1040 for ($k = 0; $k < $C->n; ++$k) {
1041 $s += $this->A[$i][$k] * $B->A[$k][$j];
1043 $C->A[$i][$j] = $s;
1047 return $C;
1050 throw new CalculationException(self::MATRIX_DIMENSION_EXCEPTION);
1051 case 'integer':
1052 $C = new self($this->A);
1053 for ($i = 0; $i < $C->m; ++$i) {
1054 for ($j = 0; $j < $C->n; ++$j) {
1055 $C->A[$i][$j] *= $args[0];
1059 return $C;
1060 case 'double':
1061 $C = new self($this->m, $this->n);
1062 for ($i = 0; $i < $C->m; ++$i) {
1063 for ($j = 0; $j < $C->n; ++$j) {
1064 $C->A[$i][$j] = $args[0] * $this->A[$i][$j];
1068 return $C;
1069 case 'float':
1070 $C = new self($this->A);
1071 for ($i = 0; $i < $C->m; ++$i) {
1072 for ($j = 0; $j < $C->n; ++$j) {
1073 $C->A[$i][$j] *= $args[0];
1077 return $C;
1078 default:
1079 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
1081 } else {
1082 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
1087 * power.
1089 * A = A ^ B
1091 * @param mixed $B Matrix/Array
1093 * @return Matrix Sum
1095 public function power(...$args)
1097 if (count($args) > 0) {
1098 $match = implode(',', array_map('gettype', $args));
1100 switch ($match) {
1101 case 'object':
1102 if ($args[0] instanceof self) {
1103 $M = $args[0];
1104 } else {
1105 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
1108 break;
1109 case 'array':
1110 $M = new self($args[0]);
1112 break;
1113 default:
1114 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
1116 break;
1118 $this->checkMatrixDimensions($M);
1119 for ($i = 0; $i < $this->m; ++$i) {
1120 for ($j = 0; $j < $this->n; ++$j) {
1121 $validValues = true;
1122 $value = $M->get($i, $j);
1123 if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
1124 $this->A[$i][$j] = trim($this->A[$i][$j], '"');
1125 $validValues &= StringHelper::convertToNumberIfFraction($this->A[$i][$j]);
1127 if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
1128 $value = trim($value, '"');
1129 $validValues &= StringHelper::convertToNumberIfFraction($value);
1131 if ($validValues) {
1132 $this->A[$i][$j] = pow($this->A[$i][$j], $value);
1133 } else {
1134 $this->A[$i][$j] = Functions::NAN();
1139 return $this;
1142 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
1146 * concat.
1148 * A = A & B
1150 * @param mixed $B Matrix/Array
1152 * @return Matrix Sum
1154 public function concat(...$args)
1156 if (count($args) > 0) {
1157 $match = implode(',', array_map('gettype', $args));
1159 switch ($match) {
1160 case 'object':
1161 if ($args[0] instanceof self) {
1162 $M = $args[0];
1163 } else {
1164 throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
1167 break;
1168 case 'array':
1169 $M = new self($args[0]);
1171 break;
1172 default:
1173 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
1175 break;
1177 $this->checkMatrixDimensions($M);
1178 for ($i = 0; $i < $this->m; ++$i) {
1179 for ($j = 0; $j < $this->n; ++$j) {
1180 $this->A[$i][$j] = trim($this->A[$i][$j], '"') . trim($M->get($i, $j), '"');
1184 return $this;
1187 throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
1191 * Solve A*X = B.
1193 * @param Matrix $B Right hand side
1195 * @return Matrix ... Solution if A is square, least squares solution otherwise
1197 public function solve($B)
1199 if ($this->m == $this->n) {
1200 $LU = new LUDecomposition($this);
1202 return $LU->solve($B);
1204 $QR = new QRDecomposition($this);
1206 return $QR->solve($B);
1210 * Matrix inverse or pseudoinverse.
1212 * @return Matrix ... Inverse(A) if A is square, pseudoinverse otherwise.
1214 public function inverse()
1216 return $this->solve($this->identity($this->m, $this->m));
1220 * det.
1222 * Calculate determinant
1224 * @return float Determinant
1226 public function det()
1228 $L = new LUDecomposition($this);
1230 return $L->det();