From 75160b12821f7f4299cce7f0b69c83c1502ae071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Mon, 27 May 2024 13:08:29 +0200 Subject: 2024-02-19 upstream --- .../markbaker/complex/.github/workflows/main.yml | 150 ++++++++ vendor/markbaker/complex/README.md | 156 ++++++++ vendor/markbaker/complex/classes/Autoloader.php | 53 +++ vendor/markbaker/complex/classes/Bootstrap.php | 38 ++ vendor/markbaker/complex/classes/src/Complex.php | 390 +++++++++++++++++++ vendor/markbaker/complex/classes/src/Exception.php | 13 + .../complex/classes/src/functions/abs.php | 31 ++ .../complex/classes/src/functions/acos.php | 40 ++ .../complex/classes/src/functions/acosh.php | 36 ++ .../complex/classes/src/functions/acot.php | 27 ++ .../complex/classes/src/functions/acoth.php | 27 ++ .../complex/classes/src/functions/acsc.php | 31 ++ .../complex/classes/src/functions/acsch.php | 31 ++ .../complex/classes/src/functions/argument.php | 30 ++ .../complex/classes/src/functions/asec.php | 31 ++ .../complex/classes/src/functions/asech.php | 31 ++ .../complex/classes/src/functions/asin.php | 39 ++ .../complex/classes/src/functions/asinh.php | 35 ++ .../complex/classes/src/functions/atan.php | 47 +++ .../complex/classes/src/functions/atanh.php | 40 ++ .../complex/classes/src/functions/conjugate.php | 30 ++ .../complex/classes/src/functions/cos.php | 36 ++ .../complex/classes/src/functions/cosh.php | 34 ++ .../complex/classes/src/functions/cot.php | 31 ++ .../complex/classes/src/functions/coth.php | 26 ++ .../complex/classes/src/functions/csc.php | 31 ++ .../complex/classes/src/functions/csch.php | 31 ++ .../complex/classes/src/functions/exp.php | 36 ++ .../complex/classes/src/functions/inverse.php | 31 ++ .../markbaker/complex/classes/src/functions/ln.php | 35 ++ .../complex/classes/src/functions/log10.php | 34 ++ .../complex/classes/src/functions/log2.php | 34 ++ .../complex/classes/src/functions/negative.php | 33 ++ .../complex/classes/src/functions/pow.php | 42 ++ .../complex/classes/src/functions/rho.php | 30 ++ .../complex/classes/src/functions/sec.php | 27 ++ .../complex/classes/src/functions/sech.php | 27 ++ .../complex/classes/src/functions/sin.php | 34 ++ .../complex/classes/src/functions/sinh.php | 34 ++ .../complex/classes/src/functions/sqrt.php | 31 ++ .../complex/classes/src/functions/tan.php | 42 ++ .../complex/classes/src/functions/tanh.php | 37 ++ .../complex/classes/src/functions/theta.php | 40 ++ .../complex/classes/src/operations/add.php | 48 +++ .../complex/classes/src/operations/divideby.php | 58 +++ .../complex/classes/src/operations/divideinto.php | 58 +++ .../complex/classes/src/operations/multiply.php | 50 +++ .../complex/classes/src/operations/subtract.php | 48 +++ vendor/markbaker/complex/composer.json | 77 ++++ vendor/markbaker/complex/examples/complexTest.php | 154 ++++++++ .../markbaker/complex/examples/testFunctions.php | 52 +++ .../markbaker/complex/examples/testOperations.php | 34 ++ vendor/markbaker/complex/license.md | 25 ++ .../markbaker/matrix/.github/workflows/main.yaml | 124 ++++++ vendor/markbaker/matrix/README.md | 207 ++++++++++ vendor/markbaker/matrix/buildPhar.php | 62 +++ vendor/markbaker/matrix/classes/src/Builder.php | 70 ++++ .../classes/src/Decomposition/Decomposition.php | 27 ++ .../matrix/classes/src/Decomposition/LU.php | 260 +++++++++++++ .../matrix/classes/src/Decomposition/QR.php | 191 ++++++++++ .../markbaker/matrix/classes/src/Div0Exception.php | 13 + vendor/markbaker/matrix/classes/src/Exception.php | 13 + vendor/markbaker/matrix/classes/src/Functions.php | 337 ++++++++++++++++ .../matrix/classes/src/Functions/adjoint.php | 32 ++ .../matrix/classes/src/Functions/antidiagonal.php | 31 ++ .../matrix/classes/src/Functions/cofactors.php | 32 ++ .../matrix/classes/src/Functions/determinant.php | 32 ++ .../matrix/classes/src/Functions/diagonal.php | 32 ++ .../matrix/classes/src/Functions/identity.php | 32 ++ .../matrix/classes/src/Functions/inverse.php | 32 ++ .../matrix/classes/src/Functions/minors.php | 32 ++ .../matrix/classes/src/Functions/trace.php | 32 ++ .../matrix/classes/src/Functions/transpose.php | 32 ++ vendor/markbaker/matrix/classes/src/Matrix.php | 423 +++++++++++++++++++++ .../matrix/classes/src/Operations/add.php | 46 +++ .../matrix/classes/src/Operations/directsum.php | 46 +++ .../matrix/classes/src/Operations/divideby.php | 46 +++ .../matrix/classes/src/Operations/divideinto.php | 47 +++ .../matrix/classes/src/Operations/multiply.php | 46 +++ .../matrix/classes/src/Operations/subtract.php | 46 +++ .../matrix/classes/src/Operators/Addition.php | 68 ++++ .../matrix/classes/src/Operators/DirectSum.php | 64 ++++ .../matrix/classes/src/Operators/Division.php | 35 ++ .../classes/src/Operators/Multiplication.php | 86 +++++ .../matrix/classes/src/Operators/Operator.php | 78 ++++ .../matrix/classes/src/Operators/Subtraction.php | 68 ++++ vendor/markbaker/matrix/composer.json | 89 +++++ vendor/markbaker/matrix/examples/test.php | 33 ++ vendor/markbaker/matrix/infection.json.dist | 17 + vendor/markbaker/matrix/license.md | 25 ++ vendor/markbaker/matrix/phpstan.neon | 6 + 91 files changed, 5538 insertions(+) create mode 100644 vendor/markbaker/complex/.github/workflows/main.yml create mode 100644 vendor/markbaker/complex/README.md create mode 100644 vendor/markbaker/complex/classes/Autoloader.php create mode 100644 vendor/markbaker/complex/classes/Bootstrap.php create mode 100644 vendor/markbaker/complex/classes/src/Complex.php create mode 100644 vendor/markbaker/complex/classes/src/Exception.php create mode 100644 vendor/markbaker/complex/classes/src/functions/abs.php create mode 100644 vendor/markbaker/complex/classes/src/functions/acos.php create mode 100644 vendor/markbaker/complex/classes/src/functions/acosh.php create mode 100644 vendor/markbaker/complex/classes/src/functions/acot.php create mode 100644 vendor/markbaker/complex/classes/src/functions/acoth.php create mode 100644 vendor/markbaker/complex/classes/src/functions/acsc.php create mode 100644 vendor/markbaker/complex/classes/src/functions/acsch.php create mode 100644 vendor/markbaker/complex/classes/src/functions/argument.php create mode 100644 vendor/markbaker/complex/classes/src/functions/asec.php create mode 100644 vendor/markbaker/complex/classes/src/functions/asech.php create mode 100644 vendor/markbaker/complex/classes/src/functions/asin.php create mode 100644 vendor/markbaker/complex/classes/src/functions/asinh.php create mode 100644 vendor/markbaker/complex/classes/src/functions/atan.php create mode 100644 vendor/markbaker/complex/classes/src/functions/atanh.php create mode 100644 vendor/markbaker/complex/classes/src/functions/conjugate.php create mode 100644 vendor/markbaker/complex/classes/src/functions/cos.php create mode 100644 vendor/markbaker/complex/classes/src/functions/cosh.php create mode 100644 vendor/markbaker/complex/classes/src/functions/cot.php create mode 100644 vendor/markbaker/complex/classes/src/functions/coth.php create mode 100644 vendor/markbaker/complex/classes/src/functions/csc.php create mode 100644 vendor/markbaker/complex/classes/src/functions/csch.php create mode 100644 vendor/markbaker/complex/classes/src/functions/exp.php create mode 100644 vendor/markbaker/complex/classes/src/functions/inverse.php create mode 100644 vendor/markbaker/complex/classes/src/functions/ln.php create mode 100644 vendor/markbaker/complex/classes/src/functions/log10.php create mode 100644 vendor/markbaker/complex/classes/src/functions/log2.php create mode 100644 vendor/markbaker/complex/classes/src/functions/negative.php create mode 100644 vendor/markbaker/complex/classes/src/functions/pow.php create mode 100644 vendor/markbaker/complex/classes/src/functions/rho.php create mode 100644 vendor/markbaker/complex/classes/src/functions/sec.php create mode 100644 vendor/markbaker/complex/classes/src/functions/sech.php create mode 100644 vendor/markbaker/complex/classes/src/functions/sin.php create mode 100644 vendor/markbaker/complex/classes/src/functions/sinh.php create mode 100644 vendor/markbaker/complex/classes/src/functions/sqrt.php create mode 100644 vendor/markbaker/complex/classes/src/functions/tan.php create mode 100644 vendor/markbaker/complex/classes/src/functions/tanh.php create mode 100644 vendor/markbaker/complex/classes/src/functions/theta.php create mode 100644 vendor/markbaker/complex/classes/src/operations/add.php create mode 100644 vendor/markbaker/complex/classes/src/operations/divideby.php create mode 100644 vendor/markbaker/complex/classes/src/operations/divideinto.php create mode 100644 vendor/markbaker/complex/classes/src/operations/multiply.php create mode 100644 vendor/markbaker/complex/classes/src/operations/subtract.php create mode 100644 vendor/markbaker/complex/composer.json create mode 100644 vendor/markbaker/complex/examples/complexTest.php create mode 100644 vendor/markbaker/complex/examples/testFunctions.php create mode 100644 vendor/markbaker/complex/examples/testOperations.php create mode 100644 vendor/markbaker/complex/license.md create mode 100644 vendor/markbaker/matrix/.github/workflows/main.yaml create mode 100644 vendor/markbaker/matrix/README.md create mode 100644 vendor/markbaker/matrix/buildPhar.php create mode 100644 vendor/markbaker/matrix/classes/src/Builder.php create mode 100644 vendor/markbaker/matrix/classes/src/Decomposition/Decomposition.php create mode 100644 vendor/markbaker/matrix/classes/src/Decomposition/LU.php create mode 100644 vendor/markbaker/matrix/classes/src/Decomposition/QR.php create mode 100644 vendor/markbaker/matrix/classes/src/Div0Exception.php create mode 100644 vendor/markbaker/matrix/classes/src/Exception.php create mode 100644 vendor/markbaker/matrix/classes/src/Functions.php create mode 100644 vendor/markbaker/matrix/classes/src/Functions/adjoint.php create mode 100644 vendor/markbaker/matrix/classes/src/Functions/antidiagonal.php create mode 100644 vendor/markbaker/matrix/classes/src/Functions/cofactors.php create mode 100644 vendor/markbaker/matrix/classes/src/Functions/determinant.php create mode 100644 vendor/markbaker/matrix/classes/src/Functions/diagonal.php create mode 100644 vendor/markbaker/matrix/classes/src/Functions/identity.php create mode 100644 vendor/markbaker/matrix/classes/src/Functions/inverse.php create mode 100644 vendor/markbaker/matrix/classes/src/Functions/minors.php create mode 100644 vendor/markbaker/matrix/classes/src/Functions/trace.php create mode 100644 vendor/markbaker/matrix/classes/src/Functions/transpose.php create mode 100644 vendor/markbaker/matrix/classes/src/Matrix.php create mode 100644 vendor/markbaker/matrix/classes/src/Operations/add.php create mode 100644 vendor/markbaker/matrix/classes/src/Operations/directsum.php create mode 100644 vendor/markbaker/matrix/classes/src/Operations/divideby.php create mode 100644 vendor/markbaker/matrix/classes/src/Operations/divideinto.php create mode 100644 vendor/markbaker/matrix/classes/src/Operations/multiply.php create mode 100644 vendor/markbaker/matrix/classes/src/Operations/subtract.php create mode 100644 vendor/markbaker/matrix/classes/src/Operators/Addition.php create mode 100644 vendor/markbaker/matrix/classes/src/Operators/DirectSum.php create mode 100644 vendor/markbaker/matrix/classes/src/Operators/Division.php create mode 100644 vendor/markbaker/matrix/classes/src/Operators/Multiplication.php create mode 100644 vendor/markbaker/matrix/classes/src/Operators/Operator.php create mode 100644 vendor/markbaker/matrix/classes/src/Operators/Subtraction.php create mode 100644 vendor/markbaker/matrix/composer.json create mode 100644 vendor/markbaker/matrix/examples/test.php create mode 100644 vendor/markbaker/matrix/infection.json.dist create mode 100644 vendor/markbaker/matrix/license.md create mode 100644 vendor/markbaker/matrix/phpstan.neon (limited to 'vendor/markbaker') diff --git a/vendor/markbaker/complex/.github/workflows/main.yml b/vendor/markbaker/complex/.github/workflows/main.yml new file mode 100644 index 0000000..3b41498 --- /dev/null +++ b/vendor/markbaker/complex/.github/workflows/main.yml @@ -0,0 +1,150 @@ +name: main +on: [ push, pull_request ] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + experimental: + - false + php-version: + - '7.2' + - '7.3' + - '7.4' + - '8.0' + + include: + - php-version: '8.1' + experimental: true + + name: PHP ${{ matrix.php-version }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Delete composer lock file + id: composer-lock + if: ${{ matrix.php-version == '8.1' }} + run: | + echo "::set-output name=flags::--ignore-platform-reqs" + + - name: Install dependencies + run: composer update --no-progress --prefer-dist --optimize-autoloader ${{ steps.composer-lock.outputs.flags }} + + - name: Setup problem matchers for PHP + run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" + + - name: Setup problem matchers for PHPUnit + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: "Run PHPUnit tests (Experimental: ${{ matrix.experimental }})" + env: + FAILURE_ACTION: "${{ matrix.experimental == true }}" + run: vendor/bin/phpunit --verbose || $FAILURE_ACTION + + phpcs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: none + tools: cs2pr + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Code style with PHP_CodeSniffer + run: ./vendor/bin/phpcs -q --report=checkstyle classes/src/ | cs2pr + + versions: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: none + tools: cs2pr + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Code Version Compatibility check with PHP_CodeSniffer + run: ./vendor/bin/phpcs -q --report-width=200 --report=summary,full classes/src/ --standard=PHPCompatibility --runtime-set testVersion 7.2- + + coverage: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: pcov + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Test Coverage + run: ./vendor/bin/phpunit --verbose --coverage-text diff --git a/vendor/markbaker/complex/README.md b/vendor/markbaker/complex/README.md new file mode 100644 index 0000000..8ded0df --- /dev/null +++ b/vendor/markbaker/complex/README.md @@ -0,0 +1,156 @@ +PHPComplex +========== + +--- + +PHP Class for handling Complex numbers + +Master: [![Build Status](https://travis-ci.org/MarkBaker/PHPComplex.png?branch=master)](http://travis-ci.org/MarkBaker/PHPComplex) + +Develop: [![Build Status](https://travis-ci.org/MarkBaker/PHPComplex.png?branch=develop)](http://travis-ci.org/MarkBaker/PHPComplex) + +[![Complex Numbers](https://imgs.xkcd.com/comics/complex_numbers_2x.png)](https://xkcd.com/2028/) + +--- + +The library currently provides the following operations: + + - addition + - subtraction + - multiplication + - division + - division by + - division into + +together with functions for + + - theta (polar theta angle) + - rho (polar distance/radius) + - conjugate + * negative + - inverse (1 / complex) + - cos (cosine) + - acos (inverse cosine) + - cosh (hyperbolic cosine) + - acosh (inverse hyperbolic cosine) + - sin (sine) + - asin (inverse sine) + - sinh (hyperbolic sine) + - asinh (inverse hyperbolic sine) + - sec (secant) + - asec (inverse secant) + - sech (hyperbolic secant) + - asech (inverse hyperbolic secant) + - csc (cosecant) + - acsc (inverse cosecant) + - csch (hyperbolic secant) + - acsch (inverse hyperbolic secant) + - tan (tangent) + - atan (inverse tangent) + - tanh (hyperbolic tangent) + - atanh (inverse hyperbolic tangent) + - cot (cotangent) + - acot (inverse cotangent) + - coth (hyperbolic cotangent) + - acoth (inverse hyperbolic cotangent) + - sqrt (square root) + - exp (exponential) + - ln (natural log) + - log10 (base-10 log) + - log2 (base-2 log) + - pow (raised to the power of a real number) + + +--- + +# Usage + +To create a new complex object, you can provide either the real, imaginary and suffix parts as individual values, or as an array of values passed passed to the constructor; or a string representing the value. e.g + +``` +$real = 1.23; +$imaginary = -4.56; +$suffix = 'i'; + +$complexObject = new Complex\Complex($real, $imaginary, $suffix); +``` +or +``` +$real = 1.23; +$imaginary = -4.56; +$suffix = 'i'; + +$arguments = [$real, $imaginary, $suffix]; + +$complexObject = new Complex\Complex($arguments); +``` +or +``` +$complexString = '1.23-4.56i'; + +$complexObject = new Complex\Complex($complexString); +``` + +Complex objects are immutable: whenever you call a method or pass a complex value to a function that returns a complex value, a new Complex object will be returned, and the original will remain unchanged. +This also allows you to chain multiple methods as you would for a fluent interface (as long as they are methods that will return a Complex result). + +## Performing Mathematical Operations + +To perform mathematical operations with Complex values, you can call the appropriate method against a complex value, passing other values as arguments + +``` +$complexString1 = '1.23-4.56i'; +$complexString2 = '2.34+5.67i'; + +$complexObject = new Complex\Complex($complexString1); +echo $complexObject->add($complexString2); +``` +or pass all values to the appropriate function +``` +$complexString1 = '1.23-4.56i'; +$complexString2 = '2.34+5.67i'; + +echo Complex\add($complexString1, $complexString2); +``` +If you want to perform the same operation against multiple values (e.g. to add three or more complex numbers), then you can pass multiple arguments to any of the operations. + +You can pass these arguments as Complex objects, or as an array or string that will parse to a complex object. + +## Using functions + +When calling any of the available functions for a complex value, you can either call the relevant method for the Complex object +``` +$complexString = '1.23-4.56i'; + +$complexObject = new Complex\Complex($complexString); +echo $complexObject->sinh(); +``` +or you can call the function as you would in procedural code, passing the Complex object as an argument +``` +$complexString = '1.23-4.56i'; + +$complexObject = new Complex\Complex($complexString); +echo Complex\sinh($complexObject); +``` +When called procedurally using the function, you can pass in the argument as a Complex object, or as an array or string that will parse to a complex object. +``` +$complexString = '1.23-4.56i'; + +echo Complex\sinh($complexString); +``` + +In the case of the `pow()` function (the only implemented function that requires an additional argument) you need to pass both arguments when calling the function procedurally + +``` +$complexString = '1.23-4.56i'; + +$complexObject = new Complex\Complex($complexString); +echo Complex\pow($complexObject, 2); +``` +or pass the additional argument when calling the method +``` +$complexString = '1.23-4.56i'; + +$complexObject = new Complex\Complex($complexString); +echo $complexObject->pow(2); +``` diff --git a/vendor/markbaker/complex/classes/Autoloader.php b/vendor/markbaker/complex/classes/Autoloader.php new file mode 100644 index 0000000..e7cfdc4 --- /dev/null +++ b/vendor/markbaker/complex/classes/Autoloader.php @@ -0,0 +1,53 @@ +regex = $regex; + parent::__construct($it, $regex); + } +} + +class FilenameFilter extends FilesystemRegexFilter +{ + // Filter files against the regex + public function accept() + { + return (!$this->isFile() || preg_match($this->regex, $this->getFilename())); + } +} + + +$srcFolder = __DIR__ . DIRECTORY_SEPARATOR . 'src'; +$srcDirectory = new RecursiveDirectoryIterator($srcFolder); + +$filteredFileList = new FilenameFilter($srcDirectory, '/(?:php)$/i'); +$filteredFileList = new FilenameFilter($filteredFileList, '/^(?!.*(Complex|Exception)\.php).*$/i'); + +foreach (new RecursiveIteratorIterator($filteredFileList) as $file) { + if ($file->isFile()) { + include_once $file; + } +} diff --git a/vendor/markbaker/complex/classes/src/Complex.php b/vendor/markbaker/complex/classes/src/Complex.php new file mode 100644 index 0000000..3666486 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/Complex.php @@ -0,0 +1,390 @@ +realPart = (float) $realPart; + $this->imaginaryPart = (float) $imaginaryPart; + $this->suffix = strtolower($suffix ?? ''); + } + + /** + * Gets the real part of this complex number + * + * @return Float + */ + public function getReal(): float + { + return $this->realPart; + } + + /** + * Gets the imaginary part of this complex number + * + * @return Float + */ + public function getImaginary(): float + { + return $this->imaginaryPart; + } + + /** + * Gets the suffix of this complex number + * + * @return String + */ + public function getSuffix(): string + { + return $this->suffix; + } + + /** + * Returns true if this is a real value, false if a complex value + * + * @return Bool + */ + public function isReal(): bool + { + return $this->imaginaryPart == 0.0; + } + + /** + * Returns true if this is a complex value, false if a real value + * + * @return Bool + */ + public function isComplex(): bool + { + return !$this->isReal(); + } + + public function format(): string + { + $str = ""; + if ($this->imaginaryPart != 0.0) { + if (\abs($this->imaginaryPart) != 1.0) { + $str .= $this->imaginaryPart . $this->suffix; + } else { + $str .= (($this->imaginaryPart < 0.0) ? '-' : '') . $this->suffix; + } + } + if ($this->realPart != 0.0) { + if (($str) && ($this->imaginaryPart > 0.0)) { + $str = "+" . $str; + } + $str = $this->realPart . $str; + } + if (!$str) { + $str = "0.0"; + } + + return $str; + } + + public function __toString(): string + { + return $this->format(); + } + + /** + * Validates whether the argument is a valid complex number, converting scalar or array values if possible + * + * @param mixed $complex The value to validate + * @return Complex + * @throws Exception If the argument isn't a Complex number or cannot be converted to one + */ + public static function validateComplexArgument($complex): Complex + { + if (is_scalar($complex) || is_array($complex)) { + $complex = new Complex($complex); + } elseif (!is_object($complex) || !($complex instanceof Complex)) { + throw new Exception('Value is not a valid complex number'); + } + + return $complex; + } + + /** + * Returns the reverse of this complex number + * + * @return Complex + */ + public function reverse(): Complex + { + return new Complex( + $this->imaginaryPart, + $this->realPart, + ($this->realPart == 0.0) ? null : $this->suffix + ); + } + + public function invertImaginary(): Complex + { + return new Complex( + $this->realPart, + $this->imaginaryPart * -1, + ($this->imaginaryPart == 0.0) ? null : $this->suffix + ); + } + + public function invertReal(): Complex + { + return new Complex( + $this->realPart * -1, + $this->imaginaryPart, + ($this->imaginaryPart == 0.0) ? null : $this->suffix + ); + } + + protected static $functions = [ + 'abs', + 'acos', + 'acosh', + 'acot', + 'acoth', + 'acsc', + 'acsch', + 'argument', + 'asec', + 'asech', + 'asin', + 'asinh', + 'atan', + 'atanh', + 'conjugate', + 'cos', + 'cosh', + 'cot', + 'coth', + 'csc', + 'csch', + 'exp', + 'inverse', + 'ln', + 'log2', + 'log10', + 'negative', + 'pow', + 'rho', + 'sec', + 'sech', + 'sin', + 'sinh', + 'sqrt', + 'tan', + 'tanh', + 'theta', + ]; + + protected static $operations = [ + 'add', + 'subtract', + 'multiply', + 'divideby', + 'divideinto', + ]; + + /** + * Returns the result of the function call or operation + * + * @return Complex|float + * @throws Exception|\InvalidArgumentException + */ + public function __call($functionName, $arguments) + { + $functionName = strtolower(str_replace('_', '', $functionName)); + + // Test for function calls + if (in_array($functionName, self::$functions, true)) { + $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}"; + return $functionName($this, ...$arguments); + } + // Test for operation calls + if (in_array($functionName, self::$operations, true)) { + $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}"; + return $functionName($this, ...$arguments); + } + throw new Exception('Complex Function or Operation does not exist'); + } +} diff --git a/vendor/markbaker/complex/classes/src/Exception.php b/vendor/markbaker/complex/classes/src/Exception.php new file mode 100644 index 0000000..14dd545 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/Exception.php @@ -0,0 +1,13 @@ +getReal() - $invsqrt->getImaginary(), + $complex->getImaginary() + $invsqrt->getReal() + ); + $log = ln($adjust); + + return new Complex( + $log->getImaginary(), + -1 * $log->getReal() + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/acosh.php b/vendor/markbaker/complex/classes/src/functions/acosh.php new file mode 100644 index 0000000..37e604c --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/acosh.php @@ -0,0 +1,36 @@ +isReal() && ($complex->getReal() > 1)) { + return new Complex(\acosh($complex->getReal())); + } + + $acosh = acos($complex) + ->reverse(); + if ($acosh->getReal() < 0.0) { + $acosh = $acosh->invertReal(); + } + + return $acosh; + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/acot.php b/vendor/markbaker/complex/classes/src/functions/acot.php new file mode 100644 index 0000000..bc77379 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/acot.php @@ -0,0 +1,27 @@ +getReal() == 0.0 && $complex->getImaginary() == 0.0) { + return new Complex(INF); + } + + return asin(inverse($complex)); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/acsch.php b/vendor/markbaker/complex/classes/src/functions/acsch.php new file mode 100644 index 0000000..fb909c2 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/acsch.php @@ -0,0 +1,31 @@ +getReal() == 0.0 && $complex->getImaginary() == 0.0) { + return new Complex(INF); + } + + return asinh(inverse($complex)); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/argument.php b/vendor/markbaker/complex/classes/src/functions/argument.php new file mode 100644 index 0000000..17e6ecb --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/argument.php @@ -0,0 +1,30 @@ +getReal() == 0.0 && $complex->getImaginary() == 0.0) { + return new Complex(INF); + } + + return acos(inverse($complex)); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/asech.php b/vendor/markbaker/complex/classes/src/functions/asech.php new file mode 100644 index 0000000..a1571a1 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/asech.php @@ -0,0 +1,31 @@ +getReal() == 0.0 && $complex->getImaginary() == 0.0) { + return new Complex(INF); + } + + return acosh(inverse($complex)); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/asin.php b/vendor/markbaker/complex/classes/src/functions/asin.php new file mode 100644 index 0000000..05790ff --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/asin.php @@ -0,0 +1,39 @@ +getReal() - $complex->getImaginary(), + $invsqrt->getImaginary() + $complex->getReal() + ); + $log = ln($adjust); + + return new Complex( + $log->getImaginary(), + -1 * $log->getReal() + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/asinh.php b/vendor/markbaker/complex/classes/src/functions/asinh.php new file mode 100644 index 0000000..cee7610 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/asinh.php @@ -0,0 +1,35 @@ +isReal() && ($complex->getReal() > 1)) { + return new Complex(\asinh($complex->getReal())); + } + + $asinh = clone $complex; + $asinh = $asinh->reverse() + ->invertReal(); + $asinh = asin($asinh); + return $asinh->reverse() + ->invertImaginary(); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/atan.php b/vendor/markbaker/complex/classes/src/functions/atan.php new file mode 100644 index 0000000..e026e7a --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/atan.php @@ -0,0 +1,47 @@ +isReal()) { + return new Complex(\atan($complex->getReal())); + } + + $t1Value = new Complex(-1 * $complex->getImaginary(), $complex->getReal()); + $uValue = new Complex(1, 0); + + $d1Value = clone $uValue; + $d1Value = subtract($d1Value, $t1Value); + $d2Value = add($t1Value, $uValue); + $uResult = $d1Value->divideBy($d2Value); + $uResult = ln($uResult); + + return new Complex( + (($uResult->getImaginary() == M_PI) ? -M_PI : $uResult->getImaginary()) * -0.5, + $uResult->getReal() * 0.5, + $complex->getSuffix() + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/atanh.php b/vendor/markbaker/complex/classes/src/functions/atanh.php new file mode 100644 index 0000000..d1e60e6 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/atanh.php @@ -0,0 +1,40 @@ +isReal()) { + $real = $complex->getReal(); + if ($real >= -1.0 && $real <= 1.0) { + return new Complex(\atanh($real)); + } else { + return new Complex(\atanh(1 / $real), (($real < 0.0) ? M_PI_2 : -1 * M_PI_2)); + } + } + + $iComplex = clone $complex; + $iComplex = $iComplex->invertImaginary() + ->reverse(); + return atan($iComplex) + ->invertReal() + ->reverse(); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/conjugate.php b/vendor/markbaker/complex/classes/src/functions/conjugate.php new file mode 100644 index 0000000..d394ab5 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/conjugate.php @@ -0,0 +1,30 @@ +getReal(), + -1 * $complex->getImaginary(), + $complex->getSuffix() + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/cos.php b/vendor/markbaker/complex/classes/src/functions/cos.php new file mode 100644 index 0000000..84fd51f --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/cos.php @@ -0,0 +1,36 @@ +isReal()) { + return new Complex(\cos($complex->getReal())); + } + + return conjugate( + new Complex( + \cos($complex->getReal()) * \cosh($complex->getImaginary()), + \sin($complex->getReal()) * \sinh($complex->getImaginary()), + $complex->getSuffix() + ) + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/cosh.php b/vendor/markbaker/complex/classes/src/functions/cosh.php new file mode 100644 index 0000000..531ae52 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/cosh.php @@ -0,0 +1,34 @@ +isReal()) { + return new Complex(\cosh($complex->getReal())); + } + + return new Complex( + \cosh($complex->getReal()) * \cos($complex->getImaginary()), + \sinh($complex->getReal()) * \sin($complex->getImaginary()), + $complex->getSuffix() + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/cot.php b/vendor/markbaker/complex/classes/src/functions/cot.php new file mode 100644 index 0000000..b775458 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/cot.php @@ -0,0 +1,31 @@ +getReal() == 0.0 && $complex->getImaginary() == 0.0) { + return new Complex(INF); + } + + return inverse(tan($complex)); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/coth.php b/vendor/markbaker/complex/classes/src/functions/coth.php new file mode 100644 index 0000000..6944d77 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/coth.php @@ -0,0 +1,26 @@ +getReal() == 0.0 && $complex->getImaginary() == 0.0) { + return new Complex(INF); + } + + return inverse(sin($complex)); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/csch.php b/vendor/markbaker/complex/classes/src/functions/csch.php new file mode 100644 index 0000000..e714caa --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/csch.php @@ -0,0 +1,31 @@ +getReal() == 0.0 && $complex->getImaginary() == 0.0) { + return new Complex(INF); + } + + return inverse(sinh($complex)); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/exp.php b/vendor/markbaker/complex/classes/src/functions/exp.php new file mode 100644 index 0000000..cbc37e5 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/exp.php @@ -0,0 +1,36 @@ +getReal() == 0.0) && (\abs($complex->getImaginary()) == M_PI)) { + return new Complex(-1.0, 0.0); + } + + $rho = \exp($complex->getReal()); + + return new Complex( + $rho * \cos($complex->getImaginary()), + $rho * \sin($complex->getImaginary()), + $complex->getSuffix() + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/inverse.php b/vendor/markbaker/complex/classes/src/functions/inverse.php new file mode 100644 index 0000000..9e879e7 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/inverse.php @@ -0,0 +1,31 @@ +getReal() == 0.0 && $complex->getImaginary() == 0.0) { + throw new \InvalidArgumentException('Division by zero'); + } + + return $complex->divideInto(1.0); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/ln.php b/vendor/markbaker/complex/classes/src/functions/ln.php new file mode 100644 index 0000000..e011519 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/ln.php @@ -0,0 +1,35 @@ +getReal() == 0.0) && ($complex->getImaginary() == 0.0)) { + throw new \InvalidArgumentException(); + } + + return new Complex( + \log(rho($complex)), + theta($complex), + $complex->getSuffix() + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/log10.php b/vendor/markbaker/complex/classes/src/functions/log10.php new file mode 100644 index 0000000..2efdc30 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/log10.php @@ -0,0 +1,34 @@ +getReal() == 0.0) && ($complex->getImaginary() == 0.0)) { + throw new \InvalidArgumentException(); + } elseif (($complex->getReal() > 0.0) && ($complex->getImaginary() == 0.0)) { + return new Complex(\log10($complex->getReal()), 0.0, $complex->getSuffix()); + } + + return ln($complex) + ->multiply(\log10(Complex::EULER)); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/log2.php b/vendor/markbaker/complex/classes/src/functions/log2.php new file mode 100644 index 0000000..1b114bc --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/log2.php @@ -0,0 +1,34 @@ +getReal() == 0.0) && ($complex->getImaginary() == 0.0)) { + throw new \InvalidArgumentException(); + } elseif (($complex->getReal() > 0.0) && ($complex->getImaginary() == 0.0)) { + return new Complex(\log($complex->getReal(), 2), 0.0, $complex->getSuffix()); + } + + return ln($complex) + ->multiply(\log(Complex::EULER, 2)); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/negative.php b/vendor/markbaker/complex/classes/src/functions/negative.php new file mode 100644 index 0000000..24e3ad7 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/negative.php @@ -0,0 +1,33 @@ +getReal(), + -1 * $complex->getImaginary(), + $complex->getSuffix() + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/pow.php b/vendor/markbaker/complex/classes/src/functions/pow.php new file mode 100644 index 0000000..326a41c --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/pow.php @@ -0,0 +1,42 @@ +getImaginary() == 0.0 && $complex->getReal() >= 0.0) { + return new Complex(\pow($complex->getReal(), $power)); + } + + $rValue = \sqrt(($complex->getReal() * $complex->getReal()) + ($complex->getImaginary() * $complex->getImaginary())); + $rPower = \pow($rValue, $power); + $theta = $complex->argument() * $power; + if ($theta == 0) { + return new Complex(1); + } + + return new Complex($rPower * \cos($theta), $rPower * \sin($theta), $complex->getSuffix()); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/rho.php b/vendor/markbaker/complex/classes/src/functions/rho.php new file mode 100644 index 0000000..dfc4049 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/rho.php @@ -0,0 +1,30 @@ +getReal() * $complex->getReal()) + + ($complex->getImaginary() * $complex->getImaginary()) + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/sec.php b/vendor/markbaker/complex/classes/src/functions/sec.php new file mode 100644 index 0000000..e1a0b13 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/sec.php @@ -0,0 +1,27 @@ +isReal()) { + return new Complex(\sin($complex->getReal())); + } + + return new Complex( + \sin($complex->getReal()) * \cosh($complex->getImaginary()), + \cos($complex->getReal()) * \sinh($complex->getImaginary()), + $complex->getSuffix() + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/sinh.php b/vendor/markbaker/complex/classes/src/functions/sinh.php new file mode 100644 index 0000000..6563422 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/sinh.php @@ -0,0 +1,34 @@ +isReal()) { + return new Complex(\sinh($complex->getReal())); + } + + return new Complex( + \sinh($complex->getReal()) * \cos($complex->getImaginary()), + \cosh($complex->getReal()) * \sin($complex->getImaginary()), + $complex->getSuffix() + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/sqrt.php b/vendor/markbaker/complex/classes/src/functions/sqrt.php new file mode 100644 index 0000000..5753a86 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/sqrt.php @@ -0,0 +1,31 @@ +getSuffix()); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/tan.php b/vendor/markbaker/complex/classes/src/functions/tan.php new file mode 100644 index 0000000..a6f9610 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/tan.php @@ -0,0 +1,42 @@ +isReal()) { + return new Complex(\tan($complex->getReal())); + } + + $real = $complex->getReal(); + $imaginary = $complex->getImaginary(); + $divisor = 1 + \pow(\tan($real), 2) * \pow(\tanh($imaginary), 2); + if ($divisor == 0.0) { + throw new \InvalidArgumentException('Division by zero'); + } + + return new Complex( + \pow(sech($imaginary)->getReal(), 2) * \tan($real) / $divisor, + \pow(sec($real)->getReal(), 2) * \tanh($imaginary) / $divisor, + $complex->getSuffix() + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/tanh.php b/vendor/markbaker/complex/classes/src/functions/tanh.php new file mode 100644 index 0000000..8a8ccdc --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/tanh.php @@ -0,0 +1,37 @@ +getReal(); + $imaginary = $complex->getImaginary(); + $divisor = \cos($imaginary) * \cos($imaginary) + \sinh($real) * \sinh($real); + if ($divisor == 0.0) { + throw new \InvalidArgumentException('Division by zero'); + } + + return new Complex( + \sinh($real) * \cosh($real) / $divisor, + 0.5 * \sin(2 * $imaginary) / $divisor, + $complex->getSuffix() + ); + } +} diff --git a/vendor/markbaker/complex/classes/src/functions/theta.php b/vendor/markbaker/complex/classes/src/functions/theta.php new file mode 100644 index 0000000..cdba45e --- /dev/null +++ b/vendor/markbaker/complex/classes/src/functions/theta.php @@ -0,0 +1,40 @@ +getReal() == 0.0) { + if ($complex->isReal()) { + return 0.0; + } elseif ($complex->getImaginary() < 0.0) { + return M_PI / -2; + } + return M_PI / 2; + } elseif ($complex->getReal() > 0.0) { + return \atan($complex->getImaginary() / $complex->getReal()); + } elseif ($complex->getImaginary() < 0.0) { + return -(M_PI - \atan(\abs($complex->getImaginary()) / \abs($complex->getReal()))); + } + + return M_PI - \atan($complex->getImaginary() / \abs($complex->getReal())); + } +} diff --git a/vendor/markbaker/complex/classes/src/operations/add.php b/vendor/markbaker/complex/classes/src/operations/add.php new file mode 100644 index 0000000..1c726be --- /dev/null +++ b/vendor/markbaker/complex/classes/src/operations/add.php @@ -0,0 +1,48 @@ +isComplex() && $complex->isComplex() && + $result->getSuffix() !== $complex->getSuffix()) { + throw new Exception('Suffix Mismatch'); + } + + $real = $result->getReal() + $complex->getReal(); + $imaginary = $result->getImaginary() + $complex->getImaginary(); + + $result = new Complex( + $real, + $imaginary, + ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix()) + ); + } + + return $result; + } +} diff --git a/vendor/markbaker/complex/classes/src/operations/divideby.php b/vendor/markbaker/complex/classes/src/operations/divideby.php new file mode 100644 index 0000000..8c84a88 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/operations/divideby.php @@ -0,0 +1,58 @@ +isComplex() && $complex->isComplex() && + $result->getSuffix() !== $complex->getSuffix()) { + throw new Exception('Suffix Mismatch'); + } + if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) { + throw new \InvalidArgumentException('Division by zero'); + } + + $delta1 = ($result->getReal() * $complex->getReal()) + + ($result->getImaginary() * $complex->getImaginary()); + $delta2 = ($result->getImaginary() * $complex->getReal()) - + ($result->getReal() * $complex->getImaginary()); + $delta3 = ($complex->getReal() * $complex->getReal()) + + ($complex->getImaginary() * $complex->getImaginary()); + + $real = $delta1 / $delta3; + $imaginary = $delta2 / $delta3; + + $result = new Complex( + $real, + $imaginary, + ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix()) + ); + } + + return $result; + } +} diff --git a/vendor/markbaker/complex/classes/src/operations/divideinto.php b/vendor/markbaker/complex/classes/src/operations/divideinto.php new file mode 100644 index 0000000..dff2174 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/operations/divideinto.php @@ -0,0 +1,58 @@ +isComplex() && $complex->isComplex() && + $result->getSuffix() !== $complex->getSuffix()) { + throw new Exception('Suffix Mismatch'); + } + if ($result->getReal() == 0.0 && $result->getImaginary() == 0.0) { + throw new \InvalidArgumentException('Division by zero'); + } + + $delta1 = ($complex->getReal() * $result->getReal()) + + ($complex->getImaginary() * $result->getImaginary()); + $delta2 = ($complex->getImaginary() * $result->getReal()) - + ($complex->getReal() * $result->getImaginary()); + $delta3 = ($result->getReal() * $result->getReal()) + + ($result->getImaginary() * $result->getImaginary()); + + $real = $delta1 / $delta3; + $imaginary = $delta2 / $delta3; + + $result = new Complex( + $real, + $imaginary, + ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix()) + ); + } + + return $result; + } +} diff --git a/vendor/markbaker/complex/classes/src/operations/multiply.php b/vendor/markbaker/complex/classes/src/operations/multiply.php new file mode 100644 index 0000000..60de4ce --- /dev/null +++ b/vendor/markbaker/complex/classes/src/operations/multiply.php @@ -0,0 +1,50 @@ +isComplex() && $complex->isComplex() && + $result->getSuffix() !== $complex->getSuffix()) { + throw new Exception('Suffix Mismatch'); + } + + $real = ($result->getReal() * $complex->getReal()) - + ($result->getImaginary() * $complex->getImaginary()); + $imaginary = ($result->getReal() * $complex->getImaginary()) + + ($result->getImaginary() * $complex->getReal()); + + $result = new Complex( + $real, + $imaginary, + ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix()) + ); + } + + return $result; + } +} diff --git a/vendor/markbaker/complex/classes/src/operations/subtract.php b/vendor/markbaker/complex/classes/src/operations/subtract.php new file mode 100644 index 0000000..79427b5 --- /dev/null +++ b/vendor/markbaker/complex/classes/src/operations/subtract.php @@ -0,0 +1,48 @@ +isComplex() && $complex->isComplex() && + $result->getSuffix() !== $complex->getSuffix()) { + throw new Exception('Suffix Mismatch'); + } + + $real = $result->getReal() - $complex->getReal(); + $imaginary = $result->getImaginary() - $complex->getImaginary(); + + $result = new Complex( + $real, + $imaginary, + ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix()) + ); + } + + return $result; + } +} diff --git a/vendor/markbaker/complex/composer.json b/vendor/markbaker/complex/composer.json new file mode 100644 index 0000000..adc24dc --- /dev/null +++ b/vendor/markbaker/complex/composer.json @@ -0,0 +1,77 @@ +{ + "name": "markbaker/complex", + "type": "library", + "description": "PHP Class for working with complex numbers", + "keywords": ["complex", "mathematics"], + "homepage": "https://github.com/MarkBaker/PHPComplex", + "license": "MIT", + "authors": [ + { + "name": "Mark Baker", + "email": "mark@lange.demon.co.uk" + } + ], + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.3", + "squizlabs/php_codesniffer": "^3.4", + "phpcompatibility/php-compatibility": "^9.0", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0" + }, + "autoload": { + "psr-4": { + "Complex\\": "classes/src/" + }, + "files": [ + "classes/src/functions/abs.php", + "classes/src/functions/acos.php", + "classes/src/functions/acosh.php", + "classes/src/functions/acot.php", + "classes/src/functions/acoth.php", + "classes/src/functions/acsc.php", + "classes/src/functions/acsch.php", + "classes/src/functions/argument.php", + "classes/src/functions/asec.php", + "classes/src/functions/asech.php", + "classes/src/functions/asin.php", + "classes/src/functions/asinh.php", + "classes/src/functions/atan.php", + "classes/src/functions/atanh.php", + "classes/src/functions/conjugate.php", + "classes/src/functions/cos.php", + "classes/src/functions/cosh.php", + "classes/src/functions/cot.php", + "classes/src/functions/coth.php", + "classes/src/functions/csc.php", + "classes/src/functions/csch.php", + "classes/src/functions/exp.php", + "classes/src/functions/inverse.php", + "classes/src/functions/ln.php", + "classes/src/functions/log2.php", + "classes/src/functions/log10.php", + "classes/src/functions/negative.php", + "classes/src/functions/pow.php", + "classes/src/functions/rho.php", + "classes/src/functions/sec.php", + "classes/src/functions/sech.php", + "classes/src/functions/sin.php", + "classes/src/functions/sinh.php", + "classes/src/functions/sqrt.php", + "classes/src/functions/tan.php", + "classes/src/functions/tanh.php", + "classes/src/functions/theta.php", + "classes/src/operations/add.php", + "classes/src/operations/subtract.php", + "classes/src/operations/multiply.php", + "classes/src/operations/divideby.php", + "classes/src/operations/divideinto.php" + ] + }, + "scripts": { + "style": "phpcs --report-width=200 --standard=PSR2 --report=summary,full classes/src/ unitTests/classes/src -n", + "versions": "phpcs --report-width=200 --standard=PHPCompatibility --report=summary,full classes/src/ --runtime-set testVersion 7.2- -n" + }, + "minimum-stability": "dev" +} diff --git a/vendor/markbaker/complex/examples/complexTest.php b/vendor/markbaker/complex/examples/complexTest.php new file mode 100644 index 0000000..d8aed62 --- /dev/null +++ b/vendor/markbaker/complex/examples/complexTest.php @@ -0,0 +1,154 @@ +add(456); +echo $x, PHP_EOL; + +$x = new Complex(123.456); +$x->add(789.012); +echo $x, PHP_EOL; + +$x = new Complex(123.456, 78.90); +$x->add(new Complex(-987.654, -32.1)); +echo $x, PHP_EOL; + +$x = new Complex(123.456, 78.90); +$x->add(-987.654); +echo $x, PHP_EOL; + +$x = new Complex(-987.654, -32.1); +$x->add(new Complex(0, 1)); +echo $x, PHP_EOL; + +$x = new Complex(-987.654, -32.1); +$x->add(new Complex(0, -1)); +echo $x, PHP_EOL; + + +echo PHP_EOL, 'Subtract', PHP_EOL; + +$x = new Complex(123); +$x->subtract(456); +echo $x, PHP_EOL; + +$x = new Complex(123.456); +$x->subtract(789.012); +echo $x, PHP_EOL; + +$x = new Complex(123.456, 78.90); +$x->subtract(new Complex(-987.654, -32.1)); +echo $x, PHP_EOL; + +$x = new Complex(123.456, 78.90); +$x->subtract(-987.654); +echo $x, PHP_EOL; + +$x = new Complex(-987.654, -32.1); +$x->subtract(new Complex(0, 1)); +echo $x, PHP_EOL; + +$x = new Complex(-987.654, -32.1); +$x->subtract(new Complex(0, -1)); +echo $x, PHP_EOL; + + +echo PHP_EOL, 'Multiply', PHP_EOL; + +$x = new Complex(123); +$x->multiply(456); +echo $x, PHP_EOL; + +$x = new Complex(123.456); +$x->multiply(789.012); +echo $x, PHP_EOL; + +$x = new Complex(123.456, 78.90); +$x->multiply(new Complex(-987.654, -32.1)); +echo $x, PHP_EOL; + +$x = new Complex(123.456, 78.90); +$x->multiply(-987.654); +echo $x, PHP_EOL; + +$x = new Complex(-987.654, -32.1); +$x->multiply(new Complex(0, 1)); +echo $x, PHP_EOL; + +$x = new Complex(-987.654, -32.1); +$x->multiply(new Complex(0, -1)); +echo $x, PHP_EOL; + + +echo PHP_EOL, 'Divide By', PHP_EOL; + +$x = new Complex(123); +$x->divideBy(456); +echo $x, PHP_EOL; + +$x = new Complex(123.456); +$x->divideBy(789.012); +echo $x, PHP_EOL; + +$x = new Complex(123.456, 78.90); +$x->divideBy(new Complex(-987.654, -32.1)); +echo $x, PHP_EOL; + +$x = new Complex(123.456, 78.90); +$x->divideBy(-987.654); +echo $x, PHP_EOL; + +$x = new Complex(-987.654, -32.1); +$x->divideBy(new Complex(0, 1)); +echo $x, PHP_EOL; + +$x = new Complex(-987.654, -32.1); +$x->divideBy(new Complex(0, -1)); +echo $x, PHP_EOL; + + +echo PHP_EOL, 'Divide Into', PHP_EOL; + +$x = new Complex(123); +$x->divideInto(456); +echo $x, PHP_EOL; + +$x = new Complex(123.456); +$x->divideInto(789.012); +echo $x, PHP_EOL; + +$x = new Complex(123.456, 78.90); +$x->divideInto(new Complex(-987.654, -32.1)); +echo $x, PHP_EOL; + +$x = new Complex(123.456, 78.90); +$x->divideInto(-987.654); +echo $x, PHP_EOL; + +$x = new Complex(-987.654, -32.1); +$x->divideInto(new Complex(0, 1)); +echo $x, PHP_EOL; + +$x = new Complex(-987.654, -32.1); +$x->divideInto(new Complex(0, -1)); +echo $x, PHP_EOL; diff --git a/vendor/markbaker/complex/examples/testFunctions.php b/vendor/markbaker/complex/examples/testFunctions.php new file mode 100644 index 0000000..69cc7dc --- /dev/null +++ b/vendor/markbaker/complex/examples/testFunctions.php @@ -0,0 +1,52 @@ +getMessage(), PHP_EOL; + } + } + echo PHP_EOL; + } +} diff --git a/vendor/markbaker/complex/examples/testOperations.php b/vendor/markbaker/complex/examples/testOperations.php new file mode 100644 index 0000000..ee5699d --- /dev/null +++ b/vendor/markbaker/complex/examples/testOperations.php @@ -0,0 +1,34 @@ + ', $result, PHP_EOL; + +echo PHP_EOL; + +echo 'Subtraction', PHP_EOL; + +$result = \Complex\subtract(...$values); +echo '=> ', $result, PHP_EOL; + +echo PHP_EOL; + +echo 'Multiplication', PHP_EOL; + +$result = \Complex\multiply(...$values); +echo '=> ', $result, PHP_EOL; diff --git a/vendor/markbaker/complex/license.md b/vendor/markbaker/complex/license.md new file mode 100644 index 0000000..e123c90 --- /dev/null +++ b/vendor/markbaker/complex/license.md @@ -0,0 +1,25 @@ +The MIT License (MIT) +===================== + +Copyright © `2017` `Mark Baker` + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vendor/markbaker/matrix/.github/workflows/main.yaml b/vendor/markbaker/matrix/.github/workflows/main.yaml new file mode 100644 index 0000000..229fe04 --- /dev/null +++ b/vendor/markbaker/matrix/.github/workflows/main.yaml @@ -0,0 +1,124 @@ +name: main +on: [ push, pull_request ] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + php-version: + - '7.1' + - '7.2' + - '7.3' + - '7.4' + - '8.0' + - '8.1' + - '8.2' + + include: + - php-version: 'nightly' + experimental: true + + name: PHP ${{ matrix.php-version }} + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v3 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Delete composer lock file + id: composer-lock + if: ${{ matrix.php-version == '8.0' || matrix.php-version == '8.1' || matrix.php-version == '8.2' || matrix.php-version == 'nightly' }} + run: | + rm composer.lock + echo "::set-output name=flags::--ignore-platform-reqs" + + - name: Install dependencies + run: composer update --no-progress --prefer-dist --optimize-autoloader ${{ steps.composer-lock.outputs.flags }} + + - name: Setup problem matchers for PHP + run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" + + - name: Setup problem matchers for PHPUnit + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Test with PHPUnit + run: ./vendor/bin/phpunit + + phpcs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib + coverage: none + tools: cs2pr + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v3 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Code style with PHP_CodeSniffer + run: ./vendor/bin/phpcs -q --report=checkstyle | cs2pr --graceful-warnings --colorize + + coverage: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib + coverage: pcov + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v3 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Coverage + run: | + ./vendor/bin/phpunit --coverage-text diff --git a/vendor/markbaker/matrix/README.md b/vendor/markbaker/matrix/README.md new file mode 100644 index 0000000..50f3580 --- /dev/null +++ b/vendor/markbaker/matrix/README.md @@ -0,0 +1,207 @@ +PHPMatrix +========== + +--- + +PHP Class for handling Matrices + +[![Build Status](https://travis-ci.org/MarkBaker/PHPMatrix.png?branch=2.0)](http://travis-ci.org/MarkBaker/PHPMatrix) + +[![Matrix Transform](https://imgs.xkcd.com/comics/matrix_transform.png)](https://xkcd.com/184/) + +Matrix Transform + +--- + +This library currently provides the following operations: + + - addition + - direct sum + - subtraction + - multiplication + - division (using [A].[B]-1) + - division by + - division into + +together with functions for + + - adjoint + - antidiagonal + - cofactors + - determinant + - diagonal + - identity + - inverse + - minors + - trace + - transpose + - solve + + Given Matrices A and B, calculate X for A.X = B + +and classes for + + - Decomposition + - LU Decomposition with partial row pivoting, + + such that [P].[A] = [L].[U] and [A] = [P]|.[L].[U] + - QR Decomposition + + such that [A] = [Q].[R] + +## TO DO + + - power() function + - Decomposition + - Cholesky Decomposition + - EigenValue Decomposition + - EigenValues + - EigenVectors + +--- + +# Usage + +To create a new Matrix object, provide an array as the constructor argument + +```php +$grid = [ + [16, 3, 2, 13], + [ 5, 10, 11, 8], + [ 9, 6, 7, 12], + [ 4, 15, 14, 1], +]; + +$matrix = new Matrix\Matrix($grid); +``` +The `Builder` class provides helper methods for creating specific matrices, specifically an identity matrix of a specified size; or a matrix of a specified dimensions, with every cell containing a set value. +```php +$matrix = Matrix\Builder::createFilledMatrix(1, 5, 3); +``` +Will create a matrix of 5 rows and 3 columns, filled with a `1` in every cell; while +```php +$matrix = Matrix\Builder::createIdentityMatrix(3); +``` +will create a 3x3 identity matrix. + + +Matrix objects are immutable: whenever you call a method or pass a grid to a function that returns a matrix value, a new Matrix object will be returned, and the original will remain unchanged. This also allows you to chain multiple methods as you would for a fluent interface (as long as they are methods that will return a Matrix result). + +## Performing Mathematical Operations + +To perform mathematical operations with Matrices, you can call the appropriate method against a matrix value, passing other values as arguments + +```php +$matrix1 = new Matrix\Matrix([ + [2, 7, 6], + [9, 5, 1], + [4, 3, 8], +]); +$matrix2 = new Matrix\Matrix([ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], +]); + +var_dump($matrix1->multiply($matrix2)->toArray()); +``` +or pass all values to the appropriate function +```php +$matrix1 = new Matrix\Matrix([ + [2, 7, 6], + [9, 5, 1], + [4, 3, 8], +]); +$matrix2 = new Matrix\Matrix([ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], +]); + +var_dump(Matrix\multiply($matrix1, $matrix2)->toArray()); +``` +You can pass in the arguments as Matrix objects, or as arrays. + +If you want to perform the same operation against multiple values (e.g. to add three or more matrices), then you can pass multiple arguments to any of the operations. + +## Using functions + +When calling any of the available functions for a matrix value, you can either call the relevant method for the Matrix object +```php +$grid = [ + [16, 3, 2, 13], + [ 5, 10, 11, 8], + [ 9, 6, 7, 12], + [ 4, 15, 14, 1], +]; + +$matrix = new Matrix\Matrix($grid); + +echo $matrix->trace(); +``` +or you can call the function as you would in procedural code, passing the Matrix object as an argument +```php +$grid = [ + [16, 3, 2, 13], + [ 5, 10, 11, 8], + [ 9, 6, 7, 12], + [ 4, 15, 14, 1], +]; + +$matrix = new Matrix\Matrix($grid); +echo Matrix\trace($matrix); +``` +When called procedurally using the function, you can pass in the argument as a Matrix object, or as an array. +```php +$grid = [ + [16, 3, 2, 13], + [ 5, 10, 11, 8], + [ 9, 6, 7, 12], + [ 4, 15, 14, 1], +]; + +echo Matrix\trace($grid); +``` +As an alternative, it is also possible to call the method directly from the `Functions` class. +```php +$grid = [ + [16, 3, 2, 13], + [ 5, 10, 11, 8], + [ 9, 6, 7, 12], + [ 4, 15, 14, 1], +]; + +$matrix = new Matrix\Matrix($grid); +echo Matrix\Functions::trace($matrix); +``` +Used this way, methods must be called statically, and the argument must be the Matrix object, and cannot be an array. + +## Decomposition + +The library also provides classes for matrix decomposition. You can access these using +```php +$grid = [ + [1, 2], + [3, 4], +]; + +$matrix = new Matrix\Matrix($grid); + +$decomposition = new Matrix\Decomposition\QR($matrix); +$Q = $decomposition->getQ(); +$R = $decomposition->getR(); +``` + +or alternatively us the `Decomposition` factory, identifying which form of decomposition you want to use +```php +$grid = [ + [1, 2], + [3, 4], +]; + +$matrix = new Matrix\Matrix($grid); + +$decomposition = Matrix\Decomposition\Decomposition::decomposition(Matrix\Decomposition\Decomposition::QR, $matrix); +$Q = $decomposition->getQ(); +$R = $decomposition->getR(); +``` diff --git a/vendor/markbaker/matrix/buildPhar.php b/vendor/markbaker/matrix/buildPhar.php new file mode 100644 index 0000000..8bec8be --- /dev/null +++ b/vendor/markbaker/matrix/buildPhar.php @@ -0,0 +1,62 @@ + 'Mark Baker ', + 'Description' => 'PHP Class for working with Matrix numbers', + 'Copyright' => 'Mark Baker (c) 2013-' . date('Y'), + 'Timestamp' => time(), + 'Version' => '0.1.0', + 'Date' => date('Y-m-d') +); + +// cleanup +if (file_exists($pharName)) { + echo "Removed: {$pharName}\n"; + unlink($pharName); +} + +echo "Building phar file...\n"; + +// the phar object +$phar = new Phar($pharName, null, 'Matrix'); +$phar->buildFromDirectory($sourceDir); +$phar->setStub( +<<<'EOT' +getMessage()); + exit(1); + } + + include 'phar://functions/sqrt.php'; + + __HALT_COMPILER(); +EOT +); +$phar->setMetadata($metaData); +$phar->compressFiles(Phar::GZ); + +echo "Complete.\n"; + +exit(); diff --git a/vendor/markbaker/matrix/classes/src/Builder.php b/vendor/markbaker/matrix/classes/src/Builder.php new file mode 100644 index 0000000..d075479 --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Builder.php @@ -0,0 +1,70 @@ +toArray(); + + for ($x = 0; $x < $dimensions; ++$x) { + $grid[$x][$x] = 1; + } + + return new Matrix($grid); + } +} diff --git a/vendor/markbaker/matrix/classes/src/Decomposition/Decomposition.php b/vendor/markbaker/matrix/classes/src/Decomposition/Decomposition.php new file mode 100644 index 0000000..d4e76f9 --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Decomposition/Decomposition.php @@ -0,0 +1,27 @@ +luMatrix = $matrix->toArray(); + $this->rows = $matrix->rows; + $this->columns = $matrix->columns; + + $this->buildPivot(); + } + + /** + * Get lower triangular factor. + * + * @return Matrix Lower triangular factor + */ + public function getL(): Matrix + { + $lower = []; + + $columns = min($this->rows, $this->columns); + for ($row = 0; $row < $this->rows; ++$row) { + for ($column = 0; $column < $columns; ++$column) { + if ($row > $column) { + $lower[$row][$column] = $this->luMatrix[$row][$column]; + } elseif ($row === $column) { + $lower[$row][$column] = 1.0; + } else { + $lower[$row][$column] = 0.0; + } + } + } + + return new Matrix($lower); + } + + /** + * Get upper triangular factor. + * + * @return Matrix Upper triangular factor + */ + public function getU(): Matrix + { + $upper = []; + + $rows = min($this->rows, $this->columns); + for ($row = 0; $row < $rows; ++$row) { + for ($column = 0; $column < $this->columns; ++$column) { + if ($row <= $column) { + $upper[$row][$column] = $this->luMatrix[$row][$column]; + } else { + $upper[$row][$column] = 0.0; + } + } + } + + return new Matrix($upper); + } + + /** + * Return pivot permutation vector. + * + * @return Matrix Pivot matrix + */ + public function getP(): Matrix + { + $pMatrix = []; + + $pivots = $this->pivot; + $pivotCount = count($pivots); + foreach ($pivots as $row => $pivot) { + $pMatrix[$row] = array_fill(0, $pivotCount, 0); + $pMatrix[$row][$pivot] = 1; + } + + return new Matrix($pMatrix); + } + + /** + * Return pivot permutation vector. + * + * @return array Pivot vector + */ + public function getPivot(): array + { + return $this->pivot; + } + + /** + * Is the matrix nonsingular? + * + * @return bool true if U, and hence A, is nonsingular + */ + public function isNonsingular(): bool + { + for ($diagonal = 0; $diagonal < $this->columns; ++$diagonal) { + if ($this->luMatrix[$diagonal][$diagonal] === 0.0) { + return false; + } + } + + return true; + } + + private function buildPivot(): void + { + for ($row = 0; $row < $this->rows; ++$row) { + $this->pivot[$row] = $row; + } + + for ($column = 0; $column < $this->columns; ++$column) { + $luColumn = $this->localisedReferenceColumn($column); + + $this->applyTransformations($column, $luColumn); + + $pivot = $this->findPivot($column, $luColumn); + if ($pivot !== $column) { + $this->pivotExchange($pivot, $column); + } + + $this->computeMultipliers($column); + + unset($luColumn); + } + } + + private function localisedReferenceColumn($column): array + { + $luColumn = []; + + for ($row = 0; $row < $this->rows; ++$row) { + $luColumn[$row] = &$this->luMatrix[$row][$column]; + } + + return $luColumn; + } + + private function applyTransformations($column, array $luColumn): void + { + for ($row = 0; $row < $this->rows; ++$row) { + $luRow = $this->luMatrix[$row]; + // Most of the time is spent in the following dot product. + $kmax = min($row, $column); + $sValue = 0.0; + for ($kValue = 0; $kValue < $kmax; ++$kValue) { + $sValue += $luRow[$kValue] * $luColumn[$kValue]; + } + $luRow[$column] = $luColumn[$row] -= $sValue; + } + } + + private function findPivot($column, array $luColumn): int + { + $pivot = $column; + for ($row = $column + 1; $row < $this->rows; ++$row) { + if (abs($luColumn[$row]) > abs($luColumn[$pivot])) { + $pivot = $row; + } + } + + return $pivot; + } + + private function pivotExchange($pivot, $column): void + { + for ($kValue = 0; $kValue < $this->columns; ++$kValue) { + $tValue = $this->luMatrix[$pivot][$kValue]; + $this->luMatrix[$pivot][$kValue] = $this->luMatrix[$column][$kValue]; + $this->luMatrix[$column][$kValue] = $tValue; + } + + $lValue = $this->pivot[$pivot]; + $this->pivot[$pivot] = $this->pivot[$column]; + $this->pivot[$column] = $lValue; + } + + private function computeMultipliers($diagonal): void + { + if (($diagonal < $this->rows) && ($this->luMatrix[$diagonal][$diagonal] != 0.0)) { + for ($row = $diagonal + 1; $row < $this->rows; ++$row) { + $this->luMatrix[$row][$diagonal] /= $this->luMatrix[$diagonal][$diagonal]; + } + } + } + + private function pivotB(Matrix $B): array + { + $X = []; + foreach ($this->pivot as $rowId) { + $row = $B->getRows($rowId + 1)->toArray(); + $X[] = array_pop($row); + } + + return $X; + } + + /** + * Solve A*X = B. + * + * @param Matrix $B a Matrix with as many rows as A and any number of columns + * + * @throws Exception + * + * @return Matrix X so that L*U*X = B(piv,:) + */ + public function solve(Matrix $B): Matrix + { + if ($B->rows !== $this->rows) { + throw new Exception('Matrix row dimensions are not equal'); + } + + if ($this->rows !== $this->columns) { + throw new Exception('LU solve() only works on square matrices'); + } + + if (!$this->isNonsingular()) { + throw new Exception('Can only perform operation on singular matrix'); + } + + // Copy right hand side with pivoting + $nx = $B->columns; + $X = $this->pivotB($B); + + // Solve L*Y = B(piv,:) + for ($k = 0; $k < $this->columns; ++$k) { + for ($i = $k + 1; $i < $this->columns; ++$i) { + for ($j = 0; $j < $nx; ++$j) { + $X[$i][$j] -= $X[$k][$j] * $this->luMatrix[$i][$k]; + } + } + } + + // Solve U*X = Y; + for ($k = $this->columns - 1; $k >= 0; --$k) { + for ($j = 0; $j < $nx; ++$j) { + $X[$k][$j] /= $this->luMatrix[$k][$k]; + } + for ($i = 0; $i < $k; ++$i) { + for ($j = 0; $j < $nx; ++$j) { + $X[$i][$j] -= $X[$k][$j] * $this->luMatrix[$i][$k]; + } + } + } + + return new Matrix($X); + } +} diff --git a/vendor/markbaker/matrix/classes/src/Decomposition/QR.php b/vendor/markbaker/matrix/classes/src/Decomposition/QR.php new file mode 100644 index 0000000..93bf414 --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Decomposition/QR.php @@ -0,0 +1,191 @@ +qrMatrix = $matrix->toArray(); + $this->rows = $matrix->rows; + $this->columns = $matrix->columns; + + $this->decompose(); + } + + public function getHouseholdVectors(): Matrix + { + $householdVectors = []; + for ($row = 0; $row < $this->rows; ++$row) { + for ($column = 0; $column < $this->columns; ++$column) { + if ($row >= $column) { + $householdVectors[$row][$column] = $this->qrMatrix[$row][$column]; + } else { + $householdVectors[$row][$column] = 0.0; + } + } + } + + return new Matrix($householdVectors); + } + + public function getQ(): Matrix + { + $qGrid = []; + + $rowCount = $this->rows; + for ($k = $this->columns - 1; $k >= 0; --$k) { + for ($i = 0; $i < $this->rows; ++$i) { + $qGrid[$i][$k] = 0.0; + } + $qGrid[$k][$k] = 1.0; + if ($this->columns > $this->rows) { + $qGrid = array_slice($qGrid, 0, $this->rows); + } + + for ($j = $k; $j < $this->columns; ++$j) { + if (isset($this->qrMatrix[$k], $this->qrMatrix[$k][$k]) && $this->qrMatrix[$k][$k] != 0.0) { + $s = 0.0; + for ($i = $k; $i < $this->rows; ++$i) { + $s += $this->qrMatrix[$i][$k] * $qGrid[$i][$j]; + } + $s = -$s / $this->qrMatrix[$k][$k]; + for ($i = $k; $i < $this->rows; ++$i) { + $qGrid[$i][$j] += $s * $this->qrMatrix[$i][$k]; + } + } + } + } + + array_walk( + $qGrid, + function (&$row) use ($rowCount) { + $row = array_reverse($row); + $row = array_slice($row, 0, $rowCount); + } + ); + + return new Matrix($qGrid); + } + + public function getR(): Matrix + { + $rGrid = []; + + for ($row = 0; $row < $this->columns; ++$row) { + for ($column = 0; $column < $this->columns; ++$column) { + if ($row < $column) { + $rGrid[$row][$column] = $this->qrMatrix[$row][$column] ?? 0.0; + } elseif ($row === $column) { + $rGrid[$row][$column] = $this->rDiagonal[$row] ?? 0.0; + } else { + $rGrid[$row][$column] = 0.0; + } + } + } + + if ($this->columns > $this->rows) { + $rGrid = array_slice($rGrid, 0, $this->rows); + } + + return new Matrix($rGrid); + } + + private function hypo($a, $b): float + { + if (abs($a) > abs($b)) { + $r = $b / $a; + $r = abs($a) * sqrt(1 + $r * $r); + } elseif ($b != 0.0) { + $r = $a / $b; + $r = abs($b) * sqrt(1 + $r * $r); + } else { + $r = 0.0; + } + + return $r; + } + + /** + * QR Decomposition computed by Householder reflections. + */ + private function decompose(): void + { + for ($k = 0; $k < $this->columns; ++$k) { + // Compute 2-norm of k-th column without under/overflow. + $norm = 0.0; + for ($i = $k; $i < $this->rows; ++$i) { + $norm = $this->hypo($norm, $this->qrMatrix[$i][$k]); + } + if ($norm != 0.0) { + // Form k-th Householder vector. + if ($this->qrMatrix[$k][$k] < 0.0) { + $norm = -$norm; + } + for ($i = $k; $i < $this->rows; ++$i) { + $this->qrMatrix[$i][$k] /= $norm; + } + $this->qrMatrix[$k][$k] += 1.0; + // Apply transformation to remaining columns. + for ($j = $k + 1; $j < $this->columns; ++$j) { + $s = 0.0; + for ($i = $k; $i < $this->rows; ++$i) { + $s += $this->qrMatrix[$i][$k] * $this->qrMatrix[$i][$j]; + } + $s = -$s / $this->qrMatrix[$k][$k]; + for ($i = $k; $i < $this->rows; ++$i) { + $this->qrMatrix[$i][$j] += $s * $this->qrMatrix[$i][$k]; + } + } + } + $this->rDiagonal[$k] = -$norm; + } + } + + public function isFullRank(): bool + { + for ($j = 0; $j < $this->columns; ++$j) { + if ($this->rDiagonal[$j] == 0.0) { + return false; + } + } + + return true; + } + + /** + * Least squares solution of A*X = B. + * + * @param Matrix $B a Matrix with as many rows as A and any number of columns + * + * @throws Exception + * + * @return Matrix matrix that minimizes the two norm of Q*R*X-B + */ + public function solve(Matrix $B): Matrix + { + if ($B->rows !== $this->rows) { + throw new Exception('Matrix row dimensions are not equal'); + } + + if (!$this->isFullRank()) { + throw new Exception('Can only perform this operation on a full-rank matrix'); + } + + // Compute Y = transpose(Q)*B + $Y = $this->getQ()->transpose() + ->multiply($B); + // Solve R*X = Y; + return $this->getR()->inverse() + ->multiply($Y); + } +} diff --git a/vendor/markbaker/matrix/classes/src/Div0Exception.php b/vendor/markbaker/matrix/classes/src/Div0Exception.php new file mode 100644 index 0000000..4cb51d6 --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Div0Exception.php @@ -0,0 +1,13 @@ +isSquare()) { + throw new Exception('Adjoint can only be calculated for a square matrix'); + } + + return self::getAdjoint($matrix); + } + + /** + * Calculate the cofactors of the matrix + * + * @param Matrix $matrix The matrix whose cofactors we wish to calculate + * @return Matrix + * + * @throws Exception + */ + private static function getCofactors(Matrix $matrix) + { + $cofactors = self::getMinors($matrix); + $dimensions = $matrix->rows; + + $cof = 1; + for ($i = 0; $i < $dimensions; ++$i) { + $cofs = $cof; + for ($j = 0; $j < $dimensions; ++$j) { + $cofactors[$i][$j] *= $cofs; + $cofs = -$cofs; + } + $cof = -$cof; + } + + return new Matrix($cofactors); + } + + /** + * Return the cofactors of this matrix + * + * @param Matrix $matrix The matrix whose cofactors we wish to calculate + * @return Matrix + * + * @throws Exception + */ + public static function cofactors(Matrix $matrix) + { + if (!$matrix->isSquare()) { + throw new Exception('Cofactors can only be calculated for a square matrix'); + } + + return self::getCofactors($matrix); + } + + /** + * @param Matrix $matrix + * @param int $row + * @param int $column + * @return float + * @throws Exception + */ + private static function getDeterminantSegment(Matrix $matrix, $row, $column) + { + $tmpMatrix = $matrix->toArray(); + unset($tmpMatrix[$row]); + array_walk( + $tmpMatrix, + function (&$row) use ($column) { + unset($row[$column]); + } + ); + + return self::getDeterminant(new Matrix($tmpMatrix)); + } + + /** + * Calculate the determinant of the matrix + * + * @param Matrix $matrix The matrix whose determinant we wish to calculate + * @return float + * + * @throws Exception + */ + private static function getDeterminant(Matrix $matrix) + { + $dimensions = $matrix->rows; + $determinant = 0; + + switch ($dimensions) { + case 1: + $determinant = $matrix->getValue(1, 1); + break; + case 2: + $determinant = $matrix->getValue(1, 1) * $matrix->getValue(2, 2) - + $matrix->getValue(1, 2) * $matrix->getValue(2, 1); + break; + default: + for ($i = 1; $i <= $dimensions; ++$i) { + $det = $matrix->getValue(1, $i) * self::getDeterminantSegment($matrix, 0, $i - 1); + if (($i % 2) == 0) { + $determinant -= $det; + } else { + $determinant += $det; + } + } + break; + } + + return $determinant; + } + + /** + * Return the determinant of this matrix + * + * @param Matrix $matrix The matrix whose determinant we wish to calculate + * @return float + * @throws Exception + **/ + public static function determinant(Matrix $matrix) + { + if (!$matrix->isSquare()) { + throw new Exception('Determinant can only be calculated for a square matrix'); + } + + return self::getDeterminant($matrix); + } + + /** + * Return the diagonal of this matrix + * + * @param Matrix $matrix The matrix whose diagonal we wish to calculate + * @return Matrix + * @throws Exception + **/ + public static function diagonal(Matrix $matrix) + { + if (!$matrix->isSquare()) { + throw new Exception('Diagonal can only be extracted from a square matrix'); + } + + $dimensions = $matrix->rows; + $grid = Builder::createFilledMatrix(0, $dimensions, $dimensions) + ->toArray(); + + for ($i = 0; $i < $dimensions; ++$i) { + $grid[$i][$i] = $matrix->getValue($i + 1, $i + 1); + } + + return new Matrix($grid); + } + + /** + * Return the antidiagonal of this matrix + * + * @param Matrix $matrix The matrix whose antidiagonal we wish to calculate + * @return Matrix + * @throws Exception + **/ + public static function antidiagonal(Matrix $matrix) + { + if (!$matrix->isSquare()) { + throw new Exception('Anti-Diagonal can only be extracted from a square matrix'); + } + + $dimensions = $matrix->rows; + $grid = Builder::createFilledMatrix(0, $dimensions, $dimensions) + ->toArray(); + + for ($i = 0; $i < $dimensions; ++$i) { + $grid[$i][$dimensions - $i - 1] = $matrix->getValue($i + 1, $dimensions - $i); + } + + return new Matrix($grid); + } + + /** + * Return the identity matrix + * The identity matrix, or sometimes ambiguously called a unit matrix, of size n is the n × n square matrix + * with ones on the main diagonal and zeros elsewhere + * + * @param Matrix $matrix The matrix whose identity we wish to calculate + * @return Matrix + * @throws Exception + **/ + public static function identity(Matrix $matrix) + { + if (!$matrix->isSquare()) { + throw new Exception('Identity can only be created for a square matrix'); + } + + $dimensions = $matrix->rows; + + return Builder::createIdentityMatrix($dimensions); + } + + /** + * Return the inverse of this matrix + * + * @param Matrix $matrix The matrix whose inverse we wish to calculate + * @return Matrix + * @throws Exception + **/ + public static function inverse(Matrix $matrix, string $type = 'inverse') + { + if (!$matrix->isSquare()) { + throw new Exception(ucfirst($type) . ' can only be calculated for a square matrix'); + } + + $determinant = self::getDeterminant($matrix); + if ($determinant == 0.0) { + throw new Div0Exception(ucfirst($type) . ' can only be calculated for a matrix with a non-zero determinant'); + } + + if ($matrix->rows == 1) { + return new Matrix([[1 / $matrix->getValue(1, 1)]]); + } + + return self::getAdjoint($matrix) + ->multiply(1 / $determinant); + } + + /** + * Calculate the minors of the matrix + * + * @param Matrix $matrix The matrix whose minors we wish to calculate + * @return array[] + * + * @throws Exception + */ + protected static function getMinors(Matrix $matrix) + { + $minors = $matrix->toArray(); + $dimensions = $matrix->rows; + if ($dimensions == 1) { + return $minors; + } + + for ($i = 0; $i < $dimensions; ++$i) { + for ($j = 0; $j < $dimensions; ++$j) { + $minors[$i][$j] = self::getDeterminantSegment($matrix, $i, $j); + } + } + + return $minors; + } + + /** + * Return the minors of the matrix + * The minor of a matrix A is the determinant of some smaller square matrix, cut down from A by removing one or + * more of its rows or columns. + * Minors obtained by removing just one row and one column from square matrices (first minors) are required for + * calculating matrix cofactors, which in turn are useful for computing both the determinant and inverse of + * square matrices. + * + * @param Matrix $matrix The matrix whose minors we wish to calculate + * @return Matrix + * @throws Exception + **/ + public static function minors(Matrix $matrix) + { + if (!$matrix->isSquare()) { + throw new Exception('Minors can only be calculated for a square matrix'); + } + + return new Matrix(self::getMinors($matrix)); + } + + /** + * Return the trace of this matrix + * The trace is defined as the sum of the elements on the main diagonal (the diagonal from the upper left to the lower right) + * of the matrix + * + * @param Matrix $matrix The matrix whose trace we wish to calculate + * @return float + * @throws Exception + **/ + public static function trace(Matrix $matrix) + { + if (!$matrix->isSquare()) { + throw new Exception('Trace can only be extracted from a square matrix'); + } + + $dimensions = $matrix->rows; + $result = 0; + for ($i = 1; $i <= $dimensions; ++$i) { + $result += $matrix->getValue($i, $i); + } + + return $result; + } + + /** + * Return the transpose of this matrix + * + * @param Matrix $matrix The matrix whose transpose we wish to calculate + * @return Matrix + **/ + public static function transpose(Matrix $matrix) + { + $array = array_values(array_merge([null], $matrix->toArray())); + $grid = call_user_func_array( + 'array_map', + $array + ); + + return new Matrix($grid); + } +} diff --git a/vendor/markbaker/matrix/classes/src/Functions/adjoint.php b/vendor/markbaker/matrix/classes/src/Functions/adjoint.php new file mode 100644 index 0000000..aafbb0f --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Functions/adjoint.php @@ -0,0 +1,32 @@ +buildFromArray(array_values($grid)); + } + + /* + * Create a new Matrix object from an array of values + * + * @param array $grid + */ + protected function buildFromArray(array $grid): void + { + $this->rows = count($grid); + $columns = array_reduce( + $grid, + function ($carry, $value) { + return max($carry, is_array($value) ? count($value) : 1); + } + ); + $this->columns = $columns; + + array_walk( + $grid, + function (&$value) use ($columns) { + if (!is_array($value)) { + $value = [$value]; + } + $value = array_pad(array_values($value), $columns, null); + } + ); + + $this->grid = $grid; + } + + /** + * Validate that a row number is a positive integer + * + * @param int $row + * @return int + * @throws Exception + */ + public static function validateRow(int $row): int + { + if ((!is_numeric($row)) || (intval($row) < 1)) { + throw new Exception('Invalid Row'); + } + + return (int)$row; + } + + /** + * Validate that a column number is a positive integer + * + * @param int $column + * @return int + * @throws Exception + */ + public static function validateColumn(int $column): int + { + if ((!is_numeric($column)) || (intval($column) < 1)) { + throw new Exception('Invalid Column'); + } + + return (int)$column; + } + + /** + * Validate that a row number falls within the set of rows for this matrix + * + * @param int $row + * @return int + * @throws Exception + */ + protected function validateRowInRange(int $row): int + { + $row = static::validateRow($row); + if ($row > $this->rows) { + throw new Exception('Requested Row exceeds matrix size'); + } + + return $row; + } + + /** + * Validate that a column number falls within the set of columns for this matrix + * + * @param int $column + * @return int + * @throws Exception + */ + protected function validateColumnInRange(int $column): int + { + $column = static::validateColumn($column); + if ($column > $this->columns) { + throw new Exception('Requested Column exceeds matrix size'); + } + + return $column; + } + + /** + * Return a new matrix as a subset of rows from this matrix, starting at row number $row, and $rowCount rows + * A $rowCount value of 0 will return all rows of the matrix from $row + * A negative $rowCount value will return rows until that many rows from the end of the matrix + * + * Note that row numbers start from 1, not from 0 + * + * @param int $row + * @param int $rowCount + * @return static + * @throws Exception + */ + public function getRows(int $row, int $rowCount = 1): Matrix + { + $row = $this->validateRowInRange($row); + if ($rowCount === 0) { + $rowCount = $this->rows - $row + 1; + } + + return new static(array_slice($this->grid, $row - 1, (int)$rowCount)); + } + + /** + * Return a new matrix as a subset of columns from this matrix, starting at column number $column, and $columnCount columns + * A $columnCount value of 0 will return all columns of the matrix from $column + * A negative $columnCount value will return columns until that many columns from the end of the matrix + * + * Note that column numbers start from 1, not from 0 + * + * @param int $column + * @param int $columnCount + * @return Matrix + * @throws Exception + */ + public function getColumns(int $column, int $columnCount = 1): Matrix + { + $column = $this->validateColumnInRange($column); + if ($columnCount < 1) { + $columnCount = $this->columns + $columnCount - $column + 1; + } + + $grid = []; + for ($i = $column - 1; $i < $column + $columnCount - 1; ++$i) { + $grid[] = array_column($this->grid, $i); + } + + return (new static($grid))->transpose(); + } + + /** + * Return a new matrix as a subset of rows from this matrix, dropping rows starting at row number $row, + * and $rowCount rows + * A negative $rowCount value will drop rows until that many rows from the end of the matrix + * A $rowCount value of 0 will remove all rows of the matrix from $row + * + * Note that row numbers start from 1, not from 0 + * + * @param int $row + * @param int $rowCount + * @return static + * @throws Exception + */ + public function dropRows(int $row, int $rowCount = 1): Matrix + { + $this->validateRowInRange($row); + if ($rowCount === 0) { + $rowCount = $this->rows - $row + 1; + } + + $grid = $this->grid; + array_splice($grid, $row - 1, (int)$rowCount); + + return new static($grid); + } + + /** + * Return a new matrix as a subset of columns from this matrix, dropping columns starting at column number $column, + * and $columnCount columns + * A negative $columnCount value will drop columns until that many columns from the end of the matrix + * A $columnCount value of 0 will remove all columns of the matrix from $column + * + * Note that column numbers start from 1, not from 0 + * + * @param int $column + * @param int $columnCount + * @return static + * @throws Exception + */ + public function dropColumns(int $column, int $columnCount = 1): Matrix + { + $this->validateColumnInRange($column); + if ($columnCount < 1) { + $columnCount = $this->columns + $columnCount - $column + 1; + } + + $grid = $this->grid; + array_walk( + $grid, + function (&$row) use ($column, $columnCount) { + array_splice($row, $column - 1, (int)$columnCount); + } + ); + + return new static($grid); + } + + /** + * Return a value from this matrix, from the "cell" identified by the row and column numbers + * Note that row and column numbers start from 1, not from 0 + * + * @param int $row + * @param int $column + * @return mixed + * @throws Exception + */ + public function getValue(int $row, int $column) + { + $row = $this->validateRowInRange($row); + $column = $this->validateColumnInRange($column); + + return $this->grid[$row - 1][$column - 1]; + } + + /** + * Returns a Generator that will yield each row of the matrix in turn as a vector matrix + * or the value of each cell if the matrix is a column vector + * + * @return Generator|Matrix[]|mixed[] + */ + public function rows(): Generator + { + foreach ($this->grid as $i => $row) { + yield $i + 1 => ($this->columns == 1) + ? $row[0] + : new static([$row]); + } + } + + /** + * Returns a Generator that will yield each column of the matrix in turn as a vector matrix + * or the value of each cell if the matrix is a row vector + * + * @return Generator|Matrix[]|mixed[] + */ + public function columns(): Generator + { + for ($i = 0; $i < $this->columns; ++$i) { + yield $i + 1 => ($this->rows == 1) + ? $this->grid[0][$i] + : new static(array_column($this->grid, $i)); + } + } + + /** + * Identify if the row and column dimensions of this matrix are equal, + * i.e. if it is a "square" matrix + * + * @return bool + */ + public function isSquare(): bool + { + return $this->rows === $this->columns; + } + + /** + * Identify if this matrix is a vector + * i.e. if it comprises only a single row or a single column + * + * @return bool + */ + public function isVector(): bool + { + return $this->rows === 1 || $this->columns === 1; + } + + /** + * Return the matrix as a 2-dimensional array + * + * @return array + */ + public function toArray(): array + { + return $this->grid; + } + + /** + * Solve A*X = B. + * + * @param Matrix $B Right hand side + * + * @throws Exception + * + * @return Matrix ... Solution if A is square, least squares solution otherwise + */ + public function solve(Matrix $B): Matrix + { + if ($this->columns === $this->rows) { + return (new LU($this))->solve($B); + } + + return (new QR($this))->solve($B); + } + + protected static $getters = [ + 'rows', + 'columns', + ]; + + /** + * Access specific properties as read-only (no setters) + * + * @param string $propertyName + * @return mixed + * @throws Exception + */ + public function __get(string $propertyName) + { + $propertyName = strtolower($propertyName); + + // Test for function calls + if (in_array($propertyName, self::$getters)) { + return $this->$propertyName; + } + + throw new Exception('Property does not exist'); + } + + protected static $functions = [ + 'adjoint', + 'antidiagonal', + 'cofactors', + 'determinant', + 'diagonal', + 'identity', + 'inverse', + 'minors', + 'trace', + 'transpose', + ]; + + protected static $operations = [ + 'add', + 'subtract', + 'multiply', + 'divideby', + 'divideinto', + 'directsum', + ]; + + /** + * Returns the result of the function call or operation + * + * @param string $functionName + * @param mixed[] $arguments + * @return Matrix|float + * @throws Exception + */ + public function __call(string $functionName, $arguments) + { + $functionName = strtolower(str_replace('_', '', $functionName)); + + if (in_array($functionName, self::$functions, true) || in_array($functionName, self::$operations, true)) { + $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}"; + if (is_callable($functionName)) { + $arguments = array_values(array_merge([$this], $arguments)); + return call_user_func_array($functionName, $arguments); + } + } + throw new Exception('Function or Operation does not exist'); + } +} diff --git a/vendor/markbaker/matrix/classes/src/Operations/add.php b/vendor/markbaker/matrix/classes/src/Operations/add.php new file mode 100644 index 0000000..5ac9a5e --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Operations/add.php @@ -0,0 +1,46 @@ + $matrixValues The matrices to add + * @return Matrix + * @throws Exception + */ +if (!function_exists(__NAMESPACE__ . '\\add')) { + function add(...$matrixValues): Matrix + { + if (count($matrixValues) < 2) { + throw new Exception('Addition operation requires at least 2 arguments'); + } + + $matrix = array_shift($matrixValues); + + if (is_array($matrix)) { + $matrix = new Matrix($matrix); + } + if (!$matrix instanceof Matrix) { + throw new Exception('Addition arguments must be Matrix or array'); + } + + $result = new Addition($matrix); + + foreach ($matrixValues as $matrix) { + $result->execute($matrix); + } + + return $result->result(); + } +} diff --git a/vendor/markbaker/matrix/classes/src/Operations/directsum.php b/vendor/markbaker/matrix/classes/src/Operations/directsum.php new file mode 100644 index 0000000..21c0c62 --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Operations/directsum.php @@ -0,0 +1,46 @@ + $matrixValues The matrices to add + * @return Matrix + * @throws Exception + */ +if (!function_exists(__NAMESPACE__ . '\\directsum')) { + function directsum(...$matrixValues): Matrix + { + if (count($matrixValues) < 2) { + throw new Exception('DirectSum operation requires at least 2 arguments'); + } + + $matrix = array_shift($matrixValues); + + if (is_array($matrix)) { + $matrix = new Matrix($matrix); + } + if (!$matrix instanceof Matrix) { + throw new Exception('DirectSum arguments must be Matrix or array'); + } + + $result = new DirectSum($matrix); + + foreach ($matrixValues as $matrix) { + $result->execute($matrix); + } + + return $result->result(); + } +} diff --git a/vendor/markbaker/matrix/classes/src/Operations/divideby.php b/vendor/markbaker/matrix/classes/src/Operations/divideby.php new file mode 100644 index 0000000..5dd0b19 --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Operations/divideby.php @@ -0,0 +1,46 @@ + $matrixValues The matrices to divide + * @return Matrix + * @throws Exception + */ +if (!function_exists(__NAMESPACE__ . '\\divideby')) { + function divideby(...$matrixValues): Matrix + { + if (count($matrixValues) < 2) { + throw new Exception('Division operation requires at least 2 arguments'); + } + + $matrix = array_shift($matrixValues); + + if (is_array($matrix)) { + $matrix = new Matrix($matrix); + } + if (!$matrix instanceof Matrix) { + throw new Exception('Division arguments must be Matrix or array'); + } + + $result = new Division($matrix); + + foreach ($matrixValues as $matrix) { + $result->execute($matrix); + } + + return $result->result(); + } +} diff --git a/vendor/markbaker/matrix/classes/src/Operations/divideinto.php b/vendor/markbaker/matrix/classes/src/Operations/divideinto.php new file mode 100644 index 0000000..dfbb2df --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Operations/divideinto.php @@ -0,0 +1,47 @@ + $matrixValues The numbers to divide + * @return Matrix + * @throws Exception + */ +if (!function_exists(__NAMESPACE__ . '\\divideinto')) { + function divideinto(...$matrixValues): Matrix + { + if (count($matrixValues) < 2) { + throw new Exception('Division operation requires at least 2 arguments'); + } + + $matrix = array_pop($matrixValues); + $matrixValues = array_reverse($matrixValues); + + if (is_array($matrix)) { + $matrix = new Matrix($matrix); + } + if (!$matrix instanceof Matrix) { + throw new Exception('Division arguments must be Matrix or array'); + } + + $result = new Division($matrix); + + foreach ($matrixValues as $matrix) { + $result->execute($matrix); + } + + return $result->result(); + } +} diff --git a/vendor/markbaker/matrix/classes/src/Operations/multiply.php b/vendor/markbaker/matrix/classes/src/Operations/multiply.php new file mode 100644 index 0000000..45ecc77 --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Operations/multiply.php @@ -0,0 +1,46 @@ + $matrixValues The matrices to multiply + * @return Matrix + * @throws Exception + */ +if (!function_exists(__NAMESPACE__ . '\\multiply')) { + function multiply(...$matrixValues): Matrix + { + if (count($matrixValues) < 2) { + throw new Exception('Multiplication operation requires at least 2 arguments'); + } + + $matrix = array_shift($matrixValues); + + if (is_array($matrix)) { + $matrix = new Matrix($matrix); + } + if (!$matrix instanceof Matrix) { + throw new Exception('Multiplication arguments must be Matrix or array'); + } + + $result = new Multiplication($matrix); + + foreach ($matrixValues as $matrix) { + $result->execute($matrix); + } + + return $result->result(); + } +} diff --git a/vendor/markbaker/matrix/classes/src/Operations/subtract.php b/vendor/markbaker/matrix/classes/src/Operations/subtract.php new file mode 100644 index 0000000..72759b7 --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Operations/subtract.php @@ -0,0 +1,46 @@ + $matrixValues The matrices to subtract + * @return Matrix + * @throws Exception + */ +if (!function_exists(__NAMESPACE__ . '\\subtract')) { + function subtract(...$matrixValues): Matrix + { + if (count($matrixValues) < 2) { + throw new Exception('Subtraction operation requires at least 2 arguments'); + } + + $matrix = array_shift($matrixValues); + + if (is_array($matrix)) { + $matrix = new Matrix($matrix); + } + if (!$matrix instanceof Matrix) { + throw new Exception('Subtraction arguments must be Matrix or array'); + } + + $result = new Subtraction($matrix); + + foreach ($matrixValues as $matrix) { + $result->execute($matrix); + } + + return $result->result(); + } +} diff --git a/vendor/markbaker/matrix/classes/src/Operators/Addition.php b/vendor/markbaker/matrix/classes/src/Operators/Addition.php new file mode 100644 index 0000000..87bd342 --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Operators/Addition.php @@ -0,0 +1,68 @@ +addMatrix($value); + } elseif (is_numeric($value)) { + return $this->addScalar($value); + } + + throw new Exception('Invalid argument for addition'); + } + + /** + * Execute the addition for a scalar + * + * @param mixed $value The numeric value to add to the current base value + * @return $this The operation object, allowing multiple additions to be chained + **/ + protected function addScalar($value): Operator + { + for ($row = 0; $row < $this->rows; ++$row) { + for ($column = 0; $column < $this->columns; ++$column) { + $this->matrix[$row][$column] += $value; + } + } + + return $this; + } + + /** + * Execute the addition for a matrix + * + * @param Matrix $value The numeric value to add to the current base value + * @return $this The operation object, allowing multiple additions to be chained + * @throws Exception If the provided argument is not appropriate for the operation + **/ + protected function addMatrix(Matrix $value): Operator + { + $this->validateMatchingDimensions($value); + + for ($row = 0; $row < $this->rows; ++$row) { + for ($column = 0; $column < $this->columns; ++$column) { + $this->matrix[$row][$column] += $value->getValue($row + 1, $column + 1); + } + } + + return $this; + } +} diff --git a/vendor/markbaker/matrix/classes/src/Operators/DirectSum.php b/vendor/markbaker/matrix/classes/src/Operators/DirectSum.php new file mode 100644 index 0000000..e729a43 --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Operators/DirectSum.php @@ -0,0 +1,64 @@ +directSumMatrix($value); + } + + throw new Exception('Invalid argument for addition'); + } + + /** + * Execute the direct sum for a matrix + * + * @param Matrix $value The numeric value to concatenate/direct sum with the current base value + * @return $this The operation object, allowing multiple additions to be chained + **/ + private function directSumMatrix($value): Operator + { + $originalColumnCount = count($this->matrix[0]); + $originalRowCount = count($this->matrix); + $valColumnCount = $value->columns; + $valRowCount = $value->rows; + $value = $value->toArray(); + + for ($row = 0; $row < $this->rows; ++$row) { + $this->matrix[$row] = array_merge($this->matrix[$row], array_fill(0, $valColumnCount, 0)); + } + + $this->matrix = array_merge( + $this->matrix, + array_fill(0, $valRowCount, array_fill(0, $originalColumnCount, 0)) + ); + + for ($row = $originalRowCount; $row < $originalRowCount + $valRowCount; ++$row) { + array_splice( + $this->matrix[$row], + $originalColumnCount, + $valColumnCount, + $value[$row - $originalRowCount] + ); + } + + return $this; + } +} diff --git a/vendor/markbaker/matrix/classes/src/Operators/Division.php b/vendor/markbaker/matrix/classes/src/Operators/Division.php new file mode 100644 index 0000000..3cdedfb --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Operators/Division.php @@ -0,0 +1,35 @@ +multiplyMatrix($value, $type); + } elseif (is_numeric($value)) { + return $this->multiplyScalar(1 / $value, $type); + } + + throw new Exception('Invalid argument for division'); + } +} diff --git a/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php b/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php new file mode 100644 index 0000000..13d14f0 --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php @@ -0,0 +1,86 @@ +multiplyMatrix($value, $type); + } elseif (is_numeric($value)) { + return $this->multiplyScalar($value, $type); + } + + throw new Exception("Invalid argument for $type"); + } + + /** + * Execute the multiplication for a scalar + * + * @param mixed $value The numeric value to multiply with the current base value + * @return $this The operation object, allowing multiple mutiplications to be chained + **/ + protected function multiplyScalar($value, string $type = 'multiplication'): Operator + { + try { + for ($row = 0; $row < $this->rows; ++$row) { + for ($column = 0; $column < $this->columns; ++$column) { + $this->matrix[$row][$column] *= $value; + } + } + } catch (Throwable $e) { + throw new Exception("Invalid argument for $type"); + } + + return $this; + } + + /** + * Execute the multiplication for a matrix + * + * @param Matrix $value The numeric value to multiply with the current base value + * @return $this The operation object, allowing multiple mutiplications to be chained + * @throws Exception If the provided argument is not appropriate for the operation + **/ + protected function multiplyMatrix(Matrix $value, string $type = 'multiplication'): Operator + { + $this->validateReflectingDimensions($value); + + $newRows = $this->rows; + $newColumns = $value->columns; + $matrix = Builder::createFilledMatrix(0, $newRows, $newColumns) + ->toArray(); + try { + for ($row = 0; $row < $newRows; ++$row) { + for ($column = 0; $column < $newColumns; ++$column) { + $columnData = $value->getColumns($column + 1)->toArray(); + foreach ($this->matrix[$row] as $key => $valueData) { + $matrix[$row][$column] += $valueData * $columnData[$key][0]; + } + } + } + } catch (Throwable $e) { + throw new Exception("Invalid argument for $type"); + } + $this->matrix = $matrix; + + return $this; + } +} diff --git a/vendor/markbaker/matrix/classes/src/Operators/Operator.php b/vendor/markbaker/matrix/classes/src/Operators/Operator.php new file mode 100644 index 0000000..b7f4025 --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Operators/Operator.php @@ -0,0 +1,78 @@ +rows = $matrix->rows; + $this->columns = $matrix->columns; + $this->matrix = $matrix->toArray(); + } + + /** + * Compare the dimensions of the matrices being operated on to see if they are valid for addition/subtraction + * + * @param Matrix $matrix The second Matrix object on which the operation will be performed + * @throws Exception + */ + protected function validateMatchingDimensions(Matrix $matrix): void + { + if (($this->rows != $matrix->rows) || ($this->columns != $matrix->columns)) { + throw new Exception('Matrices have mismatched dimensions'); + } + } + + /** + * Compare the dimensions of the matrices being operated on to see if they are valid for multiplication/division + * + * @param Matrix $matrix The second Matrix object on which the operation will be performed + * @throws Exception + */ + protected function validateReflectingDimensions(Matrix $matrix): void + { + if ($this->columns != $matrix->rows) { + throw new Exception('Matrices have mismatched dimensions'); + } + } + + /** + * Return the result of the operation + * + * @return Matrix + */ + public function result(): Matrix + { + return new Matrix($this->matrix); + } +} diff --git a/vendor/markbaker/matrix/classes/src/Operators/Subtraction.php b/vendor/markbaker/matrix/classes/src/Operators/Subtraction.php new file mode 100644 index 0000000..14b3541 --- /dev/null +++ b/vendor/markbaker/matrix/classes/src/Operators/Subtraction.php @@ -0,0 +1,68 @@ +subtractMatrix($value); + } elseif (is_numeric($value)) { + return $this->subtractScalar($value); + } + + throw new Exception('Invalid argument for subtraction'); + } + + /** + * Execute the subtraction for a scalar + * + * @param mixed $value The numeric value to subtracted from the current base value + * @return $this The operation object, allowing multiple additions to be chained + **/ + protected function subtractScalar($value): Operator + { + for ($row = 0; $row < $this->rows; ++$row) { + for ($column = 0; $column < $this->columns; ++$column) { + $this->matrix[$row][$column] -= $value; + } + } + + return $this; + } + + /** + * Execute the subtraction for a matrix + * + * @param Matrix $value The numeric value to subtract from the current base value + * @return $this The operation object, allowing multiple subtractions to be chained + * @throws Exception If the provided argument is not appropriate for the operation + **/ + protected function subtractMatrix(Matrix $value): Operator + { + $this->validateMatchingDimensions($value); + + for ($row = 0; $row < $this->rows; ++$row) { + for ($column = 0; $column < $this->columns; ++$column) { + $this->matrix[$row][$column] -= $value->getValue($row + 1, $column + 1); + } + } + + return $this; + } +} diff --git a/vendor/markbaker/matrix/composer.json b/vendor/markbaker/matrix/composer.json new file mode 100644 index 0000000..22c9be5 --- /dev/null +++ b/vendor/markbaker/matrix/composer.json @@ -0,0 +1,89 @@ +{ + "name": "markbaker/matrix", + "type": "library", + "description": "PHP Class for working with matrices", + "keywords": ["matrix", "vector", "mathematics"], + "homepage": "https://github.com/MarkBaker/PHPMatrix", + "license": "MIT", + "authors": [ + { + "name": "Mark Baker", + "email": "mark@demon-angel.eu" + } + ], + "config": { + "sort-packages": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "phpdocumentor/phpdocumentor": "2.*", + "phpmd/phpmd": "2.*", + "sebastian/phpcpd": "^4.0", + "phploc/phploc": "^4.0", + "squizlabs/php_codesniffer": "^3.7", + "phpcompatibility/php-compatibility": "^9.3", + "dealerdirect/phpcodesniffer-composer-installer": "dev-master" + }, + "autoload": { + "psr-4": { + "Matrix\\": "classes/src/" + }, + "files": [ + "classes/src/Functions/adjoint.php", + "classes/src/Functions/antidiagonal.php", + "classes/src/Functions/cofactors.php", + "classes/src/Functions/determinant.php", + "classes/src/Functions/diagonal.php", + "classes/src/Functions/identity.php", + "classes/src/Functions/inverse.php", + "classes/src/Functions/minors.php", + "classes/src/Functions/trace.php", + "classes/src/Functions/transpose.php", + "classes/src/Operations/add.php", + "classes/src/Operations/directsum.php", + "classes/src/Operations/subtract.php", + "classes/src/Operations/multiply.php", + "classes/src/Operations/divideby.php", + "classes/src/Operations/divideinto.php" + ] + }, + "autoload-dev": { + "psr-4": { + "MatrixTest\\": "unitTests/classes/src/" + }, + "files": [ + "classes/src/Functions/adjoint.php", + "classes/src/Functions/antidiagonal.php", + "classes/src/Functions/cofactors.php", + "classes/src/Functions/determinant.php", + "classes/src/Functions/diagonal.php", + "classes/src/Functions/identity.php", + "classes/src/Functions/inverse.php", + "classes/src/Functions/minors.php", + "classes/src/Functions/trace.php", + "classes/src/Functions/transpose.php", + "classes/src/Operations/add.php", + "classes/src/Operations/directsum.php", + "classes/src/Operations/subtract.php", + "classes/src/Operations/multiply.php", + "classes/src/Operations/divideby.php", + "classes/src/Operations/divideinto.php" + ] + }, + "scripts": { + "style": "phpcs --report-width=200 --standard=PSR2 --report=summary,full classes/src/ unitTests/classes/src -n", + "test": "phpunit -c phpunit.xml.dist", + "mess": "phpmd classes/src/ xml codesize,unusedcode,design,naming -n", + "lines": "phploc classes/src/ -n", + "cpd": "phpcpd classes/src/ -n", + "versions": "phpcs --report-width=200 --standard=PHPCompatibility --report=summary,full classes/src/ --runtime-set testVersion 7.2- -n", + "coverage": "phpunit -c phpunit.xml.dist --coverage-text --coverage-html ./build/coverage" + }, + "minimum-stability": "dev" +} \ No newline at end of file diff --git a/vendor/markbaker/matrix/examples/test.php b/vendor/markbaker/matrix/examples/test.php new file mode 100644 index 0000000..001f9c6 --- /dev/null +++ b/vendor/markbaker/matrix/examples/test.php @@ -0,0 +1,33 @@ +solve($target); + +echo 'X', PHP_EOL; +var_export($X->toArray()); +echo PHP_EOL; + +$resolve = $matrix->multiply($X); + +echo 'Resolve', PHP_EOL; +var_export($resolve->toArray()); +echo PHP_EOL; diff --git a/vendor/markbaker/matrix/infection.json.dist b/vendor/markbaker/matrix/infection.json.dist new file mode 100644 index 0000000..ec3b645 --- /dev/null +++ b/vendor/markbaker/matrix/infection.json.dist @@ -0,0 +1,17 @@ +{ + "timeout": 1, + "source": { + "directories": [ + "classes\/src" + ] + }, + "logs": { + "text": "build/infection/text.log", + "summary": "build/infection/summary.log", + "debug": "build/infection/debug.log", + "perMutator": "build/infection/perMutator.md" + }, + "mutators": { + "@default": true + } +} diff --git a/vendor/markbaker/matrix/license.md b/vendor/markbaker/matrix/license.md new file mode 100644 index 0000000..491b1cc --- /dev/null +++ b/vendor/markbaker/matrix/license.md @@ -0,0 +1,25 @@ +The MIT License (MIT) +===================== + +Copyright © `2018` `Mark Baker` + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vendor/markbaker/matrix/phpstan.neon b/vendor/markbaker/matrix/phpstan.neon new file mode 100644 index 0000000..8e254ef --- /dev/null +++ b/vendor/markbaker/matrix/phpstan.neon @@ -0,0 +1,6 @@ +parameters: + ignoreErrors: + - '#Property [A-Za-z\\]+::\$[A-Za-z]+ has no typehint specified#' + - '#Method [A-Za-z\\]+::[A-Za-z]+\(\) has no return typehint specified#' + - '#Method [A-Za-z\\]+::[A-Za-z]+\(\) has parameter \$[A-Za-z0-9]+ with no typehint specified#' + checkMissingIterableValueType: false -- cgit v1.2.3