y 3 年之前
父節點
當前提交
6d580e2b69
共有 100 個文件被更改,包括 29403 次插入1 次删除
  1. 0 1
      .gitignore
  2. 19 0
      vendor/asm89/stack-cors/LICENSE
  3. 83 0
      vendor/asm89/stack-cors/README.md
  4. 43 0
      vendor/asm89/stack-cors/composer.json
  5. 61 0
      vendor/asm89/stack-cors/src/Cors.php
  6. 225 0
      vendor/asm89/stack-cors/src/CorsService.php
  7. 7 0
      vendor/autoload.php
  8. 117 0
      vendor/bin/carbon
  9. 5 0
      vendor/bin/carbon.bat
  10. 117 0
      vendor/bin/commonmark
  11. 5 0
      vendor/bin/commonmark.bat
  12. 117 0
      vendor/bin/php-parse
  13. 5 0
      vendor/bin/php-parse.bat
  14. 120 0
      vendor/bin/phpunit
  15. 5 0
      vendor/bin/phpunit.bat
  16. 117 0
      vendor/bin/psysh
  17. 5 0
      vendor/bin/psysh.bat
  18. 37 0
      vendor/bin/sail
  19. 5 0
      vendor/bin/sail.bat
  20. 117 0
      vendor/bin/var-dump-server
  21. 5 0
      vendor/bin/var-dump-server.bat
  22. 403 0
      vendor/brick/math/CHANGELOG.md
  23. 20 0
      vendor/brick/math/LICENSE
  24. 17 0
      vendor/brick/math/SECURITY.md
  25. 35 0
      vendor/brick/math/composer.json
  26. 194 0
      vendor/brick/math/random-tests.php
  27. 861 0
      vendor/brick/math/src/BigDecimal.php
  28. 1151 0
      vendor/brick/math/src/BigInteger.php
  29. 572 0
      vendor/brick/math/src/BigNumber.php
  30. 489 0
      vendor/brick/math/src/BigRational.php
  31. 41 0
      vendor/brick/math/src/Exception/DivisionByZeroException.php
  32. 27 0
      vendor/brick/math/src/Exception/IntegerOverflowException.php
  33. 14 0
      vendor/brick/math/src/Exception/MathException.php
  34. 12 0
      vendor/brick/math/src/Exception/NegativeNumberException.php
  35. 35 0
      vendor/brick/math/src/Exception/NumberFormatException.php
  36. 21 0
      vendor/brick/math/src/Exception/RoundingNecessaryException.php
  37. 759 0
      vendor/brick/math/src/Internal/Calculator.php
  38. 116 0
      vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php
  39. 156 0
      vendor/brick/math/src/Internal/Calculator/GmpCalculator.php
  40. 634 0
      vendor/brick/math/src/Internal/Calculator/NativeCalculator.php
  41. 107 0
      vendor/brick/math/src/RoundingMode.php
  42. 572 0
      vendor/composer/ClassLoader.php
  43. 350 0
      vendor/composer/InstalledVersions.php
  44. 21 0
      vendor/composer/LICENSE
  45. 4709 0
      vendor/composer/autoload_classmap.php
  46. 38 0
      vendor/composer/autoload_files.php
  47. 10 0
      vendor/composer/autoload_namespaces.php
  48. 86 0
      vendor/composer/autoload_psr4.php
  49. 80 0
      vendor/composer/autoload_real.php
  50. 5215 0
      vendor/composer/autoload_static.php
  51. 7678 0
      vendor/composer/installed.json
  52. 1221 0
      vendor/composer/installed.php
  53. 26 0
      vendor/composer/platform_check.php
  54. 19 0
      vendor/doctrine/inflector/LICENSE
  55. 8 0
      vendor/doctrine/inflector/README.md
  56. 40 0
      vendor/doctrine/inflector/composer.json
  57. 226 0
      vendor/doctrine/inflector/docs/en/index.rst
  58. 24 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/CachedWordInflector.php
  59. 65 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/GenericLanguageInflectorFactory.php
  60. 506 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Inflector.php
  61. 45 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/InflectorFactory.php
  62. 19 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Language.php
  63. 33 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/LanguageInflectorFactory.php
  64. 13 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/NoopWordInflector.php
  65. 182 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Inflectible.php
  66. 21 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/InflectorFactory.php
  67. 31 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Rules.php
  68. 193 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Uninflected.php
  69. 49 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Inflectible.php
  70. 21 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/InflectorFactory.php
  71. 31 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Rules.php
  72. 34 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Uninflected.php
  73. 40 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Inflectible.php
  74. 21 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/InflectorFactory.php
  75. 31 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Rules.php
  76. 36 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Uninflected.php
  77. 42 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Pattern.php
  78. 34 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Patterns.php
  79. 104 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Inflectible.php
  80. 21 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/InflectorFactory.php
  81. 31 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Rules.php
  82. 38 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Uninflected.php
  83. 39 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Ruleset.php
  84. 53 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Inflectible.php
  85. 21 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/InflectorFactory.php
  86. 31 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Rules.php
  87. 36 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Uninflected.php
  88. 30 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Substitution.php
  89. 56 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Substitutions.php
  90. 38 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformation.php
  91. 29 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformations.php
  92. 40 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Inflectible.php
  93. 21 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/InflectorFactory.php
  94. 31 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Rules.php
  95. 36 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Uninflected.php
  96. 21 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Word.php
  97. 55 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/RulesetInflector.php
  98. 10 0
      vendor/doctrine/inflector/lib/Doctrine/Inflector/WordInflector.php
  99. 13 0
      vendor/doctrine/inflector/phpstan.neon.dist
  100. 0 0
      vendor/doctrine/instantiator/.doctrine-project.json

+ 0 - 1
.gitignore

@@ -2,7 +2,6 @@
 /public/hot
 /public/storage
 /storage/*.key
-/vendor
 .env
 .env.backup
 .phpunit.result.cache

+ 19 - 0
vendor/asm89/stack-cors/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2013-2017 Alexander <iam.asm89@gmail.com>
+
+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.

+ 83 - 0
vendor/asm89/stack-cors/README.md

@@ -0,0 +1,83 @@
+# Stack/Cors
+
+Library and middleware enabling cross-origin resource sharing for your
+http-{foundation,kernel} using application. It attempts to implement the
+[W3C Recommendation] for cross-origin resource sharing.
+
+[W3C Recommendation]: http://www.w3.org/TR/cors/
+
+Build status: ![.github/workflows/run-tests.yml](https://github.com/asm89/stack-cors/workflows/.github/workflows/run-tests.yml/badge.svg)
+
+## Installation
+
+Require `asm89/stack-cors` using composer.
+
+## Usage
+
+This package can be used as a library or as [stack middleware].
+
+[stack middleware]: http://stackphp.com/
+
+### Options
+
+| Option                 | Description                                                | Default value |
+|------------------------|------------------------------------------------------------|---------------|
+| allowedMethods         | Matches the request method.                                | `array()`     |
+| allowedOrigins         | Matches the request origin.                                | `array()`     |
+| allowedOriginsPatterns | Matches the request origin with `preg_match`.              | `array()`  |
+| allowedHeaders         | Sets the Access-Control-Allow-Headers response header.     | `array()`     |
+| exposedHeaders         | Sets the Access-Control-Expose-Headers response header.    | `false`       |
+| maxAge                 | Sets the Access-Control-Max-Age response header.           | `false`       |
+| supportsCredentials    | Sets the Access-Control-Allow-Credentials header.          | `false`       |
+
+The _allowedMethods_ and _allowedHeaders_ options are case-insensitive.
+
+You don't need to provide both _allowedOrigins_ and _allowedOriginsPatterns_. If one of the strings passed matches, it is considered a valid origin.
+
+If `array('*')` is provided to _allowedMethods_, _allowedOrigins_ or _allowedHeaders_ all methods / origins / headers are allowed.
+
+### Example: using the library
+
+```php
+<?php
+
+use Asm89\Stack\CorsService;
+
+$cors = new CorsService(array(
+    'allowedHeaders'         => array('x-allowed-header', 'x-other-allowed-header'),
+    'allowedMethods'         => array('DELETE', 'GET', 'POST', 'PUT'),
+    'allowedOrigins'         => array('http://localhost'),
+    'allowedOriginsPatterns' => array('/localhost:\d/'),
+    'exposedHeaders'         => false,
+    'maxAge'                 => false,
+    'supportsCredentials'    => false,
+));
+
+$cors->addActualRequestHeaders(Response $response, $origin);
+$cors->handlePreflightRequest(Request $request);
+$cors->isActualRequestAllowed(Request $request);
+$cors->isCorsRequest(Request $request);
+$cors->isPreflightRequest(Request $request);
+```
+
+## Example: using the stack middleware
+
+```php
+<?php
+
+use Asm89\Stack\Cors;
+
+$app = new Cors($app, array(
+    // you can use array('*') to allow any headers
+    'allowedHeaders'      => array('x-allowed-header', 'x-other-allowed-header'),
+    // you can use array('*') to allow any methods
+    'allowedMethods'      => array('DELETE', 'GET', 'POST', 'PUT'),
+    // you can use array('*') to allow requests from any origin
+    'allowedOrigins'      => array('localhost'),
+    // you can enter regexes that are matched to the origin request header
+    'allowedOriginsPatterns' => array('/localhost:\d/'),
+    'exposedHeaders'      => false,
+    'maxAge'              => false,
+    'supportsCredentials' => false,
+));
+```

+ 43 - 0
vendor/asm89/stack-cors/composer.json

@@ -0,0 +1,43 @@
+{
+    "name": "asm89/stack-cors",
+    "description": "Cross-origin resource sharing library and stack middleware",
+    "keywords": ["stack", "cors"],
+    "homepage": "https://github.com/asm89/stack-cors",
+    "type": "library",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Alexander",
+            "email": "iam.asm89@gmail.com"
+        }
+    ],
+    "require": {
+        "php": "^7.0|^8.0",
+        "symfony/http-foundation": "~2.7|~3.0|~4.0|~5.0",
+        "symfony/http-kernel": "~2.7|~3.0|~4.0|~5.0"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^6|^7|^8|^9",
+        "squizlabs/php_codesniffer": "^3.5"
+    },
+    "autoload": {
+        "psr-4": {
+            "Asm89\\Stack\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Asm89\\Stack\\Tests\\": "tests/"
+        }
+    },
+    "scripts": {
+        "test": "phpunit",
+        "check-style": "phpcs -p --standard=PSR12 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src",
+        "fix-style": "phpcbf -p --standard=PSR12 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src"
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.0-dev"
+        }
+    }
+}

+ 61 - 0
vendor/asm89/stack-cors/src/Cors.php

@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of asm89/stack-cors.
+ *
+ * (c) Alexander <iam.asm89@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Asm89\Stack;
+
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+class Cors implements HttpKernelInterface
+{
+    /**
+     * @var \Symfony\Component\HttpKernel\HttpKernelInterface
+     */
+    private $app;
+
+    /**
+     * @var \Asm89\Stack\CorsService
+     */
+    private $cors;
+
+    private $defaultOptions = array(
+        'allowedHeaders'         => array(),
+        'allowedMethods'         => array(),
+        'allowedOrigins'         => array(),
+        'allowedOriginsPatterns' => array(),
+        'exposedHeaders'         => array(),
+        'maxAge'                 => 0,
+        'supportsCredentials'    => false,
+    );
+
+    public function __construct(HttpKernelInterface $app, array $options = array())
+    {
+        $this->app  = $app;
+        $this->cors = new CorsService(array_merge($this->defaultOptions, $options));
+    }
+
+    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
+    {
+        if ($this->cors->isPreflightRequest($request)) {
+            $response = $this->cors->handlePreflightRequest($request);
+            return $this->cors->varyHeader($response, 'Access-Control-Request-Method');
+        }
+
+        $response = $this->app->handle($request, $type, $catch);
+
+        if ($request->getMethod() === 'OPTIONS') {
+            $this->cors->varyHeader($response, 'Access-Control-Request-Method');
+        }
+
+        return $this->cors->addActualRequestHeaders($response, $request);
+    }
+}

+ 225 - 0
vendor/asm89/stack-cors/src/CorsService.php

@@ -0,0 +1,225 @@
+<?php
+
+/*
+ * This file is part of asm89/stack-cors.
+ *
+ * (c) Alexander <iam.asm89@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Asm89\Stack;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+class CorsService
+{
+    private $options;
+
+    public function __construct(array $options = array())
+    {
+        $this->options = $this->normalizeOptions($options);
+    }
+
+    private function normalizeOptions(array $options = array()): array
+    {
+        $options += array(
+            'allowedOrigins' => array(),
+            'allowedOriginsPatterns' => array(),
+            'supportsCredentials' => false,
+            'allowedHeaders' => array(),
+            'exposedHeaders' => array(),
+            'allowedMethods' => array(),
+            'maxAge' => 0,
+        );
+
+        // normalize array('*') to true
+        if (in_array('*', $options['allowedOrigins'])) {
+            $options['allowedOrigins'] = true;
+        }
+        if (in_array('*', $options['allowedHeaders'])) {
+            $options['allowedHeaders'] = true;
+        } else {
+            $options['allowedHeaders'] = array_map('strtolower', $options['allowedHeaders']);
+        }
+
+        if (in_array('*', $options['allowedMethods'])) {
+            $options['allowedMethods'] = true;
+        } else {
+            $options['allowedMethods'] = array_map('strtoupper', $options['allowedMethods']);
+        }
+
+        return $options;
+    }
+
+    /**
+     * @deprecated use isOriginAllowed
+     */
+    public function isActualRequestAllowed(Request $request): bool
+    {
+        return $this->isOriginAllowed($request);
+    }
+
+    public function isCorsRequest(Request $request): bool
+    {
+        return $request->headers->has('Origin');
+    }
+
+    public function isPreflightRequest(Request $request): bool
+    {
+        return $request->getMethod() === 'OPTIONS' && $request->headers->has('Access-Control-Request-Method');
+    }
+
+    public function handlePreflightRequest(Request $request): Response
+    {
+        $response = new Response();
+
+        $response->setStatusCode(204);
+
+        return $this->addPreflightRequestHeaders($response, $request);
+    }
+
+    public function addPreflightRequestHeaders(Response $response, Request $request): Response
+    {
+        $this->configureAllowedOrigin($response, $request);
+
+        if ($response->headers->has('Access-Control-Allow-Origin')) {
+            $this->configureAllowCredentials($response, $request);
+
+            $this->configureAllowedMethods($response, $request);
+
+            $this->configureAllowedHeaders($response, $request);
+
+            $this->configureMaxAge($response, $request);
+        }
+
+        return $response;
+    }
+
+    public function isOriginAllowed(Request $request): bool
+    {
+        if ($this->options['allowedOrigins'] === true) {
+            return true;
+        }
+
+        if (!$request->headers->has('Origin')) {
+            return false;
+        }
+
+        $origin = $request->headers->get('Origin');
+
+        if (in_array($origin, $this->options['allowedOrigins'])) {
+            return true;
+        }
+
+        foreach ($this->options['allowedOriginsPatterns'] as $pattern) {
+            if (preg_match($pattern, $origin)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public function addActualRequestHeaders(Response $response, Request $request): Response
+    {
+        $this->configureAllowedOrigin($response, $request);
+
+        if ($response->headers->has('Access-Control-Allow-Origin')) {
+            $this->configureAllowCredentials($response, $request);
+
+            $this->configureExposedHeaders($response, $request);
+        }
+
+        return $response;
+    }
+
+    private function configureAllowedOrigin(Response $response, Request $request)
+    {
+        if ($this->options['allowedOrigins'] === true && !$this->options['supportsCredentials']) {
+            // Safe+cacheable, allow everything
+            $response->headers->set('Access-Control-Allow-Origin', '*');
+        } elseif ($this->isSingleOriginAllowed()) {
+            // Single origins can be safely set
+            $response->headers->set('Access-Control-Allow-Origin', array_values($this->options['allowedOrigins'])[0]);
+        } else {
+            // For dynamic headers, check the origin first
+            if ($request->headers->has('Origin') && $this->isOriginAllowed($request)) {
+                $response->headers->set('Access-Control-Allow-Origin', $request->headers->get('Origin'));
+            }
+
+            $this->varyHeader($response, 'Origin');
+        }
+    }
+
+    private function isSingleOriginAllowed(): bool
+    {
+        if ($this->options['allowedOrigins'] === true || !empty($this->options['allowedOriginsPatterns'])) {
+            return false;
+        }
+
+        return count($this->options['allowedOrigins']) === 1;
+    }
+
+    private function configureAllowedMethods(Response $response, Request $request)
+    {
+        if ($this->options['allowedMethods'] === true) {
+            $allowMethods = strtoupper($request->headers->get('Access-Control-Request-Method'));
+            $this->varyHeader($response, 'Access-Control-Request-Method');
+        } else {
+            $allowMethods = implode(', ', $this->options['allowedMethods']);
+        }
+
+        $response->headers->set('Access-Control-Allow-Methods', $allowMethods);
+    }
+
+    private function configureAllowedHeaders(Response $response, Request $request)
+    {
+        if ($this->options['allowedHeaders'] === true) {
+            $allowHeaders = $request->headers->get('Access-Control-Request-Headers');
+            $this->varyHeader($response, 'Access-Control-Request-Headers');
+        } else {
+            $allowHeaders = implode(', ', $this->options['allowedHeaders']);
+        }
+        $response->headers->set('Access-Control-Allow-Headers', $allowHeaders);
+    }
+
+    private function configureAllowCredentials(Response $response, Request $request)
+    {
+        if ($this->options['supportsCredentials']) {
+            $response->headers->set('Access-Control-Allow-Credentials', 'true');
+        }
+    }
+
+    private function configureExposedHeaders(Response $response, Request $request)
+    {
+        if ($this->options['exposedHeaders']) {
+            $response->headers->set('Access-Control-Expose-Headers', implode(', ', $this->options['exposedHeaders']));
+        }
+    }
+
+    private function configureMaxAge(Response $response, Request $request)
+    {
+        if ($this->options['maxAge'] !== null) {
+            $response->headers->set('Access-Control-Max-Age', (int) $this->options['maxAge']);
+        }
+    }
+
+    public function varyHeader(Response $response, $header): Response
+    {
+        if (!$response->headers->has('Vary')) {
+            $response->headers->set('Vary', $header);
+        } elseif (!in_array($header, explode(', ', $response->headers->get('Vary')))) {
+            $response->headers->set('Vary', $response->headers->get('Vary') . ', ' . $header);
+        }
+
+        return $response;
+    }
+
+    private function isSameHost(Request $request): bool
+    {
+        return $request->headers->get('Origin') === $request->getSchemeAndHttpHost();
+    }
+}

+ 7 - 0
vendor/autoload.php

@@ -0,0 +1,7 @@
+<?php
+
+// autoload.php @generated by Composer
+
+require_once __DIR__ . '/composer/autoload_real.php';
+
+return ComposerAutoloaderInitf3fdf6613c44671dda1ed31123da14a7::getLoader();

+ 117 - 0
vendor/bin/carbon

@@ -0,0 +1,117 @@
+#!/usr/bin/env php
+<?php
+
+/**
+ * Proxy PHP file generated by Composer
+ *
+ * This file includes the referenced bin path (../nesbot/carbon/bin/carbon)
+ * using a stream wrapper to prevent the shebang from being output on PHP<8
+ *
+ * @generated
+ */
+
+namespace Composer;
+
+$GLOBALS['_composer_bin_dir'] = __DIR__;
+$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
+
+if (PHP_VERSION_ID < 80000) {
+    if (!class_exists('Composer\BinProxyWrapper')) {
+        /**
+         * @internal
+         */
+        final class BinProxyWrapper
+        {
+            private $handle;
+            private $position;
+            private $realpath;
+
+            public function stream_open($path, $mode, $options, &$opened_path)
+            {
+                // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
+                $opened_path = substr($path, 17);
+                $this->realpath = realpath($opened_path) ?: $opened_path;
+                $opened_path = $this->realpath;
+                $this->handle = fopen($this->realpath, $mode);
+                $this->position = 0;
+
+                return (bool) $this->handle;
+            }
+
+            public function stream_read($count)
+            {
+                $data = fread($this->handle, $count);
+
+                if ($this->position === 0) {
+                    $data = preg_replace('{^#!.*\r?\n}', '', $data);
+                }
+
+                $this->position += strlen($data);
+
+                return $data;
+            }
+
+            public function stream_cast($castAs)
+            {
+                return $this->handle;
+            }
+
+            public function stream_close()
+            {
+                fclose($this->handle);
+            }
+
+            public function stream_lock($operation)
+            {
+                return $operation ? flock($this->handle, $operation) : true;
+            }
+
+            public function stream_seek($offset, $whence)
+            {
+                if (0 === fseek($this->handle, $offset, $whence)) {
+                    $this->position = ftell($this->handle);
+                    return true;
+                }
+
+                return false;
+            }
+
+            public function stream_tell()
+            {
+                return $this->position;
+            }
+
+            public function stream_eof()
+            {
+                return feof($this->handle);
+            }
+
+            public function stream_stat()
+            {
+                return array();
+            }
+
+            public function stream_set_option($option, $arg1, $arg2)
+            {
+                return true;
+            }
+
+            public function url_stat($path, $flags)
+            {
+                $path = substr($path, 17);
+                if (file_exists($path)) {
+                    return stat($path);
+                }
+
+                return false;
+            }
+        }
+    }
+
+    if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) {
+        include("phpvfscomposer://" . __DIR__ . '/..'.'/nesbot/carbon/bin/carbon');
+        exit(0);
+    }
+}
+
+include __DIR__ . '/..'.'/nesbot/carbon/bin/carbon';

+ 5 - 0
vendor/bin/carbon.bat

@@ -0,0 +1,5 @@
+@ECHO OFF
+setlocal DISABLEDELAYEDEXPANSION
+SET BIN_TARGET=%~dp0/carbon
+SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
+php "%BIN_TARGET%" %*

+ 117 - 0
vendor/bin/commonmark

@@ -0,0 +1,117 @@
+#!/usr/bin/env php
+<?php
+
+/**
+ * Proxy PHP file generated by Composer
+ *
+ * This file includes the referenced bin path (../league/commonmark/bin/commonmark)
+ * using a stream wrapper to prevent the shebang from being output on PHP<8
+ *
+ * @generated
+ */
+
+namespace Composer;
+
+$GLOBALS['_composer_bin_dir'] = __DIR__;
+$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
+
+if (PHP_VERSION_ID < 80000) {
+    if (!class_exists('Composer\BinProxyWrapper')) {
+        /**
+         * @internal
+         */
+        final class BinProxyWrapper
+        {
+            private $handle;
+            private $position;
+            private $realpath;
+
+            public function stream_open($path, $mode, $options, &$opened_path)
+            {
+                // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
+                $opened_path = substr($path, 17);
+                $this->realpath = realpath($opened_path) ?: $opened_path;
+                $opened_path = $this->realpath;
+                $this->handle = fopen($this->realpath, $mode);
+                $this->position = 0;
+
+                return (bool) $this->handle;
+            }
+
+            public function stream_read($count)
+            {
+                $data = fread($this->handle, $count);
+
+                if ($this->position === 0) {
+                    $data = preg_replace('{^#!.*\r?\n}', '', $data);
+                }
+
+                $this->position += strlen($data);
+
+                return $data;
+            }
+
+            public function stream_cast($castAs)
+            {
+                return $this->handle;
+            }
+
+            public function stream_close()
+            {
+                fclose($this->handle);
+            }
+
+            public function stream_lock($operation)
+            {
+                return $operation ? flock($this->handle, $operation) : true;
+            }
+
+            public function stream_seek($offset, $whence)
+            {
+                if (0 === fseek($this->handle, $offset, $whence)) {
+                    $this->position = ftell($this->handle);
+                    return true;
+                }
+
+                return false;
+            }
+
+            public function stream_tell()
+            {
+                return $this->position;
+            }
+
+            public function stream_eof()
+            {
+                return feof($this->handle);
+            }
+
+            public function stream_stat()
+            {
+                return array();
+            }
+
+            public function stream_set_option($option, $arg1, $arg2)
+            {
+                return true;
+            }
+
+            public function url_stat($path, $flags)
+            {
+                $path = substr($path, 17);
+                if (file_exists($path)) {
+                    return stat($path);
+                }
+
+                return false;
+            }
+        }
+    }
+
+    if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) {
+        include("phpvfscomposer://" . __DIR__ . '/..'.'/league/commonmark/bin/commonmark');
+        exit(0);
+    }
+}
+
+include __DIR__ . '/..'.'/league/commonmark/bin/commonmark';

+ 5 - 0
vendor/bin/commonmark.bat

@@ -0,0 +1,5 @@
+@ECHO OFF
+setlocal DISABLEDELAYEDEXPANSION
+SET BIN_TARGET=%~dp0/commonmark
+SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
+php "%BIN_TARGET%" %*

+ 117 - 0
vendor/bin/php-parse

@@ -0,0 +1,117 @@
+#!/usr/bin/env php
+<?php
+
+/**
+ * Proxy PHP file generated by Composer
+ *
+ * This file includes the referenced bin path (../nikic/php-parser/bin/php-parse)
+ * using a stream wrapper to prevent the shebang from being output on PHP<8
+ *
+ * @generated
+ */
+
+namespace Composer;
+
+$GLOBALS['_composer_bin_dir'] = __DIR__;
+$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
+
+if (PHP_VERSION_ID < 80000) {
+    if (!class_exists('Composer\BinProxyWrapper')) {
+        /**
+         * @internal
+         */
+        final class BinProxyWrapper
+        {
+            private $handle;
+            private $position;
+            private $realpath;
+
+            public function stream_open($path, $mode, $options, &$opened_path)
+            {
+                // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
+                $opened_path = substr($path, 17);
+                $this->realpath = realpath($opened_path) ?: $opened_path;
+                $opened_path = $this->realpath;
+                $this->handle = fopen($this->realpath, $mode);
+                $this->position = 0;
+
+                return (bool) $this->handle;
+            }
+
+            public function stream_read($count)
+            {
+                $data = fread($this->handle, $count);
+
+                if ($this->position === 0) {
+                    $data = preg_replace('{^#!.*\r?\n}', '', $data);
+                }
+
+                $this->position += strlen($data);
+
+                return $data;
+            }
+
+            public function stream_cast($castAs)
+            {
+                return $this->handle;
+            }
+
+            public function stream_close()
+            {
+                fclose($this->handle);
+            }
+
+            public function stream_lock($operation)
+            {
+                return $operation ? flock($this->handle, $operation) : true;
+            }
+
+            public function stream_seek($offset, $whence)
+            {
+                if (0 === fseek($this->handle, $offset, $whence)) {
+                    $this->position = ftell($this->handle);
+                    return true;
+                }
+
+                return false;
+            }
+
+            public function stream_tell()
+            {
+                return $this->position;
+            }
+
+            public function stream_eof()
+            {
+                return feof($this->handle);
+            }
+
+            public function stream_stat()
+            {
+                return array();
+            }
+
+            public function stream_set_option($option, $arg1, $arg2)
+            {
+                return true;
+            }
+
+            public function url_stat($path, $flags)
+            {
+                $path = substr($path, 17);
+                if (file_exists($path)) {
+                    return stat($path);
+                }
+
+                return false;
+            }
+        }
+    }
+
+    if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) {
+        include("phpvfscomposer://" . __DIR__ . '/..'.'/nikic/php-parser/bin/php-parse');
+        exit(0);
+    }
+}
+
+include __DIR__ . '/..'.'/nikic/php-parser/bin/php-parse';

+ 5 - 0
vendor/bin/php-parse.bat

@@ -0,0 +1,5 @@
+@ECHO OFF
+setlocal DISABLEDELAYEDEXPANSION
+SET BIN_TARGET=%~dp0/php-parse
+SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
+php "%BIN_TARGET%" %*

+ 120 - 0
vendor/bin/phpunit

@@ -0,0 +1,120 @@
+#!/usr/bin/env php
+<?php
+
+/**
+ * Proxy PHP file generated by Composer
+ *
+ * This file includes the referenced bin path (../phpunit/phpunit/phpunit)
+ * using a stream wrapper to prevent the shebang from being output on PHP<8
+ *
+ * @generated
+ */
+
+namespace Composer;
+
+$GLOBALS['_composer_bin_dir'] = __DIR__;
+$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
+$GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'] = $GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST'] = array(realpath(__DIR__ . '/..'.'/phpunit/phpunit/phpunit'));
+
+if (PHP_VERSION_ID < 80000) {
+    if (!class_exists('Composer\BinProxyWrapper')) {
+        /**
+         * @internal
+         */
+        final class BinProxyWrapper
+        {
+            private $handle;
+            private $position;
+            private $realpath;
+
+            public function stream_open($path, $mode, $options, &$opened_path)
+            {
+                // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
+                $opened_path = substr($path, 17);
+                $this->realpath = realpath($opened_path) ?: $opened_path;
+                $opened_path = 'phpvfscomposer://'.$this->realpath;
+                $this->handle = fopen($this->realpath, $mode);
+                $this->position = 0;
+
+                return (bool) $this->handle;
+            }
+
+            public function stream_read($count)
+            {
+                $data = fread($this->handle, $count);
+
+                if ($this->position === 0) {
+                    $data = preg_replace('{^#!.*\r?\n}', '', $data);
+                }
+                $data = str_replace('__DIR__', var_export(dirname($this->realpath), true), $data);
+                $data = str_replace('__FILE__', var_export($this->realpath, true), $data);
+
+                $this->position += strlen($data);
+
+                return $data;
+            }
+
+            public function stream_cast($castAs)
+            {
+                return $this->handle;
+            }
+
+            public function stream_close()
+            {
+                fclose($this->handle);
+            }
+
+            public function stream_lock($operation)
+            {
+                return $operation ? flock($this->handle, $operation) : true;
+            }
+
+            public function stream_seek($offset, $whence)
+            {
+                if (0 === fseek($this->handle, $offset, $whence)) {
+                    $this->position = ftell($this->handle);
+                    return true;
+                }
+
+                return false;
+            }
+
+            public function stream_tell()
+            {
+                return $this->position;
+            }
+
+            public function stream_eof()
+            {
+                return feof($this->handle);
+            }
+
+            public function stream_stat()
+            {
+                return array();
+            }
+
+            public function stream_set_option($option, $arg1, $arg2)
+            {
+                return true;
+            }
+
+            public function url_stat($path, $flags)
+            {
+                $path = substr($path, 17);
+                if (file_exists($path)) {
+                    return stat($path);
+                }
+
+                return false;
+            }
+        }
+    }
+
+    if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) {
+        include("phpvfscomposer://" . __DIR__ . '/..'.'/phpunit/phpunit/phpunit');
+        exit(0);
+    }
+}
+
+include __DIR__ . '/..'.'/phpunit/phpunit/phpunit';

+ 5 - 0
vendor/bin/phpunit.bat

@@ -0,0 +1,5 @@
+@ECHO OFF
+setlocal DISABLEDELAYEDEXPANSION
+SET BIN_TARGET=%~dp0/phpunit
+SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
+php "%BIN_TARGET%" %*

+ 117 - 0
vendor/bin/psysh

@@ -0,0 +1,117 @@
+#!/usr/bin/env php
+<?php
+
+/**
+ * Proxy PHP file generated by Composer
+ *
+ * This file includes the referenced bin path (../psy/psysh/bin/psysh)
+ * using a stream wrapper to prevent the shebang from being output on PHP<8
+ *
+ * @generated
+ */
+
+namespace Composer;
+
+$GLOBALS['_composer_bin_dir'] = __DIR__;
+$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
+
+if (PHP_VERSION_ID < 80000) {
+    if (!class_exists('Composer\BinProxyWrapper')) {
+        /**
+         * @internal
+         */
+        final class BinProxyWrapper
+        {
+            private $handle;
+            private $position;
+            private $realpath;
+
+            public function stream_open($path, $mode, $options, &$opened_path)
+            {
+                // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
+                $opened_path = substr($path, 17);
+                $this->realpath = realpath($opened_path) ?: $opened_path;
+                $opened_path = $this->realpath;
+                $this->handle = fopen($this->realpath, $mode);
+                $this->position = 0;
+
+                return (bool) $this->handle;
+            }
+
+            public function stream_read($count)
+            {
+                $data = fread($this->handle, $count);
+
+                if ($this->position === 0) {
+                    $data = preg_replace('{^#!.*\r?\n}', '', $data);
+                }
+
+                $this->position += strlen($data);
+
+                return $data;
+            }
+
+            public function stream_cast($castAs)
+            {
+                return $this->handle;
+            }
+
+            public function stream_close()
+            {
+                fclose($this->handle);
+            }
+
+            public function stream_lock($operation)
+            {
+                return $operation ? flock($this->handle, $operation) : true;
+            }
+
+            public function stream_seek($offset, $whence)
+            {
+                if (0 === fseek($this->handle, $offset, $whence)) {
+                    $this->position = ftell($this->handle);
+                    return true;
+                }
+
+                return false;
+            }
+
+            public function stream_tell()
+            {
+                return $this->position;
+            }
+
+            public function stream_eof()
+            {
+                return feof($this->handle);
+            }
+
+            public function stream_stat()
+            {
+                return array();
+            }
+
+            public function stream_set_option($option, $arg1, $arg2)
+            {
+                return true;
+            }
+
+            public function url_stat($path, $flags)
+            {
+                $path = substr($path, 17);
+                if (file_exists($path)) {
+                    return stat($path);
+                }
+
+                return false;
+            }
+        }
+    }
+
+    if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) {
+        include("phpvfscomposer://" . __DIR__ . '/..'.'/psy/psysh/bin/psysh');
+        exit(0);
+    }
+}
+
+include __DIR__ . '/..'.'/psy/psysh/bin/psysh';

+ 5 - 0
vendor/bin/psysh.bat

@@ -0,0 +1,5 @@
+@ECHO OFF
+setlocal DISABLEDELAYEDEXPANSION
+SET BIN_TARGET=%~dp0/psysh
+SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
+php "%BIN_TARGET%" %*

+ 37 - 0
vendor/bin/sail

@@ -0,0 +1,37 @@
+#!/usr/bin/env sh
+
+# Support bash to support `source` with fallback on $0 if this does not run with bash
+# https://stackoverflow.com/a/35006505/6512
+selfArg="$BASH_SOURCE"
+if [ -z "$selfArg" ]; then
+    selfArg="$0"
+fi
+
+self=$(realpath $selfArg 2> /dev/null)
+if [ -z "$self" ]; then
+    self="$selfArg"
+fi
+
+dir=$(cd "${self%[/\\]*}" > /dev/null; cd ../laravel/sail/bin && pwd)
+
+if [ -d /proc/cygdrive ]; then
+    case $(which php) in
+        $(readlink -n /proc/cygdrive)/*)
+            # We are in Cygwin using Windows php, so the path must be translated
+            dir=$(cygpath -m "$dir");
+            ;;
+    esac
+fi
+
+export COMPOSER_RUNTIME_BIN_DIR=$(cd "${self%[/\\]*}" > /dev/null; pwd)
+
+# If bash is sourcing this file, we have to source the target as well
+bashSource="$BASH_SOURCE"
+if [ -n "$bashSource" ]; then
+    if [ "$bashSource" != "$0" ]; then
+        source "${dir}/sail" "$@"
+        return
+    fi
+fi
+
+"${dir}/sail" "$@"

+ 5 - 0
vendor/bin/sail.bat

@@ -0,0 +1,5 @@
+@ECHO OFF
+setlocal DISABLEDELAYEDEXPANSION
+SET BIN_TARGET=%~dp0/../laravel/sail/bin/sail
+SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
+bash "%BIN_TARGET%" %*

+ 117 - 0
vendor/bin/var-dump-server

@@ -0,0 +1,117 @@
+#!/usr/bin/env php
+<?php
+
+/**
+ * Proxy PHP file generated by Composer
+ *
+ * This file includes the referenced bin path (../symfony/var-dumper/Resources/bin/var-dump-server)
+ * using a stream wrapper to prevent the shebang from being output on PHP<8
+ *
+ * @generated
+ */
+
+namespace Composer;
+
+$GLOBALS['_composer_bin_dir'] = __DIR__;
+$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
+
+if (PHP_VERSION_ID < 80000) {
+    if (!class_exists('Composer\BinProxyWrapper')) {
+        /**
+         * @internal
+         */
+        final class BinProxyWrapper
+        {
+            private $handle;
+            private $position;
+            private $realpath;
+
+            public function stream_open($path, $mode, $options, &$opened_path)
+            {
+                // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
+                $opened_path = substr($path, 17);
+                $this->realpath = realpath($opened_path) ?: $opened_path;
+                $opened_path = $this->realpath;
+                $this->handle = fopen($this->realpath, $mode);
+                $this->position = 0;
+
+                return (bool) $this->handle;
+            }
+
+            public function stream_read($count)
+            {
+                $data = fread($this->handle, $count);
+
+                if ($this->position === 0) {
+                    $data = preg_replace('{^#!.*\r?\n}', '', $data);
+                }
+
+                $this->position += strlen($data);
+
+                return $data;
+            }
+
+            public function stream_cast($castAs)
+            {
+                return $this->handle;
+            }
+
+            public function stream_close()
+            {
+                fclose($this->handle);
+            }
+
+            public function stream_lock($operation)
+            {
+                return $operation ? flock($this->handle, $operation) : true;
+            }
+
+            public function stream_seek($offset, $whence)
+            {
+                if (0 === fseek($this->handle, $offset, $whence)) {
+                    $this->position = ftell($this->handle);
+                    return true;
+                }
+
+                return false;
+            }
+
+            public function stream_tell()
+            {
+                return $this->position;
+            }
+
+            public function stream_eof()
+            {
+                return feof($this->handle);
+            }
+
+            public function stream_stat()
+            {
+                return array();
+            }
+
+            public function stream_set_option($option, $arg1, $arg2)
+            {
+                return true;
+            }
+
+            public function url_stat($path, $flags)
+            {
+                $path = substr($path, 17);
+                if (file_exists($path)) {
+                    return stat($path);
+                }
+
+                return false;
+            }
+        }
+    }
+
+    if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) {
+        include("phpvfscomposer://" . __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server');
+        exit(0);
+    }
+}
+
+include __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server';

+ 5 - 0
vendor/bin/var-dump-server.bat

@@ -0,0 +1,5 @@
+@ECHO OFF
+setlocal DISABLEDELAYEDEXPANSION
+SET BIN_TARGET=%~dp0/var-dump-server
+SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
+php "%BIN_TARGET%" %*

+ 403 - 0
vendor/brick/math/CHANGELOG.md

@@ -0,0 +1,403 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+## [0.9.1](https://github.com/brick/math/releases/tag/0.9.1) - 2020-08-19
+
+✨ New features
+
+- `BigInteger::not()` returns the bitwise `NOT` value
+
+🐛 **Bug fixes**
+
+- `BigInteger::toBytes()` could return an incorrect binary representation for some numbers
+- The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available
+
+## [0.9.0](https://github.com/brick/math/releases/tag/0.9.0) - 2020-08-18
+
+👌 **Improvements**
+
+- `BigNumber::of()` now accepts `.123` and `123.` formats, both of which return a `BigDecimal`
+
+💥 **Breaking changes**
+
+- Deprecated method `BigInteger::powerMod()` has been removed - use `modPow()` instead
+- Deprecated method `BigInteger::parse()` has been removed - use `fromBase()` instead
+
+## [0.8.9](https://github.com/brick/math/releases/tag/0.8.9) - 2020-01-08
+
+⚡️ **Performance improvements**
+
+A few additional optimizations in `BigInteger` and `BigDecimal` when one of the operands can be returned as is. Thanks to @tomtomsen in #24.
+
+## [0.8.8](https://github.com/brick/math/releases/tag/0.8.8) - 2019-04-25
+
+🐛 **Bug fixes**
+
+- `BigInteger::toBase()` could return an empty string for zero values (BCMath & Native calculators only, GMP calculator unaffected)
+
+✨ **New features**
+
+- `BigInteger::toArbitraryBase()` converts a number to an arbitrary base, using a custom alphabet
+- `BigInteger::fromArbitraryBase()` converts a string in an arbitrary base, using a custom alphabet, back to a number
+
+These methods can be used as the foundation to convert strings between different bases/alphabets, using BigInteger as an intermediate representation.
+
+💩 **Deprecations**
+
+- `BigInteger::parse()` is now deprecated in favour of `fromBase()`
+
+`BigInteger::fromBase()` works the same way as `parse()`, with 2 minor differences:
+
+- the `$base` parameter is required, it does not default to `10`
+- it throws a `NumberFormatException` instead of an `InvalidArgumentException` when the number is malformed
+
+## [0.8.7](https://github.com/brick/math/releases/tag/0.8.7) - 2019-04-20
+
+**Improvements**
+
+- Safer conversion from `float` when using custom locales
+- **Much faster** `NativeCalculator` implementation 🚀
+
+You can expect **at least a 3x performance improvement** for common arithmetic operations when using the library on systems without GMP or BCMath; it gets exponentially faster on multiplications with a high number of digits. This is due to calculations now being performed on whole blocks of digits (the block size depending on the platform, 32-bit or 64-bit) instead of digit-by-digit as before.
+
+## [0.8.6](https://github.com/brick/math/releases/tag/0.8.6) - 2019-04-11
+
+**New method**
+
+`BigNumber::sum()` returns the sum of one or more numbers.
+
+## [0.8.5](https://github.com/brick/math/releases/tag/0.8.5) - 2019-02-12
+
+**Bug fix**: `of()` factory methods could fail when passing a `float` in environments using a `LC_NUMERIC` locale with a decimal separator other than `'.'` (#20).
+
+Thanks @manowark 👍
+
+## [0.8.4](https://github.com/brick/math/releases/tag/0.8.4) - 2018-12-07
+
+**New method**
+
+`BigDecimal::sqrt()` calculates the square root of a decimal number, to a given scale.
+
+## [0.8.3](https://github.com/brick/math/releases/tag/0.8.3) - 2018-12-06
+
+**New method**
+
+`BigInteger::sqrt()` calculates the square root of a number (thanks @peter279k).
+
+**New exception**
+
+`NegativeNumberException` is thrown when calling `sqrt()` on a negative number.
+
+## [0.8.2](https://github.com/brick/math/releases/tag/0.8.2) - 2018-11-08
+
+**Performance update**
+
+- Further improvement of `toInt()` performance
+- `NativeCalculator` can now perform some multiplications more efficiently
+
+## [0.8.17](https://github.com/brick/math/releases/tag/0.8.17) - 2020-08-19
+
+🐛 **Bug fix**
+
+- `BigInteger::toBytes()` could return an incorrect binary representation for some numbers
+- The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available
+
+## [0.8.16](https://github.com/brick/math/releases/tag/0.8.16) - 2020-08-18
+
+🚑 **Critical fix**
+
+- This version reintroduces the deprecated `BigInteger::parse()` method, that has been removed by mistake in version `0.8.9` and should have lasted for the whole `0.8` release cycle.
+
+✨ **New features**
+
+- `BigInteger::modInverse()` calculates a modular multiplicative inverse
+- `BigInteger::fromBytes()` creates a `BigInteger` from a byte string
+- `BigInteger::toBytes()` converts a `BigInteger` to a byte string
+- `BigInteger::randomBits()` creates a pseudo-random `BigInteger` of a given bit length
+- `BigInteger::randomRange()` creates a pseudo-random `BigInteger` between two bounds
+
+💩 **Deprecations**
+
+- `BigInteger::powerMod()` is now deprecated in favour of `modPow()`
+
+## [0.8.15](https://github.com/brick/math/releases/tag/0.8.15) - 2020-04-15
+
+🐛 **Fixes**
+
+- added missing `ext-json` requirement, due to `BigNumber` implementing `JsonSerializable`
+
+⚡️ **Optimizations**
+
+- additional optimization in `BigInteger::remainder()`
+
+## [0.8.14](https://github.com/brick/math/releases/tag/0.8.14) - 2020-02-18
+
+✨ **New features**
+
+- `BigInteger::getLowestSetBit()` returns the index of the rightmost one bit
+
+## [0.8.13](https://github.com/brick/math/releases/tag/0.8.13) - 2020-02-16
+
+✨ **New features**
+
+- `BigInteger::isEven()` tests whether the number is even
+- `BigInteger::isOdd()` tests whether the number is odd
+- `BigInteger::testBit()` tests if a bit is set
+- `BigInteger::getBitLength()` returns the number of bits in the minimal representation of the number
+
+## [0.8.12](https://github.com/brick/math/releases/tag/0.8.12) - 2020-02-03
+
+🛠️ **Maintenance release**
+
+Classes are now annotated for better static analysis with [psalm](https://psalm.dev/).
+
+This is a maintenance release: no bug fixes, no new features, no breaking changes.
+
+## [0.8.11](https://github.com/brick/math/releases/tag/0.8.11) - 2020-01-23
+
+✨ **New feature**
+
+`BigInteger::powerMod()` performs a power-with-modulo operation. Useful for crypto.
+
+## [0.8.10](https://github.com/brick/math/releases/tag/0.8.10) - 2020-01-21
+
+✨ **New feature**
+
+`BigInteger::mod()` returns the **modulo** of two numbers. The *modulo* differs from the *remainder* when the signs of the operands are different.
+
+## [0.8.1](https://github.com/brick/math/releases/tag/0.8.1) - 2018-11-07
+
+Performance optimization of `toInt()` methods.
+
+## [0.8.0](https://github.com/brick/math/releases/tag/0.8.0) - 2018-10-13
+
+**Breaking changes**
+
+The following deprecated methods have been removed. Use the new method name instead:
+
+| Method removed | Replacement method |
+| --- | --- |
+| `BigDecimal::getIntegral()` | `BigDecimal::getIntegralPart()` |
+| `BigDecimal::getFraction()` | `BigDecimal::getFractionalPart()` |
+
+---
+
+**New features**
+
+`BigInteger` has been augmented with 5 new methods for bitwise operations:
+
+| New method | Description |
+| --- | --- |
+| `and()` | performs a bitwise `AND` operation on two numbers |
+| `or()` | performs a bitwise `OR` operation on two numbers |
+| `xor()` | performs a bitwise `XOR` operation on two numbers |
+| `shiftedLeft()` | returns the number shifted left by a number of bits |
+| `shiftedRight()` | returns the number shifted right by a number of bits |
+
+Thanks to @DASPRiD 👍
+
+## [0.7.3](https://github.com/brick/math/releases/tag/0.7.3) - 2018-08-20
+
+**New method:** `BigDecimal::hasNonZeroFractionalPart()`
+
+**Renamed/deprecated methods:**
+
+- `BigDecimal::getIntegral()` has been renamed to `getIntegralPart()` and is now deprecated
+- `BigDecimal::getFraction()` has been renamed to `getFractionalPart()` and is now deprecated
+
+## [0.7.2](https://github.com/brick/math/releases/tag/0.7.2) - 2018-07-21
+
+**Performance update**
+
+`BigInteger::parse()` and `toBase()` now use GMP's built-in base conversion features when available.
+
+## [0.7.1](https://github.com/brick/math/releases/tag/0.7.1) - 2018-03-01
+
+This is a maintenance release, no code has been changed.
+
+- When installed with `--no-dev`, the autoloader does not autoload tests anymore
+- Tests and other files unnecessary for production are excluded from the dist package
+
+This will help make installations more compact.
+
+## [0.7.0](https://github.com/brick/math/releases/tag/0.7.0) - 2017-10-02
+
+Methods renamed:
+
+- `BigNumber:sign()` has been renamed to `getSign()`
+- `BigDecimal::unscaledValue()` has been renamed to `getUnscaledValue()`
+- `BigDecimal::scale()` has been renamed to `getScale()`
+- `BigDecimal::integral()` has been renamed to `getIntegral()`
+- `BigDecimal::fraction()` has been renamed to `getFraction()`
+- `BigRational::numerator()` has been renamed to `getNumerator()`
+- `BigRational::denominator()` has been renamed to `getDenominator()`
+
+Classes renamed:
+
+- `ArithmeticException` has been renamed to `MathException`
+
+## [0.6.2](https://github.com/brick/math/releases/tag/0.6.2) - 2017-10-02
+
+The base class for all exceptions is now `MathException`.
+`ArithmeticException` has been deprecated, and will be removed in 0.7.0.
+
+## [0.6.1](https://github.com/brick/math/releases/tag/0.6.1) - 2017-10-02
+
+A number of methods have been renamed:
+
+- `BigNumber:sign()` is deprecated; use `getSign()` instead
+- `BigDecimal::unscaledValue()` is deprecated; use `getUnscaledValue()` instead
+- `BigDecimal::scale()` is deprecated; use `getScale()` instead
+- `BigDecimal::integral()` is deprecated; use `getIntegral()` instead
+- `BigDecimal::fraction()` is deprecated; use `getFraction()` instead
+- `BigRational::numerator()` is deprecated; use `getNumerator()` instead
+- `BigRational::denominator()` is deprecated; use `getDenominator()` instead
+
+The old methods will be removed in version 0.7.0.
+
+## [0.6.0](https://github.com/brick/math/releases/tag/0.6.0) - 2017-08-25
+
+- Minimum PHP version is now [7.1](https://gophp71.org/); for PHP 5.6 and PHP 7.0 support, use version `0.5`
+- Deprecated method `BigDecimal::withScale()` has been removed; use `toScale()` instead
+- Method `BigNumber::toInteger()` has been renamed to `toInt()`
+
+## [0.5.4](https://github.com/brick/math/releases/tag/0.5.4) - 2016-10-17
+
+`BigNumber` classes now implement [JsonSerializable](http://php.net/manual/en/class.jsonserializable.php).
+The JSON output is always a string.
+
+## [0.5.3](https://github.com/brick/math/releases/tag/0.5.3) - 2016-03-31
+
+This is a bugfix release. Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.
+
+## [0.5.2](https://github.com/brick/math/releases/tag/0.5.2) - 2015-08-06
+
+The `$scale` parameter of `BigDecimal::dividedBy()` is now optional again.
+
+## [0.5.1](https://github.com/brick/math/releases/tag/0.5.1) - 2015-07-05
+
+**New method: `BigNumber::toScale()`**
+
+This allows to convert any `BigNumber` to a `BigDecimal` with a given scale, using rounding if necessary.
+
+## [0.5.0](https://github.com/brick/math/releases/tag/0.5.0) - 2015-07-04
+
+**New features**
+- Common `BigNumber` interface for all classes, with the following methods:
+  - `sign()` and derived methods (`isZero()`, `isPositive()`, ...)
+  - `compareTo()` and derived methods (`isEqualTo()`, `isGreaterThan()`, ...) that work across different `BigNumber` types
+  - `toBigInteger()`, `toBigDecimal()`, `toBigRational`() conversion methods
+  - `toInteger()` and `toFloat()` conversion methods to native types
+- Unified `of()` behaviour: every class now accepts any type of number, provided that it can be safely converted to the current type
+- New method: `BigDecimal::exactlyDividedBy()`; this method automatically computes the scale of the result, provided that the division yields a finite number of digits
+- New methods: `BigRational::quotient()` and `remainder()`
+- Fine-grained exceptions: `DivisionByZeroException`, `RoundingNecessaryException`, `NumberFormatException`
+- Factory methods `zero()`, `one()` and `ten()` available in all classes
+- Rounding mode reintroduced in `BigInteger::dividedBy()`
+
+This release also comes with many performance improvements.
+
+---
+
+**Breaking changes**
+- `BigInteger`:
+  - `getSign()` is renamed to `sign()`
+  - `toString()` is renamed to `toBase()`
+  - `BigInteger::dividedBy()` now throws an exception by default if the remainder is not zero; use `quotient()` to get the previous behaviour
+- `BigDecimal`:
+  - `getSign()` is renamed to `sign()`
+  - `getUnscaledValue()` is renamed to `unscaledValue()`
+  - `getScale()` is renamed to `scale()`
+  - `getIntegral()` is renamed to `integral()`
+  - `getFraction()` is renamed to `fraction()`
+  - `divideAndRemainder()` is renamed to `quotientAndRemainder()`
+  - `dividedBy()` now takes a **mandatory** `$scale` parameter **before** the rounding mode
+  - `toBigInteger()` does not accept a `$roundingMode` parameter any more
+  - `toBigRational()` does not simplify the fraction any more; explicitly add `->simplified()` to get the previous behaviour
+- `BigRational`:
+  - `getSign()` is renamed to `sign()`
+  - `getNumerator()` is renamed to  `numerator()`
+  - `getDenominator()` is renamed to  `denominator()`
+  - `of()` is renamed to `nd()`, while `parse()` is renamed to `of()`
+- Miscellaneous:
+  - `ArithmeticException` is moved to an `Exception\` sub-namespace
+  - `of()` factory methods now throw `NumberFormatException` instead of `InvalidArgumentException`
+
+## [0.4.3](https://github.com/brick/math/releases/tag/0.4.3) - 2016-03-31
+
+Backport of two bug fixes from the 0.5 branch:
+- `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected
+- Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.
+
+## [0.4.2](https://github.com/brick/math/releases/tag/0.4.2) - 2015-06-16
+
+New method: `BigDecimal::stripTrailingZeros()`
+
+## [0.4.1](https://github.com/brick/math/releases/tag/0.4.1) - 2015-06-12
+
+Introducing a `BigRational` class, to perform calculations on fractions of any size.
+
+## [0.4.0](https://github.com/brick/math/releases/tag/0.4.0) - 2015-06-12
+
+Rounding modes have been removed from `BigInteger`, and are now a concept specific to `BigDecimal`.
+
+`BigInteger::dividedBy()` now always returns the quotient of the division.
+
+## [0.3.5](https://github.com/brick/math/releases/tag/0.3.5) - 2016-03-31
+
+Backport of two bug fixes from the 0.5 branch:
+
+- `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected
+- Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.
+
+## [0.3.4](https://github.com/brick/math/releases/tag/0.3.4) - 2015-06-11
+
+New methods:
+- `BigInteger::remainder()` returns the remainder of a division only
+- `BigInteger::gcd()` returns the greatest common divisor of two numbers
+
+## [0.3.3](https://github.com/brick/math/releases/tag/0.3.3) - 2015-06-07
+
+Fix `toString()` not handling negative numbers.
+
+## [0.3.2](https://github.com/brick/math/releases/tag/0.3.2) - 2015-06-07
+
+`BigInteger` and `BigDecimal` now have a `getSign()` method that returns:
+- `-1` if the number is negative
+- `0` if the number is zero
+- `1` if the number is positive
+
+## [0.3.1](https://github.com/brick/math/releases/tag/0.3.1) - 2015-06-05
+
+Minor performance improvements
+
+## [0.3.0](https://github.com/brick/math/releases/tag/0.3.0) - 2015-06-04
+
+The `$roundingMode` and `$scale` parameters have been swapped in `BigDecimal::dividedBy()`.
+
+## [0.2.2](https://github.com/brick/math/releases/tag/0.2.2) - 2015-06-04
+
+Stronger immutability guarantee for `BigInteger` and `BigDecimal`.
+
+So far, it would have been possible to break immutability of these classes by calling the `unserialize()` internal function. This release fixes that.
+
+## [0.2.1](https://github.com/brick/math/releases/tag/0.2.1) - 2015-06-02
+
+Added `BigDecimal::divideAndRemainder()`
+
+## [0.2.0](https://github.com/brick/math/releases/tag/0.2.0) - 2015-05-22
+
+- `min()` and `max()` do not accept an `array` any more, but a variable number of parameters
+- **minimum PHP version is now 5.6**
+- continuous integration with PHP 7
+
+## [0.1.1](https://github.com/brick/math/releases/tag/0.1.1) - 2014-09-01
+
+- Added `BigInteger::power()`
+- Added HHVM support
+
+## [0.1.0](https://github.com/brick/math/releases/tag/0.1.0) - 2014-08-31
+
+First beta release.
+

+ 20 - 0
vendor/brick/math/LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-present Benjamin Morel
+
+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.

+ 17 - 0
vendor/brick/math/SECURITY.md

@@ -0,0 +1,17 @@
+# Security Policy
+
+## Supported Versions
+
+Only the last two release streams are supported.
+
+| Version | Supported          |
+| ------- | ------------------ |
+| 0.9.x   | :white_check_mark: |
+| 0.8.x   | :white_check_mark: |
+| < 0.8   | :x:                |
+
+## Reporting a Vulnerability
+
+To report a security vulnerability, please use the
+[Tidelift security contact](https://tidelift.com/security).
+Tidelift will coordinate the fix and disclosure.

+ 35 - 0
vendor/brick/math/composer.json

@@ -0,0 +1,35 @@
+{
+    "name": "brick/math",
+    "description": "Arbitrary-precision arithmetic library",
+    "type": "library",
+    "keywords": [
+        "Brick",
+        "Math",
+        "Arbitrary-precision",
+        "Arithmetic",
+        "BigInteger",
+        "BigDecimal",
+        "BigRational",
+        "Bignum"
+    ],
+    "license": "MIT",
+    "require": {
+        "php": "^7.1 || ^8.0",
+        "ext-json": "*"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0",
+        "php-coveralls/php-coveralls": "^2.2",
+        "vimeo/psalm": "4.3.2"
+    },
+    "autoload": {
+        "psr-4": {
+            "Brick\\Math\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Brick\\Math\\Tests\\": "tests/"
+        }
+    }
+}

+ 194 - 0
vendor/brick/math/random-tests.php

@@ -0,0 +1,194 @@
+<?php
+
+/**
+ * This script stress tests calculators with random large numbers and ensures that all implementations return the same
+ * results. It is designed to run in an infinite loop unless a bug is found.
+ */
+
+declare(strict_types=1);
+
+require __DIR__ . '/vendor/autoload.php';
+
+use Brick\Math\Internal\Calculator;
+
+(new class(30) { // max digits
+    private $gmp;
+    private $bcmath;
+    private $native;
+
+    private $maxDigits;
+
+    public function __construct(int $maxDigits)
+    {
+        $this->gmp    = new Calculator\GmpCalculator();
+        $this->bcmath = new Calculator\BcMathCalculator();
+        $this->native = new Calculator\NativeCalculator();
+
+        $this->maxDigits = $maxDigits;
+    }
+
+    public function __invoke() : void
+    {
+        for (;;) {
+            $a = $this->generateRandomNumber();
+            $b = $this->generateRandomNumber();
+            $c = $this->generateRandomNumber();
+
+            $this->runTests($a, $b);
+            $this->runTests($b, $a);
+
+            if ($a !== '0') {
+                $this->runTests("-$a", $b);
+                $this->runTests($b, "-$a");
+            }
+
+            if ($b !== '0') {
+                $this->runTests($a, "-$b");
+                $this->runTests("-$b", $a);
+            }
+
+            if ($a !== '0' && $b !== '0') {
+                $this->runTests("-$a", "-$b");
+                $this->runTests("-$b", "-$a");
+            }
+
+            if ($c !== '0') {
+                $this->test("$a POW $b MOD $c", function(Calculator $calc) use($a, $b, $c) {
+                    return $calc->modPow($a, $b, $c);
+                });
+            }
+        }
+    }
+
+    /**
+     * @param string $a The left operand.
+     * @param string $b The right operand.
+     */
+    private function runTests(string $a, string $b) : void
+    {
+        $this->test("$a + $b", function(Calculator $c) use($a, $b) {
+            return $c->add($a, $b);
+        });
+
+        $this->test("$a - $b", function(Calculator $c) use($a, $b) {
+            return $c->sub($a, $b);
+        });
+
+        $this->test("$a * $b", function(Calculator $c) use($a, $b) {
+            return $c->mul($a, $b);
+        });
+
+        if ($b !== '0') {
+            $this->test("$a / $b", function(Calculator $c) use($a, $b) {
+                return $c->divQR($a, $b);
+            });
+
+            $this->test("$a MOD $b", function(Calculator $c) use($a, $b) {
+                return $c->mod($a, $b);
+            });
+        }
+
+        if ($b !== '0' && $b[0] !== '-') {
+            $this->test("INV $a MOD $b", function(Calculator $c) use($a, $b) {
+                return $c->modInverse($a, $b);
+            });
+        }
+
+        $this->test("GCD $a, $b", function(Calculator $c) use($a, $b) {
+            return $c->gcd($a, $b);
+        });
+
+        if ($a[0] !== '-') {
+            $this->test("SQRT $a", function(Calculator $c) use($a, $b) {
+                return $c->sqrt($a);
+            });
+        }
+
+        $this->test("$a AND $b", function(Calculator $c) use($a, $b) {
+            return $c->and($a, $b);
+        });
+
+        $this->test("$a OR $b", function(Calculator $c) use($a, $b) {
+            return $c->or($a, $b);
+        });
+
+        $this->test("$a XOR $b", function(Calculator $c) use($a, $b) {
+            return $c->xor($a, $b);
+        });
+    }
+
+    /**
+     * @param string  $test     A string representing the test being executed.
+     * @param Closure $callback A callback function accepting a Calculator instance and returning a calculation result.
+     */
+    private function test(string $test, Closure $callback) : void
+    {
+        static $testCounter = 0;
+        static $lastOutputTime = 0.0;
+        static $currentSecond = 0;
+        static $currentSecondTestCounter = 0;
+        static $testsPerSecond = 0;
+
+        $gmpResult    = $callback($this->gmp);
+        $bcmathResult = $callback($this->bcmath);
+        $nativeResult = $callback($this->native);
+
+        if ($gmpResult !== $bcmathResult) {
+            self::failure('GMP', 'BCMath', $test);
+        }
+
+        if ($gmpResult !== $nativeResult) {
+            self::failure('GMP', 'Native', $test);
+        }
+
+        $testCounter++;
+        $currentSecondTestCounter++;
+
+        $time = microtime(true);
+        $second = (int) $time;
+
+        if ($second !== $currentSecond) {
+            $currentSecond = $second;
+            $testsPerSecond = $currentSecondTestCounter;
+            $currentSecondTestCounter = 0;
+        }
+
+        if ($time - $lastOutputTime >= 0.1) {
+            echo "\r", number_format($testCounter), ' (', number_format($testsPerSecond) . ' / s)';
+            $lastOutputTime = $time;
+        }
+    }
+
+    /**
+     * @param string $c1   The name of the first calculator.
+     * @param string $c2   The name of the second calculator.
+     * @param string $test A string representing the test being executed.
+     */
+    private static function failure(string $c1, string $c2, string $test) : void
+    {
+        echo PHP_EOL;
+        echo 'FAILURE!', PHP_EOL;
+        echo $c1, ' vs ', $c2, PHP_EOL;
+        echo $test, PHP_EOL;
+        die;
+    }
+
+    private function generateRandomNumber() : string
+    {
+        $length = random_int(1, $this->maxDigits);
+
+        $number = '';
+
+        for ($i = 0; $i < $length; $i++) {
+            $number .= random_int(0, 9);
+        }
+
+        $number = ltrim($number, '0');
+
+        if ($number === '') {
+            return '0';
+        }
+
+        return $number;
+    }
+})();

+ 861 - 0
vendor/brick/math/src/BigDecimal.php

@@ -0,0 +1,861 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math;
+
+use Brick\Math\Exception\DivisionByZeroException;
+use Brick\Math\Exception\MathException;
+use Brick\Math\Exception\NegativeNumberException;
+use Brick\Math\Internal\Calculator;
+
+/**
+ * Immutable, arbitrary-precision signed decimal numbers.
+ *
+ * @psalm-immutable
+ */
+final class BigDecimal extends BigNumber
+{
+    /**
+     * The unscaled value of this decimal number.
+     *
+     * This is a string of digits with an optional leading minus sign.
+     * No leading zero must be present.
+     * No leading minus sign must be present if the value is 0.
+     *
+     * @var string
+     */
+    private $value;
+
+    /**
+     * The scale (number of digits after the decimal point) of this decimal number.
+     *
+     * This must be zero or more.
+     *
+     * @var int
+     */
+    private $scale;
+
+    /**
+     * Protected constructor. Use a factory method to obtain an instance.
+     *
+     * @param string $value The unscaled value, validated.
+     * @param int    $scale The scale, validated.
+     */
+    protected function __construct(string $value, int $scale = 0)
+    {
+        $this->value = $value;
+        $this->scale = $scale;
+    }
+
+    /**
+     * Creates a BigDecimal of the given value.
+     *
+     * @param BigNumber|int|float|string $value
+     *
+     * @return BigDecimal
+     *
+     * @throws MathException If the value cannot be converted to a BigDecimal.
+     *
+     * @psalm-pure
+     */
+    public static function of($value) : BigNumber
+    {
+        return parent::of($value)->toBigDecimal();
+    }
+
+    /**
+     * Creates a BigDecimal from an unscaled value and a scale.
+     *
+     * Example: `(12345, 3)` will result in the BigDecimal `12.345`.
+     *
+     * @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger.
+     * @param int                        $scale The scale of the number, positive or zero.
+     *
+     * @return BigDecimal
+     *
+     * @throws \InvalidArgumentException If the scale is negative.
+     *
+     * @psalm-pure
+     */
+    public static function ofUnscaledValue($value, int $scale = 0) : BigDecimal
+    {
+        if ($scale < 0) {
+            throw new \InvalidArgumentException('The scale cannot be negative.');
+        }
+
+        return new BigDecimal((string) BigInteger::of($value), $scale);
+    }
+
+    /**
+     * Returns a BigDecimal representing zero, with a scale of zero.
+     *
+     * @return BigDecimal
+     *
+     * @psalm-pure
+     */
+    public static function zero() : BigDecimal
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigDecimal|null $zero
+         */
+        static $zero;
+
+        if ($zero === null) {
+            $zero = new BigDecimal('0');
+        }
+
+        return $zero;
+    }
+
+    /**
+     * Returns a BigDecimal representing one, with a scale of zero.
+     *
+     * @return BigDecimal
+     *
+     * @psalm-pure
+     */
+    public static function one() : BigDecimal
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigDecimal|null $one
+         */
+        static $one;
+
+        if ($one === null) {
+            $one = new BigDecimal('1');
+        }
+
+        return $one;
+    }
+
+    /**
+     * Returns a BigDecimal representing ten, with a scale of zero.
+     *
+     * @return BigDecimal
+     *
+     * @psalm-pure
+     */
+    public static function ten() : BigDecimal
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigDecimal|null $ten
+         */
+        static $ten;
+
+        if ($ten === null) {
+            $ten = new BigDecimal('10');
+        }
+
+        return $ten;
+    }
+
+    /**
+     * Returns the sum of this number and the given one.
+     *
+     * The result has a scale of `max($this->scale, $that->scale)`.
+     *
+     * @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigDecimal.
+     *
+     * @return BigDecimal The result.
+     *
+     * @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
+     */
+    public function plus($that) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->value === '0' && $that->scale <= $this->scale) {
+            return $this;
+        }
+
+        if ($this->value === '0' && $this->scale <= $that->scale) {
+            return $that;
+        }
+
+        [$a, $b] = $this->scaleValues($this, $that);
+
+        $value = Calculator::get()->add($a, $b);
+        $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
+
+        return new BigDecimal($value, $scale);
+    }
+
+    /**
+     * Returns the difference of this number and the given one.
+     *
+     * The result has a scale of `max($this->scale, $that->scale)`.
+     *
+     * @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigDecimal.
+     *
+     * @return BigDecimal The result.
+     *
+     * @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
+     */
+    public function minus($that) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->value === '0' && $that->scale <= $this->scale) {
+            return $this;
+        }
+
+        [$a, $b] = $this->scaleValues($this, $that);
+
+        $value = Calculator::get()->sub($a, $b);
+        $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
+
+        return new BigDecimal($value, $scale);
+    }
+
+    /**
+     * Returns the product of this number and the given one.
+     *
+     * The result has a scale of `$this->scale + $that->scale`.
+     *
+     * @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigDecimal.
+     *
+     * @return BigDecimal The result.
+     *
+     * @throws MathException If the multiplier is not a valid number, or is not convertible to a BigDecimal.
+     */
+    public function multipliedBy($that) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->value === '1' && $that->scale === 0) {
+            return $this;
+        }
+
+        if ($this->value === '1' && $this->scale === 0) {
+            return $that;
+        }
+
+        $value = Calculator::get()->mul($this->value, $that->value);
+        $scale = $this->scale + $that->scale;
+
+        return new BigDecimal($value, $scale);
+    }
+
+    /**
+     * Returns the result of the division of this number by the given one, at the given scale.
+     *
+     * @param BigNumber|int|float|string $that         The divisor.
+     * @param int|null                   $scale        The desired scale, or null to use the scale of this number.
+     * @param int                        $roundingMode An optional rounding mode.
+     *
+     * @return BigDecimal
+     *
+     * @throws \InvalidArgumentException If the scale or rounding mode is invalid.
+     * @throws MathException             If the number is invalid, is zero, or rounding was necessary.
+     */
+    public function dividedBy($that, ?int $scale = null, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->isZero()) {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        if ($scale === null) {
+            $scale = $this->scale;
+        } elseif ($scale < 0) {
+            throw new \InvalidArgumentException('Scale cannot be negative.');
+        }
+
+        if ($that->value === '1' && $that->scale === 0 && $scale === $this->scale) {
+            return $this;
+        }
+
+        $p = $this->valueWithMinScale($that->scale + $scale);
+        $q = $that->valueWithMinScale($this->scale - $scale);
+
+        $result = Calculator::get()->divRound($p, $q, $roundingMode);
+
+        return new BigDecimal($result, $scale);
+    }
+
+    /**
+     * Returns the exact result of the division of this number by the given one.
+     *
+     * The scale of the result is automatically calculated to fit all the fraction digits.
+     *
+     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
+     *
+     * @return BigDecimal The result.
+     *
+     * @throws MathException If the divisor is not a valid number, is not convertible to a BigDecimal, is zero,
+     *                       or the result yields an infinite number of digits.
+     */
+    public function exactlyDividedBy($that) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->value === '0') {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        [, $b] = $this->scaleValues($this, $that);
+
+        $d = \rtrim($b, '0');
+        $scale = \strlen($b) - \strlen($d);
+
+        $calculator = Calculator::get();
+
+        foreach ([5, 2] as $prime) {
+            for (;;) {
+                $lastDigit = (int) $d[-1];
+
+                if ($lastDigit % $prime !== 0) {
+                    break;
+                }
+
+                $d = $calculator->divQ($d, (string) $prime);
+                $scale++;
+            }
+        }
+
+        return $this->dividedBy($that, $scale)->stripTrailingZeros();
+    }
+
+    /**
+     * Returns this number exponentiated to the given value.
+     *
+     * The result has a scale of `$this->scale * $exponent`.
+     *
+     * @param int $exponent The exponent.
+     *
+     * @return BigDecimal The result.
+     *
+     * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
+     */
+    public function power(int $exponent) : BigDecimal
+    {
+        if ($exponent === 0) {
+            return BigDecimal::one();
+        }
+
+        if ($exponent === 1) {
+            return $this;
+        }
+
+        if ($exponent < 0 || $exponent > Calculator::MAX_POWER) {
+            throw new \InvalidArgumentException(\sprintf(
+                'The exponent %d is not in the range 0 to %d.',
+                $exponent,
+                Calculator::MAX_POWER
+            ));
+        }
+
+        return new BigDecimal(Calculator::get()->pow($this->value, $exponent), $this->scale * $exponent);
+    }
+
+    /**
+     * Returns the quotient of the division of this number by this given one.
+     *
+     * The quotient has a scale of `0`.
+     *
+     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
+     *
+     * @return BigDecimal The quotient.
+     *
+     * @throws MathException If the divisor is not a valid decimal number, or is zero.
+     */
+    public function quotient($that) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->isZero()) {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        $p = $this->valueWithMinScale($that->scale);
+        $q = $that->valueWithMinScale($this->scale);
+
+        $quotient = Calculator::get()->divQ($p, $q);
+
+        return new BigDecimal($quotient, 0);
+    }
+
+    /**
+     * Returns the remainder of the division of this number by this given one.
+     *
+     * The remainder has a scale of `max($this->scale, $that->scale)`.
+     *
+     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
+     *
+     * @return BigDecimal The remainder.
+     *
+     * @throws MathException If the divisor is not a valid decimal number, or is zero.
+     */
+    public function remainder($that) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->isZero()) {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        $p = $this->valueWithMinScale($that->scale);
+        $q = $that->valueWithMinScale($this->scale);
+
+        $remainder = Calculator::get()->divR($p, $q);
+
+        $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
+
+        return new BigDecimal($remainder, $scale);
+    }
+
+    /**
+     * Returns the quotient and remainder of the division of this number by the given one.
+     *
+     * The quotient has a scale of `0`, and the remainder has a scale of `max($this->scale, $that->scale)`.
+     *
+     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
+     *
+     * @return BigDecimal[] An array containing the quotient and the remainder.
+     *
+     * @throws MathException If the divisor is not a valid decimal number, or is zero.
+     */
+    public function quotientAndRemainder($that) : array
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->isZero()) {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        $p = $this->valueWithMinScale($that->scale);
+        $q = $that->valueWithMinScale($this->scale);
+
+        [$quotient, $remainder] = Calculator::get()->divQR($p, $q);
+
+        $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
+
+        $quotient = new BigDecimal($quotient, 0);
+        $remainder = new BigDecimal($remainder, $scale);
+
+        return [$quotient, $remainder];
+    }
+
+    /**
+     * Returns the square root of this number, rounded down to the given number of decimals.
+     *
+     * @param int $scale
+     *
+     * @return BigDecimal
+     *
+     * @throws \InvalidArgumentException If the scale is negative.
+     * @throws NegativeNumberException If this number is negative.
+     */
+    public function sqrt(int $scale) : BigDecimal
+    {
+        if ($scale < 0) {
+            throw new \InvalidArgumentException('Scale cannot be negative.');
+        }
+
+        if ($this->value === '0') {
+            return new BigDecimal('0', $scale);
+        }
+
+        if ($this->value[0] === '-') {
+            throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
+        }
+
+        $value = $this->value;
+        $addDigits = 2 * $scale - $this->scale;
+
+        if ($addDigits > 0) {
+            // add zeros
+            $value .= \str_repeat('0', $addDigits);
+        } elseif ($addDigits < 0) {
+            // trim digits
+            if (-$addDigits >= \strlen($this->value)) {
+                // requesting a scale too low, will always yield a zero result
+                return new BigDecimal('0', $scale);
+            }
+
+            $value = \substr($value, 0, $addDigits);
+        }
+
+        $value = Calculator::get()->sqrt($value);
+
+        return new BigDecimal($value, $scale);
+    }
+
+    /**
+     * Returns a copy of this BigDecimal with the decimal point moved $n places to the left.
+     *
+     * @param int $n
+     *
+     * @return BigDecimal
+     */
+    public function withPointMovedLeft(int $n) : BigDecimal
+    {
+        if ($n === 0) {
+            return $this;
+        }
+
+        if ($n < 0) {
+            return $this->withPointMovedRight(-$n);
+        }
+
+        return new BigDecimal($this->value, $this->scale + $n);
+    }
+
+    /**
+     * Returns a copy of this BigDecimal with the decimal point moved $n places to the right.
+     *
+     * @param int $n
+     *
+     * @return BigDecimal
+     */
+    public function withPointMovedRight(int $n) : BigDecimal
+    {
+        if ($n === 0) {
+            return $this;
+        }
+
+        if ($n < 0) {
+            return $this->withPointMovedLeft(-$n);
+        }
+
+        $value = $this->value;
+        $scale = $this->scale - $n;
+
+        if ($scale < 0) {
+            if ($value !== '0') {
+                $value .= \str_repeat('0', -$scale);
+            }
+            $scale = 0;
+        }
+
+        return new BigDecimal($value, $scale);
+    }
+
+    /**
+     * Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part.
+     *
+     * @return BigDecimal
+     */
+    public function stripTrailingZeros() : BigDecimal
+    {
+        if ($this->scale === 0) {
+            return $this;
+        }
+
+        $trimmedValue = \rtrim($this->value, '0');
+
+        if ($trimmedValue === '') {
+            return BigDecimal::zero();
+        }
+
+        $trimmableZeros = \strlen($this->value) - \strlen($trimmedValue);
+
+        if ($trimmableZeros === 0) {
+            return $this;
+        }
+
+        if ($trimmableZeros > $this->scale) {
+            $trimmableZeros = $this->scale;
+        }
+
+        $value = \substr($this->value, 0, -$trimmableZeros);
+        $scale = $this->scale - $trimmableZeros;
+
+        return new BigDecimal($value, $scale);
+    }
+
+    /**
+     * Returns the absolute value of this number.
+     *
+     * @return BigDecimal
+     */
+    public function abs() : BigDecimal
+    {
+        return $this->isNegative() ? $this->negated() : $this;
+    }
+
+    /**
+     * Returns the negated value of this number.
+     *
+     * @return BigDecimal
+     */
+    public function negated() : BigDecimal
+    {
+        return new BigDecimal(Calculator::get()->neg($this->value), $this->scale);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function compareTo($that) : int
+    {
+        $that = BigNumber::of($that);
+
+        if ($that instanceof BigInteger) {
+            $that = $that->toBigDecimal();
+        }
+
+        if ($that instanceof BigDecimal) {
+            [$a, $b] = $this->scaleValues($this, $that);
+
+            return Calculator::get()->cmp($a, $b);
+        }
+
+        return - $that->compareTo($this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSign() : int
+    {
+        return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
+    }
+
+    /**
+     * @return BigInteger
+     */
+    public function getUnscaledValue() : BigInteger
+    {
+        return BigInteger::create($this->value);
+    }
+
+    /**
+     * @return int
+     */
+    public function getScale() : int
+    {
+        return $this->scale;
+    }
+
+    /**
+     * Returns a string representing the integral part of this decimal number.
+     *
+     * Example: `-123.456` => `-123`.
+     *
+     * @return string
+     */
+    public function getIntegralPart() : string
+    {
+        if ($this->scale === 0) {
+            return $this->value;
+        }
+
+        $value = $this->getUnscaledValueWithLeadingZeros();
+
+        return \substr($value, 0, -$this->scale);
+    }
+
+    /**
+     * Returns a string representing the fractional part of this decimal number.
+     *
+     * If the scale is zero, an empty string is returned.
+     *
+     * Examples: `-123.456` => '456', `123` => ''.
+     *
+     * @return string
+     */
+    public function getFractionalPart() : string
+    {
+        if ($this->scale === 0) {
+            return '';
+        }
+
+        $value = $this->getUnscaledValueWithLeadingZeros();
+
+        return \substr($value, -$this->scale);
+    }
+
+    /**
+     * Returns whether this decimal number has a non-zero fractional part.
+     *
+     * @return bool
+     */
+    public function hasNonZeroFractionalPart() : bool
+    {
+        return $this->getFractionalPart() !== \str_repeat('0', $this->scale);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toBigInteger() : BigInteger
+    {
+        $zeroScaleDecimal = $this->scale === 0 ? $this : $this->dividedBy(1, 0);
+
+        return BigInteger::create($zeroScaleDecimal->value);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toBigDecimal() : BigDecimal
+    {
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toBigRational() : BigRational
+    {
+        $numerator = BigInteger::create($this->value);
+        $denominator = BigInteger::create('1' . \str_repeat('0', $this->scale));
+
+        return BigRational::create($numerator, $denominator, false);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
+    {
+        if ($scale === $this->scale) {
+            return $this;
+        }
+
+        return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toInt() : int
+    {
+        return $this->toBigInteger()->toInt();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toFloat() : float
+    {
+        return (float) (string) $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString() : string
+    {
+        if ($this->scale === 0) {
+            return $this->value;
+        }
+
+        $value = $this->getUnscaledValueWithLeadingZeros();
+
+        return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale);
+    }
+
+    /**
+     * This method is required by interface Serializable and SHOULD NOT be accessed directly.
+     *
+     * @internal
+     *
+     * @return string
+     */
+    public function serialize() : string
+    {
+        return $this->value . ':' . $this->scale;
+    }
+
+    /**
+     * This method is only here to implement interface Serializable and cannot be accessed directly.
+     *
+     * @internal
+     * @psalm-suppress RedundantPropertyInitializationCheck
+     *
+     * @param string $value
+     *
+     * @return void
+     *
+     * @throws \LogicException
+     */
+    public function unserialize($value) : void
+    {
+        if (isset($this->value)) {
+            throw new \LogicException('unserialize() is an internal function, it must not be called directly.');
+        }
+
+        [$value, $scale] = \explode(':', $value);
+
+        $this->value = $value;
+        $this->scale = (int) $scale;
+    }
+
+    /**
+     * Puts the internal values of the given decimal numbers on the same scale.
+     *
+     * @param BigDecimal $x The first decimal number.
+     * @param BigDecimal $y The second decimal number.
+     *
+     * @return array{0: string, 1: string} The scaled integer values of $x and $y.
+     */
+    private function scaleValues(BigDecimal $x, BigDecimal $y) : array
+    {
+        $a = $x->value;
+        $b = $y->value;
+
+        if ($b !== '0' && $x->scale > $y->scale) {
+            $b .= \str_repeat('0', $x->scale - $y->scale);
+        } elseif ($a !== '0' && $x->scale < $y->scale) {
+            $a .= \str_repeat('0', $y->scale - $x->scale);
+        }
+
+        return [$a, $b];
+    }
+
+    /**
+     * @param int $scale
+     *
+     * @return string
+     */
+    private function valueWithMinScale(int $scale) : string
+    {
+        $value = $this->value;
+
+        if ($this->value !== '0' && $scale > $this->scale) {
+            $value .= \str_repeat('0', $scale - $this->scale);
+        }
+
+        return $value;
+    }
+
+    /**
+     * Adds leading zeros if necessary to the unscaled value to represent the full decimal number.
+     *
+     * @return string
+     */
+    private function getUnscaledValueWithLeadingZeros() : string
+    {
+        $value = $this->value;
+        $targetLength = $this->scale + 1;
+        $negative = ($value[0] === '-');
+        $length = \strlen($value);
+
+        if ($negative) {
+            $length--;
+        }
+
+        if ($length >= $targetLength) {
+            return $this->value;
+        }
+
+        if ($negative) {
+            $value = \substr($value, 1);
+        }
+
+        $value = \str_pad($value, $targetLength, '0', STR_PAD_LEFT);
+
+        if ($negative) {
+            $value = '-' . $value;
+        }
+
+        return $value;
+    }
+}

File diff suppressed because it is too large
+ 1151 - 0
vendor/brick/math/src/BigInteger.php


+ 572 - 0
vendor/brick/math/src/BigNumber.php

@@ -0,0 +1,572 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math;
+
+use Brick\Math\Exception\DivisionByZeroException;
+use Brick\Math\Exception\MathException;
+use Brick\Math\Exception\NumberFormatException;
+use Brick\Math\Exception\RoundingNecessaryException;
+
+/**
+ * Common interface for arbitrary-precision rational numbers.
+ *
+ * @psalm-immutable
+ */
+abstract class BigNumber implements \Serializable, \JsonSerializable
+{
+    /**
+     * The regular expression used to parse integer, decimal and rational numbers.
+     */
+    private const PARSE_REGEXP =
+        '/^' .
+            '(?<sign>[\-\+])?' .
+            '(?:' .
+                '(?:' .
+                    '(?<integral>[0-9]+)?' .
+                    '(?<point>\.)?' .
+                    '(?<fractional>[0-9]+)?' .
+                    '(?:[eE](?<exponent>[\-\+]?[0-9]+))?' .
+                ')|(?:' .
+                    '(?<numerator>[0-9]+)' .
+                    '\/?' .
+                    '(?<denominator>[0-9]+)' .
+                ')' .
+            ')' .
+        '$/';
+
+    /**
+     * Creates a BigNumber of the given value.
+     *
+     * The concrete return type is dependent on the given value, with the following rules:
+     *
+     * - BigNumber instances are returned as is
+     * - integer numbers are returned as BigInteger
+     * - floating point numbers are converted to a string then parsed as such
+     * - strings containing a `/` character are returned as BigRational
+     * - strings containing a `.` character or using an exponential notation are returned as BigDecimal
+     * - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger
+     *
+     * @param BigNumber|int|float|string $value
+     *
+     * @return BigNumber
+     *
+     * @throws NumberFormatException   If the format of the number is not valid.
+     * @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
+     *
+     * @psalm-pure
+     */
+    public static function of($value) : BigNumber
+    {
+        if ($value instanceof BigNumber) {
+            return $value;
+        }
+
+        if (\is_int($value)) {
+            return new BigInteger((string) $value);
+        }
+
+        /** @psalm-suppress RedundantCastGivenDocblockType We cannot trust the untyped $value here! */
+        $value = \is_float($value) ? self::floatToString($value) : (string) $value;
+
+        $throw = static function() use ($value) : void {
+            throw new NumberFormatException(\sprintf(
+                'The given value "%s" does not represent a valid number.',
+                $value
+            ));
+        };
+
+        if (\preg_match(self::PARSE_REGEXP, $value, $matches) !== 1) {
+            $throw();
+        }
+
+        $getMatch = static function(string $value) use ($matches) : ?string {
+            return isset($matches[$value]) && $matches[$value] !== '' ? $matches[$value] : null;
+        };
+
+        $sign        = $getMatch('sign');
+        $numerator   = $getMatch('numerator');
+        $denominator = $getMatch('denominator');
+
+        if ($numerator !== null) {
+            assert($denominator !== null);
+
+            if ($sign !== null) {
+                $numerator = $sign . $numerator;
+            }
+
+            $numerator   = self::cleanUp($numerator);
+            $denominator = self::cleanUp($denominator);
+
+            if ($denominator === '0') {
+                throw DivisionByZeroException::denominatorMustNotBeZero();
+            }
+
+            return new BigRational(
+                new BigInteger($numerator),
+                new BigInteger($denominator),
+                false
+            );
+        }
+
+        $point      = $getMatch('point');
+        $integral   = $getMatch('integral');
+        $fractional = $getMatch('fractional');
+        $exponent   = $getMatch('exponent');
+
+        if ($integral === null && $fractional === null) {
+            $throw();
+        }
+
+        if ($integral === null) {
+            $integral = '0';
+        }
+
+        if ($point !== null || $exponent !== null) {
+            $fractional = ($fractional ?? '');
+            $exponent = ($exponent !== null) ? (int) $exponent : 0;
+
+            if ($exponent === PHP_INT_MIN || $exponent === PHP_INT_MAX) {
+                throw new NumberFormatException('Exponent too large.');
+            }
+
+            $unscaledValue = self::cleanUp(($sign ?? ''). $integral . $fractional);
+
+            $scale = \strlen($fractional) - $exponent;
+
+            if ($scale < 0) {
+                if ($unscaledValue !== '0') {
+                    $unscaledValue .= \str_repeat('0', - $scale);
+                }
+                $scale = 0;
+            }
+
+            return new BigDecimal($unscaledValue, $scale);
+        }
+
+        $integral = self::cleanUp(($sign ?? '') . $integral);
+
+        return new BigInteger($integral);
+    }
+
+    /**
+     * Safely converts float to string, avoiding locale-dependent issues.
+     *
+     * @see https://github.com/brick/math/pull/20
+     *
+     * @param float $float
+     *
+     * @return string
+     *
+     * @psalm-pure
+     * @psalm-suppress ImpureFunctionCall
+     */
+    private static function floatToString(float $float) : string
+    {
+        $currentLocale = \setlocale(LC_NUMERIC, '0');
+        \setlocale(LC_NUMERIC, 'C');
+
+        $result = (string) $float;
+
+        \setlocale(LC_NUMERIC, $currentLocale);
+
+        return $result;
+    }
+
+    /**
+     * Proxy method to access protected constructors from sibling classes.
+     *
+     * @internal
+     *
+     * @param mixed ...$args The arguments to the constructor.
+     *
+     * @return static
+     *
+     * @psalm-pure
+     * @psalm-suppress TooManyArguments
+     * @psalm-suppress UnsafeInstantiation
+     */
+    protected static function create(... $args) : BigNumber
+    {
+        return new static(... $args);
+    }
+
+    /**
+     * Returns the minimum of the given values.
+     *
+     * @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
+     *                                              to an instance of the class this method is called on.
+     *
+     * @return static The minimum value.
+     *
+     * @throws \InvalidArgumentException If no values are given.
+     * @throws MathException             If an argument is not valid.
+     *
+     * @psalm-suppress LessSpecificReturnStatement
+     * @psalm-suppress MoreSpecificReturnType
+     * @psalm-pure
+     */
+    public static function min(...$values) : BigNumber
+    {
+        $min = null;
+
+        foreach ($values as $value) {
+            $value = static::of($value);
+
+            if ($min === null || $value->isLessThan($min)) {
+                $min = $value;
+            }
+        }
+
+        if ($min === null) {
+            throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
+        }
+
+        return $min;
+    }
+
+    /**
+     * Returns the maximum of the given values.
+     *
+     * @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
+     *                                              to an instance of the class this method is called on.
+     *
+     * @return static The maximum value.
+     *
+     * @throws \InvalidArgumentException If no values are given.
+     * @throws MathException             If an argument is not valid.
+     *
+     * @psalm-suppress LessSpecificReturnStatement
+     * @psalm-suppress MoreSpecificReturnType
+     * @psalm-pure
+     */
+    public static function max(...$values) : BigNumber
+    {
+        $max = null;
+
+        foreach ($values as $value) {
+            $value = static::of($value);
+
+            if ($max === null || $value->isGreaterThan($max)) {
+                $max = $value;
+            }
+        }
+
+        if ($max === null) {
+            throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
+        }
+
+        return $max;
+    }
+
+    /**
+     * Returns the sum of the given values.
+     *
+     * @param BigNumber|int|float|string ...$values The numbers to add. All the numbers need to be convertible
+     *                                              to an instance of the class this method is called on.
+     *
+     * @return static The sum.
+     *
+     * @throws \InvalidArgumentException If no values are given.
+     * @throws MathException             If an argument is not valid.
+     *
+     * @psalm-suppress LessSpecificReturnStatement
+     * @psalm-suppress MoreSpecificReturnType
+     * @psalm-pure
+     */
+    public static function sum(...$values) : BigNumber
+    {
+        /** @var BigNumber|null $sum */
+        $sum = null;
+
+        foreach ($values as $value) {
+            $value = static::of($value);
+
+            $sum = $sum === null ? $value : self::add($sum, $value);
+        }
+
+        if ($sum === null) {
+            throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
+        }
+
+        return $sum;
+    }
+
+    /**
+     * Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException.
+     *
+     * @todo This could be better resolved by creating an abstract protected method in BigNumber, and leaving to
+     *       concrete classes the responsibility to perform the addition themselves or delegate it to the given number,
+     *       depending on their ability to perform the operation. This will also require a version bump because we're
+     *       potentially breaking custom BigNumber implementations (if any...)
+     *
+     * @param BigNumber $a
+     * @param BigNumber $b
+     *
+     * @return BigNumber
+     *
+     * @psalm-pure
+     */
+    private static function add(BigNumber $a, BigNumber $b) : BigNumber
+    {
+        if ($a instanceof BigRational) {
+            return $a->plus($b);
+        }
+
+        if ($b instanceof BigRational) {
+            return $b->plus($a);
+        }
+
+        if ($a instanceof BigDecimal) {
+            return $a->plus($b);
+        }
+
+        if ($b instanceof BigDecimal) {
+            return $b->plus($a);
+        }
+
+        /** @var BigInteger $a */
+
+        return $a->plus($b);
+    }
+
+    /**
+     * Removes optional leading zeros and + sign from the given number.
+     *
+     * @param string $number The number, validated as a non-empty string of digits with optional leading sign.
+     *
+     * @return string
+     *
+     * @psalm-pure
+     */
+    private static function cleanUp(string $number) : string
+    {
+        $firstChar = $number[0];
+
+        if ($firstChar === '+' || $firstChar === '-') {
+            $number = \substr($number, 1);
+        }
+
+        $number = \ltrim($number, '0');
+
+        if ($number === '') {
+            return '0';
+        }
+
+        if ($firstChar === '-') {
+            return '-' . $number;
+        }
+
+        return $number;
+    }
+
+    /**
+     * Checks if this number is equal to the given one.
+     *
+     * @param BigNumber|int|float|string $that
+     *
+     * @return bool
+     */
+    public function isEqualTo($that) : bool
+    {
+        return $this->compareTo($that) === 0;
+    }
+
+    /**
+     * Checks if this number is strictly lower than the given one.
+     *
+     * @param BigNumber|int|float|string $that
+     *
+     * @return bool
+     */
+    public function isLessThan($that) : bool
+    {
+        return $this->compareTo($that) < 0;
+    }
+
+    /**
+     * Checks if this number is lower than or equal to the given one.
+     *
+     * @param BigNumber|int|float|string $that
+     *
+     * @return bool
+     */
+    public function isLessThanOrEqualTo($that) : bool
+    {
+        return $this->compareTo($that) <= 0;
+    }
+
+    /**
+     * Checks if this number is strictly greater than the given one.
+     *
+     * @param BigNumber|int|float|string $that
+     *
+     * @return bool
+     */
+    public function isGreaterThan($that) : bool
+    {
+        return $this->compareTo($that) > 0;
+    }
+
+    /**
+     * Checks if this number is greater than or equal to the given one.
+     *
+     * @param BigNumber|int|float|string $that
+     *
+     * @return bool
+     */
+    public function isGreaterThanOrEqualTo($that) : bool
+    {
+        return $this->compareTo($that) >= 0;
+    }
+
+    /**
+     * Checks if this number equals zero.
+     *
+     * @return bool
+     */
+    public function isZero() : bool
+    {
+        return $this->getSign() === 0;
+    }
+
+    /**
+     * Checks if this number is strictly negative.
+     *
+     * @return bool
+     */
+    public function isNegative() : bool
+    {
+        return $this->getSign() < 0;
+    }
+
+    /**
+     * Checks if this number is negative or zero.
+     *
+     * @return bool
+     */
+    public function isNegativeOrZero() : bool
+    {
+        return $this->getSign() <= 0;
+    }
+
+    /**
+     * Checks if this number is strictly positive.
+     *
+     * @return bool
+     */
+    public function isPositive() : bool
+    {
+        return $this->getSign() > 0;
+    }
+
+    /**
+     * Checks if this number is positive or zero.
+     *
+     * @return bool
+     */
+    public function isPositiveOrZero() : bool
+    {
+        return $this->getSign() >= 0;
+    }
+
+    /**
+     * Returns the sign of this number.
+     *
+     * @return int -1 if the number is negative, 0 if zero, 1 if positive.
+     */
+    abstract public function getSign() : int;
+
+    /**
+     * Compares this number to the given one.
+     *
+     * @param BigNumber|int|float|string $that
+     *
+     * @return int [-1,0,1] If `$this` is lower than, equal to, or greater than `$that`.
+     *
+     * @throws MathException If the number is not valid.
+     */
+    abstract public function compareTo($that) : int;
+
+    /**
+     * Converts this number to a BigInteger.
+     *
+     * @return BigInteger The converted number.
+     *
+     * @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding.
+     */
+    abstract public function toBigInteger() : BigInteger;
+
+    /**
+     * Converts this number to a BigDecimal.
+     *
+     * @return BigDecimal The converted number.
+     *
+     * @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding.
+     */
+    abstract public function toBigDecimal() : BigDecimal;
+
+    /**
+     * Converts this number to a BigRational.
+     *
+     * @return BigRational The converted number.
+     */
+    abstract public function toBigRational() : BigRational;
+
+    /**
+     * Converts this number to a BigDecimal with the given scale, using rounding if necessary.
+     *
+     * @param int $scale        The scale of the resulting `BigDecimal`.
+     * @param int $roundingMode A `RoundingMode` constant.
+     *
+     * @return BigDecimal
+     *
+     * @throws RoundingNecessaryException If this number cannot be converted to the given scale without rounding.
+     *                                    This only applies when RoundingMode::UNNECESSARY is used.
+     */
+    abstract public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal;
+
+    /**
+     * Returns the exact value of this number as a native integer.
+     *
+     * If this number cannot be converted to a native integer without losing precision, an exception is thrown.
+     * Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit.
+     *
+     * @return int The converted value.
+     *
+     * @throws MathException If this number cannot be exactly converted to a native integer.
+     */
+    abstract public function toInt() : int;
+
+    /**
+     * Returns an approximation of this number as a floating-point value.
+     *
+     * Note that this method can discard information as the precision of a floating-point value
+     * is inherently limited.
+     *
+     * If the number is greater than the largest representable floating point number, positive infinity is returned.
+     * If the number is less than the smallest representable floating point number, negative infinity is returned.
+     *
+     * @return float The converted value.
+     */
+    abstract public function toFloat() : float;
+
+    /**
+     * Returns a string representation of this number.
+     *
+     * The output of this method can be parsed by the `of()` factory method;
+     * this will yield an object equal to this one, without any information loss.
+     *
+     * @return string
+     */
+    abstract public function __toString() : string;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function jsonSerialize() : string
+    {
+        return $this->__toString();
+    }
+}

+ 489 - 0
vendor/brick/math/src/BigRational.php

@@ -0,0 +1,489 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math;
+
+use Brick\Math\Exception\DivisionByZeroException;
+use Brick\Math\Exception\MathException;
+use Brick\Math\Exception\NumberFormatException;
+use Brick\Math\Exception\RoundingNecessaryException;
+
+/**
+ * An arbitrarily large rational number.
+ *
+ * This class is immutable.
+ *
+ * @psalm-immutable
+ */
+final class BigRational extends BigNumber
+{
+    /**
+     * The numerator.
+     *
+     * @var BigInteger
+     */
+    private $numerator;
+
+    /**
+     * The denominator. Always strictly positive.
+     *
+     * @var BigInteger
+     */
+    private $denominator;
+
+    /**
+     * Protected constructor. Use a factory method to obtain an instance.
+     *
+     * @param BigInteger $numerator        The numerator.
+     * @param BigInteger $denominator      The denominator.
+     * @param bool       $checkDenominator Whether to check the denominator for negative and zero.
+     *
+     * @throws DivisionByZeroException If the denominator is zero.
+     */
+    protected function __construct(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator)
+    {
+        if ($checkDenominator) {
+            if ($denominator->isZero()) {
+                throw DivisionByZeroException::denominatorMustNotBeZero();
+            }
+
+            if ($denominator->isNegative()) {
+                $numerator   = $numerator->negated();
+                $denominator = $denominator->negated();
+            }
+        }
+
+        $this->numerator   = $numerator;
+        $this->denominator = $denominator;
+    }
+
+    /**
+     * Creates a BigRational of the given value.
+     *
+     * @param BigNumber|int|float|string $value
+     *
+     * @return BigRational
+     *
+     * @throws MathException If the value cannot be converted to a BigRational.
+     *
+     * @psalm-pure
+     */
+    public static function of($value) : BigNumber
+    {
+        return parent::of($value)->toBigRational();
+    }
+
+    /**
+     * Creates a BigRational out of a numerator and a denominator.
+     *
+     * If the denominator is negative, the signs of both the numerator and the denominator
+     * will be inverted to ensure that the denominator is always positive.
+     *
+     * @param BigNumber|int|float|string $numerator   The numerator. Must be convertible to a BigInteger.
+     * @param BigNumber|int|float|string $denominator The denominator. Must be convertible to a BigInteger.
+     *
+     * @return BigRational
+     *
+     * @throws NumberFormatException      If an argument does not represent a valid number.
+     * @throws RoundingNecessaryException If an argument represents a non-integer number.
+     * @throws DivisionByZeroException    If the denominator is zero.
+     *
+     * @psalm-pure
+     */
+    public static function nd($numerator, $denominator) : BigRational
+    {
+        $numerator   = BigInteger::of($numerator);
+        $denominator = BigInteger::of($denominator);
+
+        return new BigRational($numerator, $denominator, true);
+    }
+
+    /**
+     * Returns a BigRational representing zero.
+     *
+     * @return BigRational
+     *
+     * @psalm-pure
+     */
+    public static function zero() : BigRational
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigRational|null $zero
+         */
+        static $zero;
+
+        if ($zero === null) {
+            $zero = new BigRational(BigInteger::zero(), BigInteger::one(), false);
+        }
+
+        return $zero;
+    }
+
+    /**
+     * Returns a BigRational representing one.
+     *
+     * @return BigRational
+     *
+     * @psalm-pure
+     */
+    public static function one() : BigRational
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigRational|null $one
+         */
+        static $one;
+
+        if ($one === null) {
+            $one = new BigRational(BigInteger::one(), BigInteger::one(), false);
+        }
+
+        return $one;
+    }
+
+    /**
+     * Returns a BigRational representing ten.
+     *
+     * @return BigRational
+     *
+     * @psalm-pure
+     */
+    public static function ten() : BigRational
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigRational|null $ten
+         */
+        static $ten;
+
+        if ($ten === null) {
+            $ten = new BigRational(BigInteger::ten(), BigInteger::one(), false);
+        }
+
+        return $ten;
+    }
+
+    /**
+     * @return BigInteger
+     */
+    public function getNumerator() : BigInteger
+    {
+        return $this->numerator;
+    }
+
+    /**
+     * @return BigInteger
+     */
+    public function getDenominator() : BigInteger
+    {
+        return $this->denominator;
+    }
+
+    /**
+     * Returns the quotient of the division of the numerator by the denominator.
+     *
+     * @return BigInteger
+     */
+    public function quotient() : BigInteger
+    {
+        return $this->numerator->quotient($this->denominator);
+    }
+
+    /**
+     * Returns the remainder of the division of the numerator by the denominator.
+     *
+     * @return BigInteger
+     */
+    public function remainder() : BigInteger
+    {
+        return $this->numerator->remainder($this->denominator);
+    }
+
+    /**
+     * Returns the quotient and remainder of the division of the numerator by the denominator.
+     *
+     * @return BigInteger[]
+     */
+    public function quotientAndRemainder() : array
+    {
+        return $this->numerator->quotientAndRemainder($this->denominator);
+    }
+
+    /**
+     * Returns the sum of this number and the given one.
+     *
+     * @param BigNumber|int|float|string $that The number to add.
+     *
+     * @return BigRational The result.
+     *
+     * @throws MathException If the number is not valid.
+     */
+    public function plus($that) : BigRational
+    {
+        $that = BigRational::of($that);
+
+        $numerator   = $this->numerator->multipliedBy($that->denominator);
+        $numerator   = $numerator->plus($that->numerator->multipliedBy($this->denominator));
+        $denominator = $this->denominator->multipliedBy($that->denominator);
+
+        return new BigRational($numerator, $denominator, false);
+    }
+
+    /**
+     * Returns the difference of this number and the given one.
+     *
+     * @param BigNumber|int|float|string $that The number to subtract.
+     *
+     * @return BigRational The result.
+     *
+     * @throws MathException If the number is not valid.
+     */
+    public function minus($that) : BigRational
+    {
+        $that = BigRational::of($that);
+
+        $numerator   = $this->numerator->multipliedBy($that->denominator);
+        $numerator   = $numerator->minus($that->numerator->multipliedBy($this->denominator));
+        $denominator = $this->denominator->multipliedBy($that->denominator);
+
+        return new BigRational($numerator, $denominator, false);
+    }
+
+    /**
+     * Returns the product of this number and the given one.
+     *
+     * @param BigNumber|int|float|string $that The multiplier.
+     *
+     * @return BigRational The result.
+     *
+     * @throws MathException If the multiplier is not a valid number.
+     */
+    public function multipliedBy($that) : BigRational
+    {
+        $that = BigRational::of($that);
+
+        $numerator   = $this->numerator->multipliedBy($that->numerator);
+        $denominator = $this->denominator->multipliedBy($that->denominator);
+
+        return new BigRational($numerator, $denominator, false);
+    }
+
+    /**
+     * Returns the result of the division of this number by the given one.
+     *
+     * @param BigNumber|int|float|string $that The divisor.
+     *
+     * @return BigRational The result.
+     *
+     * @throws MathException If the divisor is not a valid number, or is zero.
+     */
+    public function dividedBy($that) : BigRational
+    {
+        $that = BigRational::of($that);
+
+        $numerator   = $this->numerator->multipliedBy($that->denominator);
+        $denominator = $this->denominator->multipliedBy($that->numerator);
+
+        return new BigRational($numerator, $denominator, true);
+    }
+
+    /**
+     * Returns this number exponentiated to the given value.
+     *
+     * @param int $exponent The exponent.
+     *
+     * @return BigRational The result.
+     *
+     * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
+     */
+    public function power(int $exponent) : BigRational
+    {
+        if ($exponent === 0) {
+            $one = BigInteger::one();
+
+            return new BigRational($one, $one, false);
+        }
+
+        if ($exponent === 1) {
+            return $this;
+        }
+
+        return new BigRational(
+            $this->numerator->power($exponent),
+            $this->denominator->power($exponent),
+            false
+        );
+    }
+
+    /**
+     * Returns the reciprocal of this BigRational.
+     *
+     * The reciprocal has the numerator and denominator swapped.
+     *
+     * @return BigRational
+     *
+     * @throws DivisionByZeroException If the numerator is zero.
+     */
+    public function reciprocal() : BigRational
+    {
+        return new BigRational($this->denominator, $this->numerator, true);
+    }
+
+    /**
+     * Returns the absolute value of this BigRational.
+     *
+     * @return BigRational
+     */
+    public function abs() : BigRational
+    {
+        return new BigRational($this->numerator->abs(), $this->denominator, false);
+    }
+
+    /**
+     * Returns the negated value of this BigRational.
+     *
+     * @return BigRational
+     */
+    public function negated() : BigRational
+    {
+        return new BigRational($this->numerator->negated(), $this->denominator, false);
+    }
+
+    /**
+     * Returns the simplified value of this BigRational.
+     *
+     * @return BigRational
+     */
+    public function simplified() : BigRational
+    {
+        $gcd = $this->numerator->gcd($this->denominator);
+
+        $numerator = $this->numerator->quotient($gcd);
+        $denominator = $this->denominator->quotient($gcd);
+
+        return new BigRational($numerator, $denominator, false);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function compareTo($that) : int
+    {
+        return $this->minus($that)->getSign();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSign() : int
+    {
+        return $this->numerator->getSign();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toBigInteger() : BigInteger
+    {
+        $simplified = $this->simplified();
+
+        if (! $simplified->denominator->isEqualTo(1)) {
+            throw new RoundingNecessaryException('This rational number cannot be represented as an integer value without rounding.');
+        }
+
+        return $simplified->numerator;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toBigDecimal() : BigDecimal
+    {
+        return $this->numerator->toBigDecimal()->exactlyDividedBy($this->denominator);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toBigRational() : BigRational
+    {
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
+    {
+        return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toInt() : int
+    {
+        return $this->toBigInteger()->toInt();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toFloat() : float
+    {
+        return $this->numerator->toFloat() / $this->denominator->toFloat();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString() : string
+    {
+        $numerator   = (string) $this->numerator;
+        $denominator = (string) $this->denominator;
+
+        if ($denominator === '1') {
+            return $numerator;
+        }
+
+        return $this->numerator . '/' . $this->denominator;
+    }
+
+    /**
+     * This method is required by interface Serializable and SHOULD NOT be accessed directly.
+     *
+     * @internal
+     *
+     * @return string
+     */
+    public function serialize() : string
+    {
+        return $this->numerator . '/' . $this->denominator;
+    }
+
+    /**
+     * This method is only here to implement interface Serializable and cannot be accessed directly.
+     *
+     * @internal
+     * @psalm-suppress RedundantPropertyInitializationCheck
+     *
+     * @param string $value
+     *
+     * @return void
+     *
+     * @throws \LogicException
+     */
+    public function unserialize($value) : void
+    {
+        if (isset($this->numerator)) {
+            throw new \LogicException('unserialize() is an internal function, it must not be called directly.');
+        }
+
+        [$numerator, $denominator] = \explode('/', $value);
+
+        $this->numerator   = BigInteger::of($numerator);
+        $this->denominator = BigInteger::of($denominator);
+    }
+}

+ 41 - 0
vendor/brick/math/src/Exception/DivisionByZeroException.php

@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Exception;
+
+/**
+ * Exception thrown when a division by zero occurs.
+ */
+class DivisionByZeroException extends MathException
+{
+    /**
+     * @return DivisionByZeroException
+     *
+     * @psalm-pure
+     */
+    public static function divisionByZero() : DivisionByZeroException
+    {
+        return new self('Division by zero.');
+    }
+
+    /**
+     * @return DivisionByZeroException
+     *
+     * @psalm-pure
+     */
+    public static function modulusMustNotBeZero() : DivisionByZeroException
+    {
+        return new self('The modulus must not be zero.');
+    }
+
+    /**
+     * @return DivisionByZeroException
+     *
+     * @psalm-pure
+     */
+    public static function denominatorMustNotBeZero() : DivisionByZeroException
+    {
+        return new self('The denominator of a rational number cannot be zero.');
+    }
+}

+ 27 - 0
vendor/brick/math/src/Exception/IntegerOverflowException.php

@@ -0,0 +1,27 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Exception;
+
+use Brick\Math\BigInteger;
+
+/**
+ * Exception thrown when an integer overflow occurs.
+ */
+class IntegerOverflowException extends MathException
+{
+    /**
+     * @param BigInteger $value
+     *
+     * @return IntegerOverflowException
+     *
+     * @psalm-pure
+     */
+    public static function toIntOverflow(BigInteger $value) : IntegerOverflowException
+    {
+        $message = '%s is out of range %d to %d and cannot be represented as an integer.';
+
+        return new self(\sprintf($message, (string) $value, PHP_INT_MIN, PHP_INT_MAX));
+    }
+}

+ 14 - 0
vendor/brick/math/src/Exception/MathException.php

@@ -0,0 +1,14 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Exception;
+
+/**
+ * Base class for all math exceptions.
+ *
+ * This class is abstract to ensure that only fine-grained exceptions are thrown throughout the code.
+ */
+class MathException extends \RuntimeException
+{
+}

+ 12 - 0
vendor/brick/math/src/Exception/NegativeNumberException.php

@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Exception;
+
+/**
+ * Exception thrown when attempting to perform an unsupported operation, such as a square root, on a negative number.
+ */
+class NegativeNumberException extends MathException
+{
+}

+ 35 - 0
vendor/brick/math/src/Exception/NumberFormatException.php

@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Exception;
+
+/**
+ * Exception thrown when attempting to create a number from a string with an invalid format.
+ */
+class NumberFormatException extends MathException
+{
+    /**
+     * @param string $char The failing character.
+     *
+     * @return NumberFormatException
+     *
+     * @psalm-pure
+     */
+    public static function charNotInAlphabet(string $char) : self
+    {
+        $ord = \ord($char);
+
+        if ($ord < 32 || $ord > 126) {
+            $char = \strtoupper(\dechex($ord));
+
+            if ($ord < 10) {
+                $char = '0' . $char;
+            }
+        } else {
+            $char = '"' . $char . '"';
+        }
+
+        return new self(sprintf('Char %s is not a valid character in the given alphabet.', $char));
+    }
+}

+ 21 - 0
vendor/brick/math/src/Exception/RoundingNecessaryException.php

@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Exception;
+
+/**
+ * Exception thrown when a number cannot be represented at the requested scale without rounding.
+ */
+class RoundingNecessaryException extends MathException
+{
+    /**
+     * @return RoundingNecessaryException
+     *
+     * @psalm-pure
+     */
+    public static function roundingNecessary() : RoundingNecessaryException
+    {
+        return new self('Rounding is necessary to represent the result of the operation at this scale.');
+    }
+}

+ 759 - 0
vendor/brick/math/src/Internal/Calculator.php

@@ -0,0 +1,759 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Internal;
+
+use Brick\Math\Exception\RoundingNecessaryException;
+use Brick\Math\RoundingMode;
+
+/**
+ * Performs basic operations on arbitrary size integers.
+ *
+ * Unless otherwise specified, all parameters must be validated as non-empty strings of digits,
+ * without leading zero, and with an optional leading minus sign if the number is not zero.
+ *
+ * Any other parameter format will lead to undefined behaviour.
+ * All methods must return strings respecting this format, unless specified otherwise.
+ *
+ * @internal
+ *
+ * @psalm-immutable
+ */
+abstract class Calculator
+{
+    /**
+     * The maximum exponent value allowed for the pow() method.
+     */
+    public const MAX_POWER = 1000000;
+
+    /**
+     * The alphabet for converting from and to base 2 to 36, lowercase.
+     */
+    public const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';
+
+    /**
+     * The Calculator instance in use.
+     *
+     * @var Calculator|null
+     */
+    private static $instance;
+
+    /**
+     * Sets the Calculator instance to use.
+     *
+     * An instance is typically set only in unit tests: the autodetect is usually the best option.
+     *
+     * @param Calculator|null $calculator The calculator instance, or NULL to revert to autodetect.
+     *
+     * @return void
+     */
+    final public static function set(?Calculator $calculator) : void
+    {
+        self::$instance = $calculator;
+    }
+
+    /**
+     * Returns the Calculator instance to use.
+     *
+     * If none has been explicitly set, the fastest available implementation will be returned.
+     *
+     * @return Calculator
+     *
+     * @psalm-pure
+     * @psalm-suppress ImpureStaticProperty
+     */
+    final public static function get() : Calculator
+    {
+        if (self::$instance === null) {
+            /** @psalm-suppress ImpureMethodCall */
+            self::$instance = self::detect();
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * Returns the fastest available Calculator implementation.
+     *
+     * @codeCoverageIgnore
+     *
+     * @return Calculator
+     */
+    private static function detect() : Calculator
+    {
+        if (\extension_loaded('gmp')) {
+            return new Calculator\GmpCalculator();
+        }
+
+        if (\extension_loaded('bcmath')) {
+            return new Calculator\BcMathCalculator();
+        }
+
+        return new Calculator\NativeCalculator();
+    }
+
+    /**
+     * Extracts the sign & digits of the operands.
+     *
+     * @param string $a The first operand.
+     * @param string $b The second operand.
+     *
+     * @return array{0: bool, 1: bool, 2: string, 3: string} Whether $a and $b are negative, followed by their digits.
+     */
+    final protected function init(string $a, string $b) : array
+    {
+        return [
+            $aNeg = ($a[0] === '-'),
+            $bNeg = ($b[0] === '-'),
+
+            $aNeg ? \substr($a, 1) : $a,
+            $bNeg ? \substr($b, 1) : $b,
+        ];
+    }
+
+    /**
+     * Returns the absolute value of a number.
+     *
+     * @param string $n The number.
+     *
+     * @return string The absolute value.
+     */
+    final public function abs(string $n) : string
+    {
+        return ($n[0] === '-') ? \substr($n, 1) : $n;
+    }
+
+    /**
+     * Negates a number.
+     *
+     * @param string $n The number.
+     *
+     * @return string The negated value.
+     */
+    final public function neg(string $n) : string
+    {
+        if ($n === '0') {
+            return '0';
+        }
+
+        if ($n[0] === '-') {
+            return \substr($n, 1);
+        }
+
+        return '-' . $n;
+    }
+
+    /**
+     * Compares two numbers.
+     *
+     * @param string $a The first number.
+     * @param string $b The second number.
+     *
+     * @return int [-1, 0, 1] If the first number is less than, equal to, or greater than the second number.
+     */
+    final public function cmp(string $a, string $b) : int
+    {
+        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
+
+        if ($aNeg && ! $bNeg) {
+            return -1;
+        }
+
+        if ($bNeg && ! $aNeg) {
+            return 1;
+        }
+
+        $aLen = \strlen($aDig);
+        $bLen = \strlen($bDig);
+
+        if ($aLen < $bLen) {
+            $result = -1;
+        } elseif ($aLen > $bLen) {
+            $result = 1;
+        } else {
+            $result = $aDig <=> $bDig;
+        }
+
+        return $aNeg ? -$result : $result;
+    }
+
+    /**
+     * Adds two numbers.
+     *
+     * @param string $a The augend.
+     * @param string $b The addend.
+     *
+     * @return string The sum.
+     */
+    abstract public function add(string $a, string $b) : string;
+
+    /**
+     * Subtracts two numbers.
+     *
+     * @param string $a The minuend.
+     * @param string $b The subtrahend.
+     *
+     * @return string The difference.
+     */
+    abstract public function sub(string $a, string $b) : string;
+
+    /**
+     * Multiplies two numbers.
+     *
+     * @param string $a The multiplicand.
+     * @param string $b The multiplier.
+     *
+     * @return string The product.
+     */
+    abstract public function mul(string $a, string $b) : string;
+
+    /**
+     * Returns the quotient of the division of two numbers.
+     *
+     * @param string $a The dividend.
+     * @param string $b The divisor, must not be zero.
+     *
+     * @return string The quotient.
+     */
+    abstract public function divQ(string $a, string $b) : string;
+
+    /**
+     * Returns the remainder of the division of two numbers.
+     *
+     * @param string $a The dividend.
+     * @param string $b The divisor, must not be zero.
+     *
+     * @return string The remainder.
+     */
+    abstract public function divR(string $a, string $b) : string;
+
+    /**
+     * Returns the quotient and remainder of the division of two numbers.
+     *
+     * @param string $a The dividend.
+     * @param string $b The divisor, must not be zero.
+     *
+     * @return string[] An array containing the quotient and remainder.
+     */
+    abstract public function divQR(string $a, string $b) : array;
+
+    /**
+     * Exponentiates a number.
+     *
+     * @param string $a The base number.
+     * @param int    $e The exponent, validated as an integer between 0 and MAX_POWER.
+     *
+     * @return string The power.
+     */
+    abstract public function pow(string $a, int $e) : string;
+
+    /**
+     * @param string $a
+     * @param string $b The modulus; must not be zero.
+     *
+     * @return string
+     */
+    public function mod(string $a, string $b) : string
+    {
+        return $this->divR($this->add($this->divR($a, $b), $b), $b);
+    }
+
+    /**
+     * Returns the modular multiplicative inverse of $x modulo $m.
+     *
+     * If $x has no multiplicative inverse mod m, this method must return null.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library has built-in support.
+     *
+     * @param string $x
+     * @param string $m The modulus; must not be negative or zero.
+     *
+     * @return string|null
+     */
+    public function modInverse(string $x, string $m) : ?string
+    {
+        if ($m === '1') {
+            return '0';
+        }
+
+        $modVal = $x;
+
+        if ($x[0] === '-' || ($this->cmp($this->abs($x), $m) >= 0)) {
+            $modVal = $this->mod($x, $m);
+        }
+
+        $x = '0';
+        $y = '0';
+        $g = $this->gcdExtended($modVal, $m, $x, $y);
+
+        if ($g !== '1') {
+            return null;
+        }
+
+        return $this->mod($this->add($this->mod($x, $m), $m), $m);
+    }
+
+    /**
+     * Raises a number into power with modulo.
+     *
+     * @param string $base The base number; must be positive or zero.
+     * @param string $exp  The exponent; must be positive or zero.
+     * @param string $mod  The modulus; must be strictly positive.
+     *
+     * @return string The power.
+     */
+    abstract public function modPow(string $base, string $exp, string $mod) : string;
+
+    /**
+     * Returns the greatest common divisor of the two numbers.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library
+     * has built-in support for GCD calculations.
+     *
+     * @param string $a The first number.
+     * @param string $b The second number.
+     *
+     * @return string The GCD, always positive, or zero if both arguments are zero.
+     */
+    public function gcd(string $a, string $b) : string
+    {
+        if ($a === '0') {
+            return $this->abs($b);
+        }
+
+        if ($b === '0') {
+            return $this->abs($a);
+        }
+
+        return $this->gcd($b, $this->divR($a, $b));
+    }
+
+    private function gcdExtended(string $a, string $b, string &$x, string &$y) : string
+    {
+        if ($a === '0') {
+            $x = '0';
+            $y = '1';
+
+            return $b;
+        }
+
+        $x1 = '0';
+        $y1 = '0';
+
+        $gcd = $this->gcdExtended($this->mod($b, $a), $a, $x1, $y1);
+
+        $x = $this->sub($y1, $this->mul($this->divQ($b, $a), $x1));
+        $y = $x1;
+
+        return $gcd;
+    }
+
+    /**
+     * Returns the square root of the given number, rounded down.
+     *
+     * The result is the largest x such that x² ≤ n.
+     * The input MUST NOT be negative.
+     *
+     * @param string $n The number.
+     *
+     * @return string The square root.
+     */
+    abstract public function sqrt(string $n) : string;
+
+    /**
+     * Converts a number from an arbitrary base.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library
+     * has built-in support for base conversion.
+     *
+     * @param string $number The number, positive or zero, non-empty, case-insensitively validated for the given base.
+     * @param int    $base   The base of the number, validated from 2 to 36.
+     *
+     * @return string The converted number, following the Calculator conventions.
+     */
+    public function fromBase(string $number, int $base) : string
+    {
+        return $this->fromArbitraryBase(\strtolower($number), self::ALPHABET, $base);
+    }
+
+    /**
+     * Converts a number to an arbitrary base.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library
+     * has built-in support for base conversion.
+     *
+     * @param string $number The number to convert, following the Calculator conventions.
+     * @param int    $base   The base to convert to, validated from 2 to 36.
+     *
+     * @return string The converted number, lowercase.
+     */
+    public function toBase(string $number, int $base) : string
+    {
+        $negative = ($number[0] === '-');
+
+        if ($negative) {
+            $number = \substr($number, 1);
+        }
+
+        $number = $this->toArbitraryBase($number, self::ALPHABET, $base);
+
+        if ($negative) {
+            return '-' . $number;
+        }
+
+        return $number;
+    }
+
+    /**
+     * Converts a non-negative number in an arbitrary base using a custom alphabet, to base 10.
+     *
+     * @param string $number   The number to convert, validated as a non-empty string,
+     *                         containing only chars in the given alphabet/base.
+     * @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
+     * @param int    $base     The base of the number, validated from 2 to alphabet length.
+     *
+     * @return string The number in base 10, following the Calculator conventions.
+     */
+    final public function fromArbitraryBase(string $number, string $alphabet, int $base) : string
+    {
+        // remove leading "zeros"
+        $number = \ltrim($number, $alphabet[0]);
+
+        if ($number === '') {
+            return '0';
+        }
+
+        // optimize for "one"
+        if ($number === $alphabet[1]) {
+            return '1';
+        }
+
+        $result = '0';
+        $power = '1';
+
+        $base = (string) $base;
+
+        for ($i = \strlen($number) - 1; $i >= 0; $i--) {
+            $index = \strpos($alphabet, $number[$i]);
+
+            if ($index !== 0) {
+                $result = $this->add($result, ($index === 1)
+                    ? $power
+                    : $this->mul($power, (string) $index)
+                );
+            }
+
+            if ($i !== 0) {
+                $power = $this->mul($power, $base);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Converts a non-negative number to an arbitrary base using a custom alphabet.
+     *
+     * @param string $number   The number to convert, positive or zero, following the Calculator conventions.
+     * @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
+     * @param int    $base     The base to convert to, validated from 2 to alphabet length.
+     *
+     * @return string The converted number in the given alphabet.
+     */
+    final public function toArbitraryBase(string $number, string $alphabet, int $base) : string
+    {
+        if ($number === '0') {
+            return $alphabet[0];
+        }
+
+        $base = (string) $base;
+        $result = '';
+
+        while ($number !== '0') {
+            [$number, $remainder] = $this->divQR($number, $base);
+            $remainder = (int) $remainder;
+
+            $result .= $alphabet[$remainder];
+        }
+
+        return \strrev($result);
+    }
+
+    /**
+     * Performs a rounded division.
+     *
+     * Rounding is performed when the remainder of the division is not zero.
+     *
+     * @param string $a            The dividend.
+     * @param string $b            The divisor, must not be zero.
+     * @param int    $roundingMode The rounding mode.
+     *
+     * @return string
+     *
+     * @throws \InvalidArgumentException  If the rounding mode is invalid.
+     * @throws RoundingNecessaryException If RoundingMode::UNNECESSARY is provided but rounding is necessary.
+     */
+    final public function divRound(string $a, string $b, int $roundingMode) : string
+    {
+        [$quotient, $remainder] = $this->divQR($a, $b);
+
+        $hasDiscardedFraction = ($remainder !== '0');
+        $isPositiveOrZero = ($a[0] === '-') === ($b[0] === '-');
+
+        $discardedFractionSign = function() use ($remainder, $b) : int {
+            $r = $this->abs($this->mul($remainder, '2'));
+            $b = $this->abs($b);
+
+            return $this->cmp($r, $b);
+        };
+
+        $increment = false;
+
+        switch ($roundingMode) {
+            case RoundingMode::UNNECESSARY:
+                if ($hasDiscardedFraction) {
+                    throw RoundingNecessaryException::roundingNecessary();
+                }
+                break;
+
+            case RoundingMode::UP:
+                $increment = $hasDiscardedFraction;
+                break;
+
+            case RoundingMode::DOWN:
+                break;
+
+            case RoundingMode::CEILING:
+                $increment = $hasDiscardedFraction && $isPositiveOrZero;
+                break;
+
+            case RoundingMode::FLOOR:
+                $increment = $hasDiscardedFraction && ! $isPositiveOrZero;
+                break;
+
+            case RoundingMode::HALF_UP:
+                $increment = $discardedFractionSign() >= 0;
+                break;
+
+            case RoundingMode::HALF_DOWN:
+                $increment = $discardedFractionSign() > 0;
+                break;
+
+            case RoundingMode::HALF_CEILING:
+                $increment = $isPositiveOrZero ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0;
+                break;
+
+            case RoundingMode::HALF_FLOOR:
+                $increment = $isPositiveOrZero ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
+                break;
+
+            case RoundingMode::HALF_EVEN:
+                $lastDigit = (int) $quotient[-1];
+                $lastDigitIsEven = ($lastDigit % 2 === 0);
+                $increment = $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
+                break;
+
+            default:
+                throw new \InvalidArgumentException('Invalid rounding mode.');
+        }
+
+        if ($increment) {
+            return $this->add($quotient, $isPositiveOrZero ? '1' : '-1');
+        }
+
+        return $quotient;
+    }
+
+    /**
+     * Calculates bitwise AND of two numbers.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library
+     * has built-in support for bitwise operations.
+     *
+     * @param string $a
+     * @param string $b
+     *
+     * @return string
+     */
+    public function and(string $a, string $b) : string
+    {
+        return $this->bitwise('and', $a, $b);
+    }
+
+    /**
+     * Calculates bitwise OR of two numbers.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library
+     * has built-in support for bitwise operations.
+     *
+     * @param string $a
+     * @param string $b
+     *
+     * @return string
+     */
+    public function or(string $a, string $b) : string
+    {
+        return $this->bitwise('or', $a, $b);
+    }
+
+    /**
+     * Calculates bitwise XOR of two numbers.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library
+     * has built-in support for bitwise operations.
+     *
+     * @param string $a
+     * @param string $b
+     *
+     * @return string
+     */
+    public function xor(string $a, string $b) : string
+    {
+        return $this->bitwise('xor', $a, $b);
+    }
+
+    /**
+     * Performs a bitwise operation on a decimal number.
+     *
+     * @param string $operator The operator to use, must be "and", "or" or "xor".
+     * @param string $a        The left operand.
+     * @param string $b        The right operand.
+     *
+     * @return string
+     */
+    private function bitwise(string $operator, string $a, string $b) : string
+    {
+        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
+
+        $aBin = $this->toBinary($aDig);
+        $bBin = $this->toBinary($bDig);
+
+        $aLen = \strlen($aBin);
+        $bLen = \strlen($bBin);
+
+        if ($aLen > $bLen) {
+            $bBin = \str_repeat("\x00", $aLen - $bLen) . $bBin;
+        } elseif ($bLen > $aLen) {
+            $aBin = \str_repeat("\x00", $bLen - $aLen) . $aBin;
+        }
+
+        if ($aNeg) {
+            $aBin = $this->twosComplement($aBin);
+        }
+        if ($bNeg) {
+            $bBin = $this->twosComplement($bBin);
+        }
+
+        switch ($operator) {
+            case 'and':
+                $value = $aBin & $bBin;
+                $negative = ($aNeg and $bNeg);
+                break;
+
+            case 'or':
+                $value = $aBin | $bBin;
+                $negative = ($aNeg or $bNeg);
+                break;
+
+            case 'xor':
+                $value = $aBin ^ $bBin;
+                $negative = ($aNeg xor $bNeg);
+                break;
+
+            // @codeCoverageIgnoreStart
+            default:
+                throw new \InvalidArgumentException('Invalid bitwise operator.');
+            // @codeCoverageIgnoreEnd
+        }
+
+        if ($negative) {
+            $value = $this->twosComplement($value);
+        }
+
+        $result = $this->toDecimal($value);
+
+        return $negative ? $this->neg($result) : $result;
+    }
+
+    /**
+     * @psalm-suppress InvalidOperand
+     * @see https://github.com/vimeo/psalm/issues/4456
+     *
+     * @param string $number A positive, binary number.
+     *
+     * @return string
+     */
+    private function twosComplement(string $number) : string
+    {
+        $xor = \str_repeat("\xff", \strlen($number));
+
+        $number ^= $xor;
+
+        for ($i = \strlen($number) - 1; $i >= 0; $i--) {
+            $byte = \ord($number[$i]);
+
+            if (++$byte !== 256) {
+                $number[$i] = \chr($byte);
+                break;
+            }
+
+            $number[$i] = "\x00";
+
+            if ($i === 0) {
+                $number = "\x01" . $number;
+            }
+        }
+
+        return $number;
+    }
+
+    /**
+     * Converts a decimal number to a binary string.
+     *
+     * @param string $number The number to convert, positive or zero, only digits.
+     *
+     * @return string
+     */
+    private function toBinary(string $number) : string
+    {
+        $result = '';
+
+        while ($number !== '0') {
+            [$number, $remainder] = $this->divQR($number, '256');
+            $result .= \chr((int) $remainder);
+        }
+
+        return \strrev($result);
+    }
+
+    /**
+     * Returns the positive decimal representation of a binary number.
+     *
+     * @param string $bytes The bytes representing the number.
+     *
+     * @return string
+     */
+    private function toDecimal(string $bytes) : string
+    {
+        $result = '0';
+        $power = '1';
+
+        for ($i = \strlen($bytes) - 1; $i >= 0; $i--) {
+            $index = \ord($bytes[$i]);
+
+            if ($index !== 0) {
+                $result = $this->add($result, ($index === 1)
+                    ? $power
+                    : $this->mul($power, (string) $index)
+                );
+            }
+
+            if ($i !== 0) {
+                $power = $this->mul($power, '256');
+            }
+        }
+
+        return $result;
+    }
+}

+ 116 - 0
vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php

@@ -0,0 +1,116 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Internal\Calculator;
+
+use Brick\Math\Internal\Calculator;
+
+/**
+ * Calculator implementation built around the bcmath library.
+ *
+ * @internal
+ *
+ * @psalm-immutable
+ */
+class BcMathCalculator extends Calculator
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function add(string $a, string $b) : string
+    {
+        return \bcadd($a, $b, 0);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sub(string $a, string $b) : string
+    {
+        return \bcsub($a, $b, 0);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function mul(string $a, string $b) : string
+    {
+        return \bcmul($a, $b, 0);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @psalm-suppress InvalidNullableReturnType
+     * @psalm-suppress NullableReturnStatement
+     */
+    public function divQ(string $a, string $b) : string
+    {
+        return \bcdiv($a, $b, 0);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @psalm-suppress InvalidNullableReturnType
+     * @psalm-suppress NullableReturnStatement
+     */
+    public function divR(string $a, string $b) : string
+    {
+        if (version_compare(PHP_VERSION, '7.2') >= 0) {
+            return \bcmod($a, $b, 0);
+        }
+
+        return \bcmod($a, $b);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function divQR(string $a, string $b) : array
+    {
+        $q = \bcdiv($a, $b, 0);
+
+        if (version_compare(PHP_VERSION, '7.2') >= 0) {
+            $r = \bcmod($a, $b, 0);
+        } else {
+            $r = \bcmod($a, $b);
+        }
+
+        assert($q !== null);
+        assert($r !== null);
+
+        return [$q, $r];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function pow(string $a, int $e) : string
+    {
+        return \bcpow($a, (string) $e, 0);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @psalm-suppress InvalidNullableReturnType
+     * @psalm-suppress NullableReturnStatement
+     */
+    public function modPow(string $base, string $exp, string $mod) : string
+    {
+        return \bcpowmod($base, $exp, $mod, 0);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @psalm-suppress NullableReturnStatement
+     * @psalm-suppress InvalidNullableReturnType
+     */
+    public function sqrt(string $n) : string
+    {
+        return \bcsqrt($n, 0);
+    }
+}

+ 156 - 0
vendor/brick/math/src/Internal/Calculator/GmpCalculator.php

@@ -0,0 +1,156 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Internal\Calculator;
+
+use Brick\Math\Internal\Calculator;
+
+/**
+ * Calculator implementation built around the GMP library.
+ *
+ * @internal
+ *
+ * @psalm-immutable
+ */
+class GmpCalculator extends Calculator
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function add(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_add($a, $b));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sub(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_sub($a, $b));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function mul(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_mul($a, $b));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function divQ(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_div_q($a, $b));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function divR(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_div_r($a, $b));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function divQR(string $a, string $b) : array
+    {
+        [$q, $r] = \gmp_div_qr($a, $b);
+
+        return [
+            \gmp_strval($q),
+            \gmp_strval($r)
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function pow(string $a, int $e) : string
+    {
+        return \gmp_strval(\gmp_pow($a, $e));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function modInverse(string $x, string $m) : ?string
+    {
+        $result = \gmp_invert($x, $m);
+
+        if ($result === false) {
+            return null;
+        }
+
+        return \gmp_strval($result);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function modPow(string $base, string $exp, string $mod) : string
+    {
+        return \gmp_strval(\gmp_powm($base, $exp, $mod));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function gcd(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_gcd($a, $b));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function fromBase(string $number, int $base) : string
+    {
+        return \gmp_strval(\gmp_init($number, $base));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toBase(string $number, int $base) : string
+    {
+        return \gmp_strval($number, $base);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function and(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_and($a, $b));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function or(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_or($a, $b));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function xor(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_xor($a, $b));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function sqrt(string $n) : string
+    {
+        return \gmp_strval(\gmp_sqrt($n));
+    }
+}

+ 634 - 0
vendor/brick/math/src/Internal/Calculator/NativeCalculator.php

@@ -0,0 +1,634 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Internal\Calculator;
+
+use Brick\Math\Internal\Calculator;
+
+/**
+ * Calculator implementation using only native PHP code.
+ *
+ * @internal
+ *
+ * @psalm-immutable
+ */
+class NativeCalculator extends Calculator
+{
+    /**
+     * The max number of digits the platform can natively add, subtract, multiply or divide without overflow.
+     * For multiplication, this represents the max sum of the lengths of both operands.
+     *
+     * For addition, it is assumed that an extra digit can hold a carry (1) without overflowing.
+     * Example: 32-bit: max number 1,999,999,999 (9 digits + carry)
+     *          64-bit: max number 1,999,999,999,999,999,999 (18 digits + carry)
+     *
+     * @var int
+     */
+    private $maxDigits;
+
+    /**
+     * Class constructor.
+     *
+     * @codeCoverageIgnore
+     */
+    public function __construct()
+    {
+        switch (PHP_INT_SIZE) {
+            case 4:
+                $this->maxDigits = 9;
+                break;
+
+            case 8:
+                $this->maxDigits = 18;
+                break;
+
+            default:
+                throw new \RuntimeException('The platform is not 32-bit or 64-bit as expected.');
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function add(string $a, string $b) : string
+    {
+        /**
+         * @psalm-var numeric-string $a
+         * @psalm-var numeric-string $b
+         */
+        $result = $a + $b;
+
+        if (is_int($result)) {
+            return (string) $result;
+        }
+
+        if ($a === '0') {
+            return $b;
+        }
+
+        if ($b === '0') {
+            return $a;
+        }
+
+        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
+
+        $result = $aNeg === $bNeg ? $this->doAdd($aDig, $bDig) : $this->doSub($aDig, $bDig);
+
+        if ($aNeg) {
+            $result = $this->neg($result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sub(string $a, string $b) : string
+    {
+        return $this->add($a, $this->neg($b));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function mul(string $a, string $b) : string
+    {
+        /**
+         * @psalm-var numeric-string $a
+         * @psalm-var numeric-string $b
+         */
+        $result = $a * $b;
+
+        if (is_int($result)) {
+            return (string) $result;
+        }
+
+        if ($a === '0' || $b === '0') {
+            return '0';
+        }
+
+        if ($a === '1') {
+            return $b;
+        }
+
+        if ($b === '1') {
+            return $a;
+        }
+
+        if ($a === '-1') {
+            return $this->neg($b);
+        }
+
+        if ($b === '-1') {
+            return $this->neg($a);
+        }
+
+        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
+
+        $result = $this->doMul($aDig, $bDig);
+
+        if ($aNeg !== $bNeg) {
+            $result = $this->neg($result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function divQ(string $a, string $b) : string
+    {
+        return $this->divQR($a, $b)[0];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function divR(string $a, string $b): string
+    {
+        return $this->divQR($a, $b)[1];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function divQR(string $a, string $b) : array
+    {
+        if ($a === '0') {
+            return ['0', '0'];
+        }
+
+        if ($a === $b) {
+            return ['1', '0'];
+        }
+
+        if ($b === '1') {
+            return [$a, '0'];
+        }
+
+        if ($b === '-1') {
+            return [$this->neg($a), '0'];
+        }
+
+        /** @psalm-var numeric-string $a */
+        $na = $a * 1; // cast to number
+
+        if (is_int($na)) {
+            /** @psalm-var numeric-string $b */
+            $nb = $b * 1;
+
+            if (is_int($nb)) {
+                // the only division that may overflow is PHP_INT_MIN / -1,
+                // which cannot happen here as we've already handled a divisor of -1 above.
+                $r = $na % $nb;
+                $q = ($na - $r) / $nb;
+
+                assert(is_int($q));
+
+                return [
+                    (string) $q,
+                    (string) $r
+                ];
+            }
+        }
+
+        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
+
+        [$q, $r] = $this->doDiv($aDig, $bDig);
+
+        if ($aNeg !== $bNeg) {
+            $q = $this->neg($q);
+        }
+
+        if ($aNeg) {
+            $r = $this->neg($r);
+        }
+
+        return [$q, $r];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function pow(string $a, int $e) : string
+    {
+        if ($e === 0) {
+            return '1';
+        }
+
+        if ($e === 1) {
+            return $a;
+        }
+
+        $odd = $e % 2;
+        $e -= $odd;
+
+        $aa = $this->mul($a, $a);
+
+        /** @psalm-suppress PossiblyInvalidArgument We're sure that $e / 2 is an int now */
+        $result = $this->pow($aa, $e / 2);
+
+        if ($odd === 1) {
+            $result = $this->mul($result, $a);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Algorithm from: https://www.geeksforgeeks.org/modular-exponentiation-power-in-modular-arithmetic/
+     *
+     * {@inheritdoc}
+     */
+    public function modPow(string $base, string $exp, string $mod) : string
+    {
+        // special case: the algorithm below fails with 0 power 0 mod 1 (returns 1 instead of 0)
+        if ($base === '0' && $exp === '0' && $mod === '1') {
+            return '0';
+        }
+
+        // special case: the algorithm below fails with power 0 mod 1 (returns 1 instead of 0)
+        if ($exp === '0' && $mod === '1') {
+            return '0';
+        }
+
+        $x = $base;
+
+        $res = '1';
+
+        // numbers are positive, so we can use remainder instead of modulo
+        $x = $this->divR($x, $mod);
+
+        while ($exp !== '0') {
+            if (in_array($exp[-1], ['1', '3', '5', '7', '9'])) { // odd
+                $res = $this->divR($this->mul($res, $x), $mod);
+            }
+
+            $exp = $this->divQ($exp, '2');
+            $x = $this->divR($this->mul($x, $x), $mod);
+        }
+
+        return $res;
+    }
+
+    /**
+     * Adapted from https://cp-algorithms.com/num_methods/roots_newton.html
+     *
+     * {@inheritDoc}
+     */
+    public function sqrt(string $n) : string
+    {
+        if ($n === '0') {
+            return '0';
+        }
+
+        // initial approximation
+        $x = \str_repeat('9', \intdiv(\strlen($n), 2) ?: 1);
+
+        $decreased = false;
+
+        for (;;) {
+            $nx = $this->divQ($this->add($x, $this->divQ($n, $x)), '2');
+
+            if ($x === $nx || $this->cmp($nx, $x) > 0 && $decreased) {
+                break;
+            }
+
+            $decreased = $this->cmp($nx, $x) < 0;
+            $x = $nx;
+        }
+
+        return $x;
+    }
+
+    /**
+     * Performs the addition of two non-signed large integers.
+     *
+     * @param string $a The first operand.
+     * @param string $b The second operand.
+     *
+     * @return string
+     */
+    private function doAdd(string $a, string $b) : string
+    {
+        [$a, $b, $length] = $this->pad($a, $b);
+
+        $carry = 0;
+        $result = '';
+
+        for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
+            $blockLength = $this->maxDigits;
+
+            if ($i < 0) {
+                $blockLength += $i;
+                /** @psalm-suppress LoopInvalidation */
+                $i = 0;
+            }
+
+            /** @psalm-var numeric-string $blockA */
+            $blockA = \substr($a, $i, $blockLength);
+
+            /** @psalm-var numeric-string $blockB */
+            $blockB = \substr($b, $i, $blockLength);
+
+            $sum = (string) ($blockA + $blockB + $carry);
+            $sumLength = \strlen($sum);
+
+            if ($sumLength > $blockLength) {
+                $sum = \substr($sum, 1);
+                $carry = 1;
+            } else {
+                if ($sumLength < $blockLength) {
+                    $sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
+                }
+                $carry = 0;
+            }
+
+            $result = $sum . $result;
+
+            if ($i === 0) {
+                break;
+            }
+        }
+
+        if ($carry === 1) {
+            $result = '1' . $result;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Performs the subtraction of two non-signed large integers.
+     *
+     * @param string $a The first operand.
+     * @param string $b The second operand.
+     *
+     * @return string
+     */
+    private function doSub(string $a, string $b) : string
+    {
+        if ($a === $b) {
+            return '0';
+        }
+
+        // Ensure that we always subtract to a positive result: biggest minus smallest.
+        $cmp = $this->doCmp($a, $b);
+
+        $invert = ($cmp === -1);
+
+        if ($invert) {
+            $c = $a;
+            $a = $b;
+            $b = $c;
+        }
+
+        [$a, $b, $length] = $this->pad($a, $b);
+
+        $carry = 0;
+        $result = '';
+
+        $complement = 10 ** $this->maxDigits;
+
+        for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
+            $blockLength = $this->maxDigits;
+
+            if ($i < 0) {
+                $blockLength += $i;
+                /** @psalm-suppress LoopInvalidation */
+                $i = 0;
+            }
+
+            /** @psalm-var numeric-string $blockA */
+            $blockA = \substr($a, $i, $blockLength);
+
+            /** @psalm-var numeric-string $blockB */
+            $blockB = \substr($b, $i, $blockLength);
+
+            $sum = $blockA - $blockB - $carry;
+
+            if ($sum < 0) {
+                $sum += $complement;
+                $carry = 1;
+            } else {
+                $carry = 0;
+            }
+
+            $sum = (string) $sum;
+            $sumLength = \strlen($sum);
+
+            if ($sumLength < $blockLength) {
+                $sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
+            }
+
+            $result = $sum . $result;
+
+            if ($i === 0) {
+                break;
+            }
+        }
+
+        // Carry cannot be 1 when the loop ends, as a > b
+        assert($carry === 0);
+
+        $result = \ltrim($result, '0');
+
+        if ($invert) {
+            $result = $this->neg($result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Performs the multiplication of two non-signed large integers.
+     *
+     * @param string $a The first operand.
+     * @param string $b The second operand.
+     *
+     * @return string
+     */
+    private function doMul(string $a, string $b) : string
+    {
+        $x = \strlen($a);
+        $y = \strlen($b);
+
+        $maxDigits = \intdiv($this->maxDigits, 2);
+        $complement = 10 ** $maxDigits;
+
+        $result = '0';
+
+        for ($i = $x - $maxDigits;; $i -= $maxDigits) {
+            $blockALength = $maxDigits;
+
+            if ($i < 0) {
+                $blockALength += $i;
+                /** @psalm-suppress LoopInvalidation */
+                $i = 0;
+            }
+
+            $blockA = (int) \substr($a, $i, $blockALength);
+
+            $line = '';
+            $carry = 0;
+
+            for ($j = $y - $maxDigits;; $j -= $maxDigits) {
+                $blockBLength = $maxDigits;
+
+                if ($j < 0) {
+                    $blockBLength += $j;
+                    /** @psalm-suppress LoopInvalidation */
+                    $j = 0;
+                }
+
+                $blockB = (int) \substr($b, $j, $blockBLength);
+
+                $mul = $blockA * $blockB + $carry;
+                $value = $mul % $complement;
+                $carry = ($mul - $value) / $complement;
+
+                $value = (string) $value;
+                $value = \str_pad($value, $maxDigits, '0', STR_PAD_LEFT);
+
+                $line = $value . $line;
+
+                if ($j === 0) {
+                    break;
+                }
+            }
+
+            if ($carry !== 0) {
+                $line = $carry . $line;
+            }
+
+            $line = \ltrim($line, '0');
+
+            if ($line !== '') {
+                $line .= \str_repeat('0', $x - $blockALength - $i);
+                $result = $this->add($result, $line);
+            }
+
+            if ($i === 0) {
+                break;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Performs the division of two non-signed large integers.
+     *
+     * @param string $a The first operand.
+     * @param string $b The second operand.
+     *
+     * @return string[] The quotient and remainder.
+     */
+    private function doDiv(string $a, string $b) : array
+    {
+        $cmp = $this->doCmp($a, $b);
+
+        if ($cmp === -1) {
+            return ['0', $a];
+        }
+
+        $x = \strlen($a);
+        $y = \strlen($b);
+
+        // we now know that a >= b && x >= y
+
+        $q = '0'; // quotient
+        $r = $a; // remainder
+        $z = $y; // focus length, always $y or $y+1
+
+        for (;;) {
+            $focus = \substr($a, 0, $z);
+
+            $cmp = $this->doCmp($focus, $b);
+
+            if ($cmp === -1) {
+                if ($z === $x) { // remainder < dividend
+                    break;
+                }
+
+                $z++;
+            }
+
+            $zeros = \str_repeat('0', $x - $z);
+
+            $q = $this->add($q, '1' . $zeros);
+            $a = $this->sub($a, $b . $zeros);
+
+            $r = $a;
+
+            if ($r === '0') { // remainder == 0
+                break;
+            }
+
+            $x = \strlen($a);
+
+            if ($x < $y) { // remainder < dividend
+                break;
+            }
+
+            $z = $y;
+        }
+
+        return [$q, $r];
+    }
+
+    /**
+     * Compares two non-signed large numbers.
+     *
+     * @param string $a The first operand.
+     * @param string $b The second operand.
+     *
+     * @return int [-1, 0, 1]
+     */
+    private function doCmp(string $a, string $b) : int
+    {
+        $x = \strlen($a);
+        $y = \strlen($b);
+
+        $cmp = $x <=> $y;
+
+        if ($cmp !== 0) {
+            return $cmp;
+        }
+
+        return \strcmp($a, $b) <=> 0; // enforce [-1, 0, 1]
+    }
+
+    /**
+     * Pads the left of one of the given numbers with zeros if necessary to make both numbers the same length.
+     *
+     * The numbers must only consist of digits, without leading minus sign.
+     *
+     * @param string $a The first operand.
+     * @param string $b The second operand.
+     *
+     * @return array{0: string, 1: string, 2: int}
+     */
+    private function pad(string $a, string $b) : array
+    {
+        $x = \strlen($a);
+        $y = \strlen($b);
+
+        if ($x > $y) {
+            $b = \str_repeat('0', $x - $y) . $b;
+
+            return [$a, $b, $x];
+        }
+
+        if ($x < $y) {
+            $a = \str_repeat('0', $y - $x) . $a;
+
+            return [$a, $b, $y];
+        }
+
+        return [$a, $b, $x];
+    }
+}

+ 107 - 0
vendor/brick/math/src/RoundingMode.php

@@ -0,0 +1,107 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math;
+
+/**
+ * Specifies a rounding behavior for numerical operations capable of discarding precision.
+ *
+ * Each rounding mode indicates how the least significant returned digit of a rounded result
+ * is to be calculated. If fewer digits are returned than the digits needed to represent the
+ * exact numerical result, the discarded digits will be referred to as the discarded fraction
+ * regardless the digits' contribution to the value of the number. In other words, considered
+ * as a numerical value, the discarded fraction could have an absolute value greater than one.
+ */
+final class RoundingMode
+{
+    /**
+     * Private constructor. This class is not instantiable.
+     *
+     * @codeCoverageIgnore
+     */
+    private function __construct()
+    {
+    }
+
+    /**
+     * Asserts that the requested operation has an exact result, hence no rounding is necessary.
+     *
+     * If this rounding mode is specified on an operation that yields a result that
+     * cannot be represented at the requested scale, a RoundingNecessaryException is thrown.
+     */
+    public const UNNECESSARY = 0;
+
+    /**
+     * Rounds away from zero.
+     *
+     * Always increments the digit prior to a nonzero discarded fraction.
+     * Note that this rounding mode never decreases the magnitude of the calculated value.
+     */
+    public const UP = 1;
+
+    /**
+     * Rounds towards zero.
+     *
+     * Never increments the digit prior to a discarded fraction (i.e., truncates).
+     * Note that this rounding mode never increases the magnitude of the calculated value.
+     */
+    public const DOWN = 2;
+
+    /**
+     * Rounds towards positive infinity.
+     *
+     * If the result is positive, behaves as for UP; if negative, behaves as for DOWN.
+     * Note that this rounding mode never decreases the calculated value.
+     */
+    public const CEILING = 3;
+
+    /**
+     * Rounds towards negative infinity.
+     *
+     * If the result is positive, behave as for DOWN; if negative, behave as for UP.
+     * Note that this rounding mode never increases the calculated value.
+     */
+    public const FLOOR = 4;
+
+    /**
+     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
+     *
+     * Behaves as for UP if the discarded fraction is >= 0.5; otherwise, behaves as for DOWN.
+     * Note that this is the rounding mode commonly taught at school.
+     */
+    public const HALF_UP = 5;
+
+    /**
+     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.
+     *
+     * Behaves as for UP if the discarded fraction is > 0.5; otherwise, behaves as for DOWN.
+     */
+    public const HALF_DOWN = 6;
+
+    /**
+     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards positive infinity.
+     *
+     * If the result is positive, behaves as for HALF_UP; if negative, behaves as for HALF_DOWN.
+     */
+    public const HALF_CEILING = 7;
+
+    /**
+     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards negative infinity.
+     *
+     * If the result is positive, behaves as for HALF_DOWN; if negative, behaves as for HALF_UP.
+     */
+    public const HALF_FLOOR = 8;
+
+    /**
+     * Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the even neighbor.
+     *
+     * Behaves as for HALF_UP if the digit to the left of the discarded fraction is odd;
+     * behaves as for HALF_DOWN if it's even.
+     *
+     * Note that this is the rounding mode that statistically minimizes
+     * cumulative error when applied repeatedly over a sequence of calculations.
+     * It is sometimes known as "Banker's rounding", and is chiefly used in the USA.
+     */
+    public const HALF_EVEN = 9;
+}

+ 572 - 0
vendor/composer/ClassLoader.php

@@ -0,0 +1,572 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ *     $loader = new \Composer\Autoload\ClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->add('Symfony\Component', __DIR__.'/component');
+ *     $loader->add('Symfony',           __DIR__.'/framework');
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ *     // to enable searching the include path (eg. for PEAR packages)
+ *     $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @see    https://www.php-fig.org/psr/psr-0/
+ * @see    https://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+    /** @var ?string */
+    private $vendorDir;
+
+    // PSR-4
+    /**
+     * @var array[]
+     * @psalm-var array<string, array<string, int>>
+     */
+    private $prefixLengthsPsr4 = array();
+    /**
+     * @var array[]
+     * @psalm-var array<string, array<int, string>>
+     */
+    private $prefixDirsPsr4 = array();
+    /**
+     * @var array[]
+     * @psalm-var array<string, string>
+     */
+    private $fallbackDirsPsr4 = array();
+
+    // PSR-0
+    /**
+     * @var array[]
+     * @psalm-var array<string, array<string, string[]>>
+     */
+    private $prefixesPsr0 = array();
+    /**
+     * @var array[]
+     * @psalm-var array<string, string>
+     */
+    private $fallbackDirsPsr0 = array();
+
+    /** @var bool */
+    private $useIncludePath = false;
+
+    /**
+     * @var string[]
+     * @psalm-var array<string, string>
+     */
+    private $classMap = array();
+
+    /** @var bool */
+    private $classMapAuthoritative = false;
+
+    /**
+     * @var bool[]
+     * @psalm-var array<string, bool>
+     */
+    private $missingClasses = array();
+
+    /** @var ?string */
+    private $apcuPrefix;
+
+    /**
+     * @var self[]
+     */
+    private static $registeredLoaders = array();
+
+    /**
+     * @param ?string $vendorDir
+     */
+    public function __construct($vendorDir = null)
+    {
+        $this->vendorDir = $vendorDir;
+    }
+
+    /**
+     * @return string[]
+     */
+    public function getPrefixes()
+    {
+        if (!empty($this->prefixesPsr0)) {
+            return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+        }
+
+        return array();
+    }
+
+    /**
+     * @return array[]
+     * @psalm-return array<string, array<int, string>>
+     */
+    public function getPrefixesPsr4()
+    {
+        return $this->prefixDirsPsr4;
+    }
+
+    /**
+     * @return array[]
+     * @psalm-return array<string, string>
+     */
+    public function getFallbackDirs()
+    {
+        return $this->fallbackDirsPsr0;
+    }
+
+    /**
+     * @return array[]
+     * @psalm-return array<string, string>
+     */
+    public function getFallbackDirsPsr4()
+    {
+        return $this->fallbackDirsPsr4;
+    }
+
+    /**
+     * @return string[] Array of classname => path
+     * @psalm-return array<string, string>
+     */
+    public function getClassMap()
+    {
+        return $this->classMap;
+    }
+
+    /**
+     * @param string[] $classMap Class to filename map
+     * @psalm-param array<string, string> $classMap
+     *
+     * @return void
+     */
+    public function addClassMap(array $classMap)
+    {
+        if ($this->classMap) {
+            $this->classMap = array_merge($this->classMap, $classMap);
+        } else {
+            $this->classMap = $classMap;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix, either
+     * appending or prepending to the ones previously set for this prefix.
+     *
+     * @param string          $prefix  The prefix
+     * @param string[]|string $paths   The PSR-0 root directories
+     * @param bool            $prepend Whether to prepend the directories
+     *
+     * @return void
+     */
+    public function add($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            if ($prepend) {
+                $this->fallbackDirsPsr0 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr0
+                );
+            } else {
+                $this->fallbackDirsPsr0 = array_merge(
+                    $this->fallbackDirsPsr0,
+                    (array) $paths
+                );
+            }
+
+            return;
+        }
+
+        $first = $prefix[0];
+        if (!isset($this->prefixesPsr0[$first][$prefix])) {
+            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+            return;
+        }
+        if ($prepend) {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixesPsr0[$first][$prefix]
+            );
+        } else {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                $this->prefixesPsr0[$first][$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace, either
+     * appending or prepending to the ones previously set for this namespace.
+     *
+     * @param string          $prefix  The prefix/namespace, with trailing '\\'
+     * @param string[]|string $paths   The PSR-4 base directories
+     * @param bool            $prepend Whether to prepend the directories
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @return void
+     */
+    public function addPsr4($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            // Register directories for the root namespace.
+            if ($prepend) {
+                $this->fallbackDirsPsr4 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr4
+                );
+            } else {
+                $this->fallbackDirsPsr4 = array_merge(
+                    $this->fallbackDirsPsr4,
+                    (array) $paths
+                );
+            }
+        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+            // Register directories for a new namespace.
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        } elseif ($prepend) {
+            // Prepend directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixDirsPsr4[$prefix]
+            );
+        } else {
+            // Append directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                $this->prefixDirsPsr4[$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix,
+     * replacing any others previously set for this prefix.
+     *
+     * @param string          $prefix The prefix
+     * @param string[]|string $paths  The PSR-0 base directories
+     *
+     * @return void
+     */
+    public function set($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr0 = (array) $paths;
+        } else {
+            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace,
+     * replacing any others previously set for this namespace.
+     *
+     * @param string          $prefix The prefix/namespace, with trailing '\\'
+     * @param string[]|string $paths  The PSR-4 base directories
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @return void
+     */
+    public function setPsr4($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr4 = (array) $paths;
+        } else {
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Turns on searching the include path for class files.
+     *
+     * @param bool $useIncludePath
+     *
+     * @return void
+     */
+    public function setUseIncludePath($useIncludePath)
+    {
+        $this->useIncludePath = $useIncludePath;
+    }
+
+    /**
+     * Can be used to check if the autoloader uses the include path to check
+     * for classes.
+     *
+     * @return bool
+     */
+    public function getUseIncludePath()
+    {
+        return $this->useIncludePath;
+    }
+
+    /**
+     * Turns off searching the prefix and fallback directories for classes
+     * that have not been registered with the class map.
+     *
+     * @param bool $classMapAuthoritative
+     *
+     * @return void
+     */
+    public function setClassMapAuthoritative($classMapAuthoritative)
+    {
+        $this->classMapAuthoritative = $classMapAuthoritative;
+    }
+
+    /**
+     * Should class lookup fail if not found in the current class map?
+     *
+     * @return bool
+     */
+    public function isClassMapAuthoritative()
+    {
+        return $this->classMapAuthoritative;
+    }
+
+    /**
+     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+     *
+     * @param string|null $apcuPrefix
+     *
+     * @return void
+     */
+    public function setApcuPrefix($apcuPrefix)
+    {
+        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+    }
+
+    /**
+     * The APCu prefix in use, or null if APCu caching is not enabled.
+     *
+     * @return string|null
+     */
+    public function getApcuPrefix()
+    {
+        return $this->apcuPrefix;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     *
+     * @return void
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+        if (null === $this->vendorDir) {
+            return;
+        }
+
+        if ($prepend) {
+            self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+        } else {
+            unset(self::$registeredLoaders[$this->vendorDir]);
+            self::$registeredLoaders[$this->vendorDir] = $this;
+        }
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     *
+     * @return void
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+
+        if (null !== $this->vendorDir) {
+            unset(self::$registeredLoaders[$this->vendorDir]);
+        }
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param  string    $class The name of the class
+     * @return true|null True if loaded, null otherwise
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            includeFile($file);
+
+            return true;
+        }
+
+        return null;
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|false The path if found, false otherwise
+     */
+    public function findFile($class)
+    {
+        // class map lookup
+        if (isset($this->classMap[$class])) {
+            return $this->classMap[$class];
+        }
+        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+            return false;
+        }
+        if (null !== $this->apcuPrefix) {
+            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+            if ($hit) {
+                return $file;
+            }
+        }
+
+        $file = $this->findFileWithExtension($class, '.php');
+
+        // Search for Hack files if we are running on HHVM
+        if (false === $file && defined('HHVM_VERSION')) {
+            $file = $this->findFileWithExtension($class, '.hh');
+        }
+
+        if (null !== $this->apcuPrefix) {
+            apcu_add($this->apcuPrefix.$class, $file);
+        }
+
+        if (false === $file) {
+            // Remember that this class does not exist.
+            $this->missingClasses[$class] = true;
+        }
+
+        return $file;
+    }
+
+    /**
+     * Returns the currently registered loaders indexed by their corresponding vendor directories.
+     *
+     * @return self[]
+     */
+    public static function getRegisteredLoaders()
+    {
+        return self::$registeredLoaders;
+    }
+
+    /**
+     * @param  string       $class
+     * @param  string       $ext
+     * @return string|false
+     */
+    private function findFileWithExtension($class, $ext)
+    {
+        // PSR-4 lookup
+        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+        $first = $class[0];
+        if (isset($this->prefixLengthsPsr4[$first])) {
+            $subPath = $class;
+            while (false !== $lastPos = strrpos($subPath, '\\')) {
+                $subPath = substr($subPath, 0, $lastPos);
+                $search = $subPath . '\\';
+                if (isset($this->prefixDirsPsr4[$search])) {
+                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
+                        if (file_exists($file = $dir . $pathEnd)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-4 fallback dirs
+        foreach ($this->fallbackDirsPsr4 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 lookup
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+        } else {
+            // PEAR-like class name
+            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+        }
+
+        if (isset($this->prefixesPsr0[$first])) {
+            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($dirs as $dir) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-0 fallback dirs
+        foreach ($this->fallbackDirsPsr0 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 include paths.
+        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+            return $file;
+        }
+
+        return false;
+    }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ *
+ * @param  string $file
+ * @return void
+ * @private
+ */
+function includeFile($file)
+{
+    include $file;
+}

+ 350 - 0
vendor/composer/InstalledVersions.php

@@ -0,0 +1,350 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer;
+
+use Composer\Autoload\ClassLoader;
+use Composer\Semver\VersionParser;
+
+/**
+ * This class is copied in every Composer installed project and available to all
+ *
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
+ *
+ * To require its presence, you can require `composer-runtime-api ^2.0`
+ */
+class InstalledVersions
+{
+    /**
+     * @var mixed[]|null
+     * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
+     */
+    private static $installed;
+
+    /**
+     * @var bool|null
+     */
+    private static $canGetVendors;
+
+    /**
+     * @var array[]
+     * @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
+     */
+    private static $installedByVendor = array();
+
+    /**
+     * Returns a list of all package names which are present, either by being installed, replaced or provided
+     *
+     * @return string[]
+     * @psalm-return list<string>
+     */
+    public static function getInstalledPackages()
+    {
+        $packages = array();
+        foreach (self::getInstalled() as $installed) {
+            $packages[] = array_keys($installed['versions']);
+        }
+
+        if (1 === \count($packages)) {
+            return $packages[0];
+        }
+
+        return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+    }
+
+    /**
+     * Returns a list of all package names with a specific type e.g. 'library'
+     *
+     * @param  string   $type
+     * @return string[]
+     * @psalm-return list<string>
+     */
+    public static function getInstalledPackagesByType($type)
+    {
+        $packagesByType = array();
+
+        foreach (self::getInstalled() as $installed) {
+            foreach ($installed['versions'] as $name => $package) {
+                if (isset($package['type']) && $package['type'] === $type) {
+                    $packagesByType[] = $name;
+                }
+            }
+        }
+
+        return $packagesByType;
+    }
+
+    /**
+     * Checks whether the given package is installed
+     *
+     * This also returns true if the package name is provided or replaced by another package
+     *
+     * @param  string $packageName
+     * @param  bool   $includeDevRequirements
+     * @return bool
+     */
+    public static function isInstalled($packageName, $includeDevRequirements = true)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (isset($installed['versions'][$packageName])) {
+                return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks whether the given package satisfies a version constraint
+     *
+     * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
+     *
+     *   Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
+     *
+     * @param  VersionParser $parser      Install composer/semver to have access to this class and functionality
+     * @param  string        $packageName
+     * @param  string|null   $constraint  A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
+     * @return bool
+     */
+    public static function satisfies(VersionParser $parser, $packageName, $constraint)
+    {
+        $constraint = $parser->parseConstraints($constraint);
+        $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+        return $provided->matches($constraint);
+    }
+
+    /**
+     * Returns a version constraint representing all the range(s) which are installed for a given package
+     *
+     * It is easier to use this via isInstalled() with the $constraint argument if you need to check
+     * whether a given version of a package is installed, and not just whether it exists
+     *
+     * @param  string $packageName
+     * @return string Version constraint usable with composer/semver
+     */
+    public static function getVersionRanges($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            $ranges = array();
+            if (isset($installed['versions'][$packageName]['pretty_version'])) {
+                $ranges[] = $installed['versions'][$packageName]['pretty_version'];
+            }
+            if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+                $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+            }
+            if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+                $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+            }
+            if (array_key_exists('provided', $installed['versions'][$packageName])) {
+                $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+            }
+
+            return implode(' || ', $ranges);
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+     */
+    public static function getVersion($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            if (!isset($installed['versions'][$packageName]['version'])) {
+                return null;
+            }
+
+            return $installed['versions'][$packageName]['version'];
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+     */
+    public static function getPrettyVersion($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+                return null;
+            }
+
+            return $installed['versions'][$packageName]['pretty_version'];
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
+     */
+    public static function getReference($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            if (!isset($installed['versions'][$packageName]['reference'])) {
+                return null;
+            }
+
+            return $installed['versions'][$packageName]['reference'];
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @param  string      $packageName
+     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
+     */
+    public static function getInstallPath($packageName)
+    {
+        foreach (self::getInstalled() as $installed) {
+            if (!isset($installed['versions'][$packageName])) {
+                continue;
+            }
+
+            return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
+        }
+
+        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+    }
+
+    /**
+     * @return array
+     * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
+     */
+    public static function getRootPackage()
+    {
+        $installed = self::getInstalled();
+
+        return $installed[0]['root'];
+    }
+
+    /**
+     * Returns the raw installed.php data for custom implementations
+     *
+     * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
+     * @return array[]
+     * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
+     */
+    public static function getRawData()
+    {
+        @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
+
+        if (null === self::$installed) {
+            // only require the installed.php file if this file is loaded from its dumped location,
+            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+            if (substr(__DIR__, -8, 1) !== 'C') {
+                self::$installed = include __DIR__ . '/installed.php';
+            } else {
+                self::$installed = array();
+            }
+        }
+
+        return self::$installed;
+    }
+
+    /**
+     * Returns the raw data of all installed.php which are currently loaded for custom implementations
+     *
+     * @return array[]
+     * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
+     */
+    public static function getAllRawData()
+    {
+        return self::getInstalled();
+    }
+
+    /**
+     * Lets you reload the static array from another file
+     *
+     * This is only useful for complex integrations in which a project needs to use
+     * this class but then also needs to execute another project's autoloader in process,
+     * and wants to ensure both projects have access to their version of installed.php.
+     *
+     * A typical case would be PHPUnit, where it would need to make sure it reads all
+     * the data it needs from this class, then call reload() with
+     * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
+     * the project in which it runs can then also use this class safely, without
+     * interference between PHPUnit's dependencies and the project's dependencies.
+     *
+     * @param  array[] $data A vendor/composer/installed.php data set
+     * @return void
+     *
+     * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
+     */
+    public static function reload($data)
+    {
+        self::$installed = $data;
+        self::$installedByVendor = array();
+    }
+
+    /**
+     * @return array[]
+     * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
+     */
+    private static function getInstalled()
+    {
+        if (null === self::$canGetVendors) {
+            self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+        }
+
+        $installed = array();
+
+        if (self::$canGetVendors) {
+            foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+                if (isset(self::$installedByVendor[$vendorDir])) {
+                    $installed[] = self::$installedByVendor[$vendorDir];
+                } elseif (is_file($vendorDir.'/composer/installed.php')) {
+                    $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
+                    if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
+                        self::$installed = $installed[count($installed) - 1];
+                    }
+                }
+            }
+        }
+
+        if (null === self::$installed) {
+            // only require the installed.php file if this file is loaded from its dumped location,
+            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+            if (substr(__DIR__, -8, 1) !== 'C') {
+                self::$installed = require __DIR__ . '/installed.php';
+            } else {
+                self::$installed = array();
+            }
+        }
+        $installed[] = self::$installed;
+
+        return $installed;
+    }
+}

+ 21 - 0
vendor/composer/LICENSE

@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+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.
+

File diff suppressed because it is too large
+ 4709 - 0
vendor/composer/autoload_classmap.php


+ 38 - 0
vendor/composer/autoload_files.php

@@ -0,0 +1,38 @@
+<?php
+
+// autoload_files.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
+    'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
+    '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
+    'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
+    '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
+    'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
+    '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
+    'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
+    '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
+    '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
+    'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
+    '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
+    '801c31d8ed748cfa537fa45402288c95' => $vendorDir . '/psy/psysh/src/functions.php',
+    'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
+    '2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
+    'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
+    '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
+    'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
+    'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
+    'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',
+    '538ca81a9a966a6716601ecf48f4eaef' => $vendorDir . '/opis/closure/functions.php',
+    '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
+    '265b4faa2b3a9766332744949e83bf97' => $vendorDir . '/laravel/framework/src/Illuminate/Collections/helpers.php',
+    'c7a3c339e7e14b60e06a2d7fcce9476b' => $vendorDir . '/laravel/framework/src/Illuminate/Events/functions.php',
+    'f0906e6318348a765ffb6eb24e0d0938' => $vendorDir . '/laravel/framework/src/Illuminate/Foundation/helpers.php',
+    '58571171fd5812e6e447dce228f52f4d' => $vendorDir . '/laravel/framework/src/Illuminate/Support/helpers.php',
+    '9cdd7b9056abc3081735233ba9dd9c7f' => $vendorDir . '/facade/flare-client-php/src/helpers.php',
+    '6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
+    'ed962a97bd972bc82007176b647d4e36' => $vendorDir . '/facade/ignition/src/helpers.php',
+);

+ 10 - 0
vendor/composer/autoload_namespaces.php

@@ -0,0 +1,10 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'Mockery' => array($vendorDir . '/mockery/mockery/library'),
+);

+ 86 - 0
vendor/composer/autoload_psr4.php

@@ -0,0 +1,86 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'voku\\' => array($vendorDir . '/voku/portable-ascii/src/voku'),
+    'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/type-resolver/src', $vendorDir . '/phpdocumentor/reflection-docblock/src'),
+    'Whoops\\' => array($vendorDir . '/filp/whoops/src/Whoops'),
+    'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
+    'TijsVerkoyen\\CssToInlineStyles\\' => array($vendorDir . '/tijsverkoyen/css-to-inline-styles/src'),
+    'Tests\\' => array($baseDir . '/tests'),
+    'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
+    'Symfony\\Polyfill\\Php73\\' => array($vendorDir . '/symfony/polyfill-php73'),
+    'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
+    'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
+    'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
+    'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
+    'Symfony\\Polyfill\\Intl\\Grapheme\\' => array($vendorDir . '/symfony/polyfill-intl-grapheme'),
+    'Symfony\\Polyfill\\Iconv\\' => array($vendorDir . '/symfony/polyfill-iconv'),
+    'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
+    'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'),
+    'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'),
+    'Symfony\\Contracts\\HttpClient\\' => array($vendorDir . '/symfony/http-client-contracts'),
+    'Symfony\\Contracts\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher-contracts'),
+    'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
+    'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
+    'Symfony\\Component\\String\\' => array($vendorDir . '/symfony/string'),
+    'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'),
+    'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
+    'Symfony\\Component\\Mime\\' => array($vendorDir . '/symfony/mime'),
+    'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'),
+    'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
+    'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
+    'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
+    'Symfony\\Component\\ErrorHandler\\' => array($vendorDir . '/symfony/error-handler'),
+    'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'),
+    'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
+    'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'),
+    'Ramsey\\Collection\\' => array($vendorDir . '/ramsey/collection/src'),
+    'Psy\\' => array($vendorDir . '/psy/psysh/src'),
+    'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
+    'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
+    'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
+    'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
+    'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'),
+    'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
+    'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src/Prophecy'),
+    'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
+    'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src/PhpOption'),
+    'Opis\\Closure\\' => array($vendorDir . '/opis/closure/src'),
+    'NunoMaduro\\Collision\\' => array($vendorDir . '/nunomaduro/collision/src'),
+    'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
+    'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
+    'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
+    'League\\CommonMark\\' => array($vendorDir . '/league/commonmark/src'),
+    'Laravel\\Tinker\\' => array($vendorDir . '/laravel/tinker/src'),
+    'Laravel\\Sail\\' => array($vendorDir . '/laravel/sail/src'),
+    'Illuminate\\Support\\' => array($vendorDir . '/laravel/framework/src/Illuminate/Macroable', $vendorDir . '/laravel/framework/src/Illuminate/Collections'),
+    'Illuminate\\' => array($vendorDir . '/laravel/framework/src/Illuminate'),
+    'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
+    'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
+    'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
+    'GrahamCampbell\\ResultType\\' => array($vendorDir . '/graham-campbell/result-type/src'),
+    'Fruitcake\\Cors\\' => array($vendorDir . '/fruitcake/laravel-cors/src'),
+    'Fideloper\\Proxy\\' => array($vendorDir . '/fideloper/proxy/src'),
+    'Faker\\' => array($vendorDir . '/fakerphp/faker/src/Faker'),
+    'Facade\\Ignition\\' => array($vendorDir . '/facade/ignition/src'),
+    'Facade\\IgnitionContracts\\' => array($vendorDir . '/facade/ignition-contracts/src'),
+    'Facade\\FlareClient\\' => array($vendorDir . '/facade/flare-client-php/src'),
+    'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/src'),
+    'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
+    'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'),
+    'Doctrine\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Inflector'),
+    'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'),
+    'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
+    'Database\\Seeders\\' => array($baseDir . '/database/seeders'),
+    'Database\\Factories\\' => array($baseDir . '/database/factories'),
+    'Cron\\' => array($vendorDir . '/dragonmantank/cron-expression/src/Cron'),
+    'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
+    'Brick\\Math\\' => array($vendorDir . '/brick/math/src'),
+    'Asm89\\Stack\\' => array($vendorDir . '/asm89/stack-cors/src'),
+    'App\\' => array($baseDir . '/app'),
+);

+ 80 - 0
vendor/composer/autoload_real.php

@@ -0,0 +1,80 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInitf3fdf6613c44671dda1ed31123da14a7
+{
+    private static $loader;
+
+    public static function loadClassLoader($class)
+    {
+        if ('Composer\Autoload\ClassLoader' === $class) {
+            require __DIR__ . '/ClassLoader.php';
+        }
+    }
+
+    /**
+     * @return \Composer\Autoload\ClassLoader
+     */
+    public static function getLoader()
+    {
+        if (null !== self::$loader) {
+            return self::$loader;
+        }
+
+        require __DIR__ . '/platform_check.php';
+
+        spl_autoload_register(array('ComposerAutoloaderInitf3fdf6613c44671dda1ed31123da14a7', 'loadClassLoader'), true, true);
+        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
+        spl_autoload_unregister(array('ComposerAutoloaderInitf3fdf6613c44671dda1ed31123da14a7', 'loadClassLoader'));
+
+        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+        if ($useStaticLoader) {
+            require __DIR__ . '/autoload_static.php';
+
+            call_user_func(\Composer\Autoload\ComposerStaticInitf3fdf6613c44671dda1ed31123da14a7::getInitializer($loader));
+        } else {
+            $map = require __DIR__ . '/autoload_namespaces.php';
+            foreach ($map as $namespace => $path) {
+                $loader->set($namespace, $path);
+            }
+
+            $map = require __DIR__ . '/autoload_psr4.php';
+            foreach ($map as $namespace => $path) {
+                $loader->setPsr4($namespace, $path);
+            }
+
+            $classMap = require __DIR__ . '/autoload_classmap.php';
+            if ($classMap) {
+                $loader->addClassMap($classMap);
+            }
+        }
+
+        $loader->register(true);
+
+        if ($useStaticLoader) {
+            $includeFiles = Composer\Autoload\ComposerStaticInitf3fdf6613c44671dda1ed31123da14a7::$files;
+        } else {
+            $includeFiles = require __DIR__ . '/autoload_files.php';
+        }
+        foreach ($includeFiles as $fileIdentifier => $file) {
+            composerRequiref3fdf6613c44671dda1ed31123da14a7($fileIdentifier, $file);
+        }
+
+        return $loader;
+    }
+}
+
+/**
+ * @param string $fileIdentifier
+ * @param string $file
+ * @return void
+ */
+function composerRequiref3fdf6613c44671dda1ed31123da14a7($fileIdentifier, $file)
+{
+    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+
+        require $file;
+    }
+}

File diff suppressed because it is too large
+ 5215 - 0
vendor/composer/autoload_static.php


File diff suppressed because it is too large
+ 7678 - 0
vendor/composer/installed.json


File diff suppressed because it is too large
+ 1221 - 0
vendor/composer/installed.php


+ 26 - 0
vendor/composer/platform_check.php

@@ -0,0 +1,26 @@
+<?php
+
+// platform_check.php @generated by Composer
+
+$issues = array();
+
+if (!(PHP_VERSION_ID >= 70300)) {
+    $issues[] = 'Your Composer dependencies require a PHP version ">= 7.3.0". You are running ' . PHP_VERSION . '.';
+}
+
+if ($issues) {
+    if (!headers_sent()) {
+        header('HTTP/1.1 500 Internal Server Error');
+    }
+    if (!ini_get('display_errors')) {
+        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+            fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
+        } elseif (!headers_sent()) {
+            echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
+        }
+    }
+    trigger_error(
+        'Composer detected issues in your platform: ' . implode(' ', $issues),
+        E_USER_ERROR
+    );
+}

+ 19 - 0
vendor/doctrine/inflector/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2006-2015 Doctrine Project
+
+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.

+ 8 - 0
vendor/doctrine/inflector/README.md

@@ -0,0 +1,8 @@
+# Doctrine Inflector
+
+Doctrine Inflector is a small library that can perform string manipulations
+with regard to uppercase/lowercase and singular/plural forms of words.
+
+[![Build Status](https://travis-ci.org/doctrine/inflector.svg)](https://travis-ci.org/doctrine/inflector)
+[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/doctrine/inflector/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/doctrine/inflector/?branch=master)
+[![Code Coverage](https://scrutinizer-ci.com/g/doctrine/inflector/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/doctrine/inflector/?branch=master)

+ 40 - 0
vendor/doctrine/inflector/composer.json

@@ -0,0 +1,40 @@
+{
+    "name": "doctrine/inflector",
+    "type": "library",
+    "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+    "keywords": ["php", "strings", "words", "manipulation", "inflector", "inflection", "uppercase", "lowercase", "singular", "plural"],
+    "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+    "license": "MIT",
+    "authors": [
+        {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
+        {"name": "Roman Borschel", "email": "roman@code-factory.org"},
+        {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
+        {"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
+        {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
+    ],
+    "require": {
+        "php": "^7.2 || ^8.0"
+    },
+    "require-dev": {
+        "doctrine/coding-standard": "^7.0",
+        "phpstan/phpstan": "^0.11",
+        "phpstan/phpstan-phpunit": "^0.11",
+        "phpstan/phpstan-strict-rules": "^0.11",
+        "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Doctrine\\Tests\\Inflector\\": "tests/Doctrine/Tests/Inflector"
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.0.x-dev"
+        }
+    }
+}

+ 226 - 0
vendor/doctrine/inflector/docs/en/index.rst

@@ -0,0 +1,226 @@
+Introduction
+============
+
+The Doctrine Inflector has methods for inflecting text. The features include pluralization,
+singularization, converting between camelCase and under_score and capitalizing
+words.
+
+Installation
+============
+
+You can install the Inflector with composer:
+
+.. code-block:: console
+
+    $ composer require doctrine/inflector
+
+Usage
+=====
+
+Using the inflector is easy, you can create a new ``Doctrine\Inflector\Inflector`` instance by using
+the ``Doctrine\Inflector\InflectorFactory`` class:
+
+.. code-block:: php
+
+    use Doctrine\Inflector\InflectorFactory;
+
+    $inflector = InflectorFactory::create()->build();
+
+By default it will create an English inflector. If you want to use another language, just pass the language
+you want to create an inflector for to the ``createForLanguage()`` method:
+
+.. code-block:: php
+
+    use Doctrine\Inflector\InflectorFactory;
+    use Doctrine\Inflector\Language;
+
+    $inflector = InflectorFactory::createForLanguage(Language::SPANISH)->build();
+
+The supported languages are as follows:
+
+- ``Language::ENGLISH``
+- ``Language::FRENCH``
+- ``Language::NORWEGIAN_BOKMAL``
+- ``Language::PORTUGUESE``
+- ``Language::SPANISH``
+- ``Language::TURKISH``
+
+If you want to manually construct the inflector instead of using a factory, you can do so like this:
+
+.. code-block:: php
+
+    use Doctrine\Inflector\CachedWordInflector;
+    use Doctrine\Inflector\RulesetInflector;
+    use Doctrine\Inflector\Rules\English;
+
+    $inflector = new Inflector(
+        new CachedWordInflector(new RulesetInflector(
+            English\Rules::getSingularRuleset()
+        )),
+        new CachedWordInflector(new RulesetInflector(
+            English\Rules::getPluralRuleset()
+        ))
+    );
+
+Adding Languages
+----------------
+
+If you are interested in adding support for your language, take a look at the other languages defined in the
+``Doctrine\Inflector\Rules`` namespace and the tests located in ``Doctrine\Tests\Inflector\Rules``. You can copy
+one of the languages and update the rules for your language.
+
+Once you have done this, send a pull request to the ``doctrine/inflector`` repository with the additions.
+
+Custom Setup
+============
+
+If you want to setup custom singular and plural rules, you can configure these in the factory:
+
+.. code-block:: php
+
+    use Doctrine\Inflector\InflectorFactory;
+    use Doctrine\Inflector\Rules\Pattern;
+    use Doctrine\Inflector\Rules\Patterns;
+    use Doctrine\Inflector\Rules\Ruleset;
+    use Doctrine\Inflector\Rules\Substitution;
+    use Doctrine\Inflector\Rules\Substitutions;
+    use Doctrine\Inflector\Rules\Transformation;
+    use Doctrine\Inflector\Rules\Transformations;
+    use Doctrine\Inflector\Rules\Word;
+
+    $inflector = InflectorFactory::create()
+        ->withSingularRules(
+            new Ruleset(
+                new Transformations(
+                    new Transformation(new Pattern('/^(bil)er$/i'), '\1'),
+                    new Transformation(new Pattern('/^(inflec|contribu)tors$/i'), '\1ta')
+                ),
+                new Patterns(new Pattern('singulars')),
+                new Substitutions(new Substitution(new Word('spins'), new Word('spinor')))
+            )
+        )
+        ->withPluralRules(
+            new Ruleset(
+                new Transformations(
+                    new Transformation(new Pattern('^(bil)er$'), '\1'),
+                    new Transformation(new Pattern('^(inflec|contribu)tors$'), '\1ta')
+                ),
+                new Patterns(new Pattern('noflect'), new Pattern('abtuse')),
+                new Substitutions(
+                    new Substitution(new Word('amaze'), new Word('amazable')),
+                    new Substitution(new Word('phone'), new Word('phonezes'))
+                )
+            )
+        )
+        ->build();
+
+No operation inflector
+----------------------
+
+The ``Doctrine\Inflector\NoopWordInflector`` may be used to configure an inflector that doesn't perform any operation for
+pluralization and/or singularization. If will simply return the input as output.
+
+This is an implementation of the `Null Object design pattern <https://sourcemaking.com/design_patterns/null_object>`_.
+
+.. code-block:: php
+
+    use Doctrine\Inflector\Inflector;
+    use Doctrine\Inflector\NoopWordInflector;
+
+    $inflector = new Inflector(new NoopWordInflector(), new NoopWordInflector());
+
+Tableize
+========
+
+Converts ``ModelName`` to ``model_name``:
+
+.. code-block:: php
+
+    echo $inflector->tableize('ModelName'); // model_name
+
+Classify
+========
+
+Converts ``model_name`` to ``ModelName``:
+
+.. code-block:: php
+
+    echo $inflector->classify('model_name'); // ModelName
+
+Camelize
+========
+
+This method uses `Classify`_ and then converts the first character to lowercase:
+
+.. code-block:: php
+
+    echo $inflector->camelize('model_name'); // modelName
+
+Capitalize
+==========
+
+Takes a string and capitalizes all of the words, like PHP's built-in
+``ucwords`` function. This extends that behavior, however, by allowing the
+word delimiters to be configured, rather than only separating on
+whitespace.
+
+Here is an example:
+
+.. code-block:: php
+
+    $string = 'top-o-the-morning to all_of_you!';
+
+    echo $inflector->capitalize($string); // Top-O-The-Morning To All_of_you!
+
+    echo $inflector->capitalize($string, '-_ '); // Top-O-The-Morning To All_Of_You!
+
+Pluralize
+=========
+
+Returns a word in plural form.
+
+.. code-block:: php
+
+    echo $inflector->pluralize('browser'); // browsers
+
+Singularize
+===========
+
+Returns a word in singular form.
+
+.. code-block:: php
+
+    echo $inflector->singularize('browsers'); // browser
+
+Urlize
+======
+
+Generate a URL friendly string from a string of text:
+
+.. code-block:: php
+
+    echo $inflector->urlize('My first blog post'); // my-first-blog-post
+
+Unaccent
+========
+
+You can unaccent a string of text using the ``unaccent()`` method:
+
+.. code-block:: php
+
+    echo $inflector->unaccent('año'); // ano
+
+Legacy API
+==========
+
+The API present in Inflector 1.x is still available, but will be deprecated in a future release and dropped for 3.0.
+Support for languages other than English is available in the 2.0 API only.
+
+Acknowledgements
+================
+
+The language rules in this library have been adapted from several different sources, including but not limited to:
+
+- `Ruby On Rails Inflector <http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html>`_
+- `ICanBoogie Inflector <https://github.com/ICanBoogie/Inflector>`_
+- `CakePHP Inflector <https://book.cakephp.org/3.0/en/core-libraries/inflector.html>`_

+ 24 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/CachedWordInflector.php

@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector;
+
+class CachedWordInflector implements WordInflector
+{
+    /** @var WordInflector */
+    private $wordInflector;
+
+    /** @var string[] */
+    private $cache = [];
+
+    public function __construct(WordInflector $wordInflector)
+    {
+        $this->wordInflector = $wordInflector;
+    }
+
+    public function inflect(string $word) : string
+    {
+        return $this->cache[$word] ?? $this->cache[$word] = $this->wordInflector->inflect($word);
+    }
+}

+ 65 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/GenericLanguageInflectorFactory.php

@@ -0,0 +1,65 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector;
+
+use Doctrine\Inflector\Rules\Ruleset;
+use function array_unshift;
+
+abstract class GenericLanguageInflectorFactory implements LanguageInflectorFactory
+{
+    /** @var Ruleset[] */
+    private $singularRulesets = [];
+
+    /** @var Ruleset[] */
+    private $pluralRulesets = [];
+
+    final public function __construct()
+    {
+        $this->singularRulesets[] = $this->getSingularRuleset();
+        $this->pluralRulesets[]   = $this->getPluralRuleset();
+    }
+
+    final public function build() : Inflector
+    {
+        return new Inflector(
+            new CachedWordInflector(new RulesetInflector(
+                ...$this->singularRulesets
+            )),
+            new CachedWordInflector(new RulesetInflector(
+                ...$this->pluralRulesets
+            ))
+        );
+    }
+
+    final public function withSingularRules(?Ruleset $singularRules, bool $reset = false) : LanguageInflectorFactory
+    {
+        if ($reset) {
+            $this->singularRulesets = [];
+        }
+
+        if ($singularRules instanceof Ruleset) {
+            array_unshift($this->singularRulesets, $singularRules);
+        }
+
+        return $this;
+    }
+
+    final public function withPluralRules(?Ruleset $pluralRules, bool $reset = false) : LanguageInflectorFactory
+    {
+        if ($reset) {
+            $this->pluralRulesets = [];
+        }
+
+        if ($pluralRules instanceof Ruleset) {
+            array_unshift($this->pluralRulesets, $pluralRules);
+        }
+
+        return $this;
+    }
+
+    abstract protected function getSingularRuleset() : Ruleset;
+
+    abstract protected function getPluralRuleset() : Ruleset;
+}

+ 506 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Inflector.php

@@ -0,0 +1,506 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector;
+
+use RuntimeException;
+use function chr;
+use function function_exists;
+use function lcfirst;
+use function mb_strtolower;
+use function ord;
+use function preg_match;
+use function preg_replace;
+use function sprintf;
+use function str_replace;
+use function strlen;
+use function strtolower;
+use function strtr;
+use function trim;
+use function ucwords;
+
+class Inflector
+{
+    private const ACCENTED_CHARACTERS = [
+        'À' => 'A',
+        'Á' => 'A',
+        'Â' => 'A',
+        'Ã' => 'A',
+        'Ä' => 'Ae',
+        'Æ' => 'Ae',
+        'Å' => 'Aa',
+        'æ' => 'a',
+        'Ç' => 'C',
+        'È' => 'E',
+        'É' => 'E',
+        'Ê' => 'E',
+        'Ë' => 'E',
+        'Ì' => 'I',
+        'Í' => 'I',
+        'Î' => 'I',
+        'Ï' => 'I',
+        'Ñ' => 'N',
+        'Ò' => 'O',
+        'Ó' => 'O',
+        'Ô' => 'O',
+        'Õ' => 'O',
+        'Ö' => 'Oe',
+        'Ù' => 'U',
+        'Ú' => 'U',
+        'Û' => 'U',
+        'Ü' => 'Ue',
+        'Ý' => 'Y',
+        'ß' => 'ss',
+        'à' => 'a',
+        'á' => 'a',
+        'â' => 'a',
+        'ã' => 'a',
+        'ä' => 'ae',
+        'å' => 'aa',
+        'ç' => 'c',
+        'è' => 'e',
+        'é' => 'e',
+        'ê' => 'e',
+        'ë' => 'e',
+        'ì' => 'i',
+        'í' => 'i',
+        'î' => 'i',
+        'ï' => 'i',
+        'ñ' => 'n',
+        'ò' => 'o',
+        'ó' => 'o',
+        'ô' => 'o',
+        'õ' => 'o',
+        'ö' => 'oe',
+        'ù' => 'u',
+        'ú' => 'u',
+        'û' => 'u',
+        'ü' => 'ue',
+        'ý' => 'y',
+        'ÿ' => 'y',
+        'Ā' => 'A',
+        'ā' => 'a',
+        'Ă' => 'A',
+        'ă' => 'a',
+        'Ą' => 'A',
+        'ą' => 'a',
+        'Ć' => 'C',
+        'ć' => 'c',
+        'Ĉ' => 'C',
+        'ĉ' => 'c',
+        'Ċ' => 'C',
+        'ċ' => 'c',
+        'Č' => 'C',
+        'č' => 'c',
+        'Ď' => 'D',
+        'ď' => 'd',
+        'Đ' => 'D',
+        'đ' => 'd',
+        'Ē' => 'E',
+        'ē' => 'e',
+        'Ĕ' => 'E',
+        'ĕ' => 'e',
+        'Ė' => 'E',
+        'ė' => 'e',
+        'Ę' => 'E',
+        'ę' => 'e',
+        'Ě' => 'E',
+        'ě' => 'e',
+        'Ĝ' => 'G',
+        'ĝ' => 'g',
+        'Ğ' => 'G',
+        'ğ' => 'g',
+        'Ġ' => 'G',
+        'ġ' => 'g',
+        'Ģ' => 'G',
+        'ģ' => 'g',
+        'Ĥ' => 'H',
+        'ĥ' => 'h',
+        'Ħ' => 'H',
+        'ħ' => 'h',
+        'Ĩ' => 'I',
+        'ĩ' => 'i',
+        'Ī' => 'I',
+        'ī' => 'i',
+        'Ĭ' => 'I',
+        'ĭ' => 'i',
+        'Į' => 'I',
+        'į' => 'i',
+        'İ' => 'I',
+        'ı' => 'i',
+        'IJ' => 'IJ',
+        'ij' => 'ij',
+        'Ĵ' => 'J',
+        'ĵ' => 'j',
+        'Ķ' => 'K',
+        'ķ' => 'k',
+        'ĸ' => 'k',
+        'Ĺ' => 'L',
+        'ĺ' => 'l',
+        'Ļ' => 'L',
+        'ļ' => 'l',
+        'Ľ' => 'L',
+        'ľ' => 'l',
+        'Ŀ' => 'L',
+        'ŀ' => 'l',
+        'Ł' => 'L',
+        'ł' => 'l',
+        'Ń' => 'N',
+        'ń' => 'n',
+        'Ņ' => 'N',
+        'ņ' => 'n',
+        'Ň' => 'N',
+        'ň' => 'n',
+        'ʼn' => 'N',
+        'Ŋ' => 'n',
+        'ŋ' => 'N',
+        'Ō' => 'O',
+        'ō' => 'o',
+        'Ŏ' => 'O',
+        'ŏ' => 'o',
+        'Ő' => 'O',
+        'ő' => 'o',
+        'Œ' => 'OE',
+        'œ' => 'oe',
+        'Ø' => 'O',
+        'ø' => 'o',
+        'Ŕ' => 'R',
+        'ŕ' => 'r',
+        'Ŗ' => 'R',
+        'ŗ' => 'r',
+        'Ř' => 'R',
+        'ř' => 'r',
+        'Ś' => 'S',
+        'ś' => 's',
+        'Ŝ' => 'S',
+        'ŝ' => 's',
+        'Ş' => 'S',
+        'ş' => 's',
+        'Š' => 'S',
+        'š' => 's',
+        'Ţ' => 'T',
+        'ţ' => 't',
+        'Ť' => 'T',
+        'ť' => 't',
+        'Ŧ' => 'T',
+        'ŧ' => 't',
+        'Ũ' => 'U',
+        'ũ' => 'u',
+        'Ū' => 'U',
+        'ū' => 'u',
+        'Ŭ' => 'U',
+        'ŭ' => 'u',
+        'Ů' => 'U',
+        'ů' => 'u',
+        'Ű' => 'U',
+        'ű' => 'u',
+        'Ų' => 'U',
+        'ų' => 'u',
+        'Ŵ' => 'W',
+        'ŵ' => 'w',
+        'Ŷ' => 'Y',
+        'ŷ' => 'y',
+        'Ÿ' => 'Y',
+        'Ź' => 'Z',
+        'ź' => 'z',
+        'Ż' => 'Z',
+        'ż' => 'z',
+        'Ž' => 'Z',
+        'ž' => 'z',
+        'ſ' => 's',
+        '€' => 'E',
+        '£' => '',
+    ];
+
+    /** @var WordInflector */
+    private $singularizer;
+
+    /** @var WordInflector */
+    private $pluralizer;
+
+    public function __construct(WordInflector $singularizer, WordInflector $pluralizer)
+    {
+        $this->singularizer = $singularizer;
+        $this->pluralizer   = $pluralizer;
+    }
+
+    /**
+     * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'.
+     */
+    public function tableize(string $word) : string
+    {
+        $tableized = preg_replace('~(?<=\\w)([A-Z])~u', '_$1', $word);
+
+        if ($tableized === null) {
+            throw new RuntimeException(sprintf(
+                'preg_replace returned null for value "%s"',
+                $word
+            ));
+        }
+
+        return mb_strtolower($tableized);
+    }
+
+    /**
+     * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'.
+     */
+    public function classify(string $word) : string
+    {
+        return str_replace([' ', '_', '-'], '', ucwords($word, ' _-'));
+    }
+
+    /**
+     * Camelizes a word. This uses the classify() method and turns the first character to lowercase.
+     */
+    public function camelize(string $word) : string
+    {
+        return lcfirst($this->classify($word));
+    }
+
+    /**
+     * Uppercases words with configurable delimiters between words.
+     *
+     * Takes a string and capitalizes all of the words, like PHP's built-in
+     * ucwords function. This extends that behavior, however, by allowing the
+     * word delimiters to be configured, rather than only separating on
+     * whitespace.
+     *
+     * Here is an example:
+     * <code>
+     * <?php
+     * $string = 'top-o-the-morning to all_of_you!';
+     * echo $inflector->capitalize($string);
+     * // Top-O-The-Morning To All_of_you!
+     *
+     * echo $inflector->capitalize($string, '-_ ');
+     * // Top-O-The-Morning To All_Of_You!
+     * ?>
+     * </code>
+     *
+     * @param string $string     The string to operate on.
+     * @param string $delimiters A list of word separators.
+     *
+     * @return string The string with all delimiter-separated words capitalized.
+     */
+    public function capitalize(string $string, string $delimiters = " \n\t\r\0\x0B-") : string
+    {
+        return ucwords($string, $delimiters);
+    }
+
+    /**
+     * Checks if the given string seems like it has utf8 characters in it.
+     *
+     * @param string $string The string to check for utf8 characters in.
+     */
+    public function seemsUtf8(string $string) : bool
+    {
+        for ($i = 0; $i < strlen($string); $i++) {
+            if (ord($string[$i]) < 0x80) {
+                continue; // 0bbbbbbb
+            }
+
+            if ((ord($string[$i]) & 0xE0) === 0xC0) {
+                $n = 1; // 110bbbbb
+            } elseif ((ord($string[$i]) & 0xF0) === 0xE0) {
+                $n = 2; // 1110bbbb
+            } elseif ((ord($string[$i]) & 0xF8) === 0xF0) {
+                $n = 3; // 11110bbb
+            } elseif ((ord($string[$i]) & 0xFC) === 0xF8) {
+                $n = 4; // 111110bb
+            } elseif ((ord($string[$i]) & 0xFE) === 0xFC) {
+                $n = 5; // 1111110b
+            } else {
+                return false; // Does not match any model
+            }
+
+            for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
+                if (++$i === strlen($string) || ((ord($string[$i]) & 0xC0) !== 0x80)) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Remove any illegal characters, accents, etc.
+     *
+     * @param  string $string String to unaccent
+     *
+     * @return string Unaccented string
+     */
+    public function unaccent(string $string) : string
+    {
+        if (preg_match('/[\x80-\xff]/', $string) === false) {
+            return $string;
+        }
+
+        if ($this->seemsUtf8($string)) {
+            $string = strtr($string, self::ACCENTED_CHARACTERS);
+        } else {
+            $characters = [];
+
+            // Assume ISO-8859-1 if not UTF-8
+            $characters['in'] =
+                  chr(128)
+                . chr(131)
+                . chr(138)
+                . chr(142)
+                . chr(154)
+                . chr(158)
+                . chr(159)
+                . chr(162)
+                . chr(165)
+                . chr(181)
+                . chr(192)
+                . chr(193)
+                . chr(194)
+                . chr(195)
+                . chr(196)
+                . chr(197)
+                . chr(199)
+                . chr(200)
+                . chr(201)
+                . chr(202)
+                . chr(203)
+                . chr(204)
+                . chr(205)
+                . chr(206)
+                . chr(207)
+                . chr(209)
+                . chr(210)
+                . chr(211)
+                . chr(212)
+                . chr(213)
+                . chr(214)
+                . chr(216)
+                . chr(217)
+                . chr(218)
+                . chr(219)
+                . chr(220)
+                . chr(221)
+                . chr(224)
+                . chr(225)
+                . chr(226)
+                . chr(227)
+                . chr(228)
+                . chr(229)
+                . chr(231)
+                . chr(232)
+                . chr(233)
+                . chr(234)
+                . chr(235)
+                . chr(236)
+                . chr(237)
+                . chr(238)
+                . chr(239)
+                . chr(241)
+                . chr(242)
+                . chr(243)
+                . chr(244)
+                . chr(245)
+                . chr(246)
+                . chr(248)
+                . chr(249)
+                . chr(250)
+                . chr(251)
+                . chr(252)
+                . chr(253)
+                . chr(255);
+
+            $characters['out'] = 'EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy';
+
+            $string = strtr($string, $characters['in'], $characters['out']);
+
+            $doubleChars = [];
+
+            $doubleChars['in'] = [
+                chr(140),
+                chr(156),
+                chr(198),
+                chr(208),
+                chr(222),
+                chr(223),
+                chr(230),
+                chr(240),
+                chr(254),
+            ];
+
+            $doubleChars['out'] = ['OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'];
+
+            $string = str_replace($doubleChars['in'], $doubleChars['out'], $string);
+        }
+
+        return $string;
+    }
+
+    /**
+     * Convert any passed string to a url friendly string.
+     * Converts 'My first blog post' to 'my-first-blog-post'
+     *
+     * @param  string $string String to urlize.
+     *
+     * @return string Urlized string.
+     */
+    public function urlize(string $string) : string
+    {
+        // Remove all non url friendly characters with the unaccent function
+        $unaccented = $this->unaccent($string);
+
+        if (function_exists('mb_strtolower')) {
+            $lowered = mb_strtolower($unaccented);
+        } else {
+            $lowered = strtolower($unaccented);
+        }
+
+        $replacements = [
+            '/\W/' => ' ',
+            '/([A-Z]+)([A-Z][a-z])/' => '\1_\2',
+            '/([a-z\d])([A-Z])/' => '\1_\2',
+            '/[^A-Z^a-z^0-9^\/]+/' => '-',
+        ];
+
+        $urlized = $lowered;
+
+        foreach ($replacements as $pattern => $replacement) {
+            $replaced = preg_replace($pattern, $replacement, $urlized);
+
+            if ($replaced === null) {
+                throw new RuntimeException(sprintf(
+                    'preg_replace returned null for value "%s"',
+                    $urlized
+                ));
+            }
+
+            $urlized = $replaced;
+        }
+
+        return trim($urlized, '-');
+    }
+
+    /**
+     * Returns a word in singular form.
+     *
+     * @param string $word The word in plural form.
+     *
+     * @return string The word in singular form.
+     */
+    public function singularize(string $word) : string
+    {
+        return $this->singularizer->inflect($word);
+    }
+
+    /**
+     * Returns a word in plural form.
+     *
+     * @param string $word The word in singular form.
+     *
+     * @return string The word in plural form.
+     */
+    public function pluralize(string $word) : string
+    {
+        return $this->pluralizer->inflect($word);
+    }
+}

+ 45 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/InflectorFactory.php

@@ -0,0 +1,45 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector;
+
+use Doctrine\Inflector\Rules\English;
+use Doctrine\Inflector\Rules\French;
+use Doctrine\Inflector\Rules\NorwegianBokmal;
+use Doctrine\Inflector\Rules\Portuguese;
+use Doctrine\Inflector\Rules\Spanish;
+use Doctrine\Inflector\Rules\Turkish;
+use InvalidArgumentException;
+use function sprintf;
+
+final class InflectorFactory
+{
+    public static function create() : LanguageInflectorFactory
+    {
+        return self::createForLanguage(Language::ENGLISH);
+    }
+
+    public static function createForLanguage(string $language) : LanguageInflectorFactory
+    {
+        switch ($language) {
+            case Language::ENGLISH:
+                return new English\InflectorFactory();
+            case Language::FRENCH:
+                return new French\InflectorFactory();
+            case Language::NORWEGIAN_BOKMAL:
+                return new NorwegianBokmal\InflectorFactory();
+            case Language::PORTUGUESE:
+                return new Portuguese\InflectorFactory();
+            case Language::SPANISH:
+                return new Spanish\InflectorFactory();
+            case Language::TURKISH:
+                return new Turkish\InflectorFactory();
+            default:
+                throw new InvalidArgumentException(sprintf(
+                    'Language "%s" is not supported.',
+                    $language
+                ));
+        }
+    }
+}

+ 19 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Language.php

@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector;
+
+final class Language
+{
+    public const ENGLISH          = 'english';
+    public const FRENCH           = 'french';
+    public const NORWEGIAN_BOKMAL = 'norwegian-bokmal';
+    public const PORTUGUESE       = 'portuguese';
+    public const SPANISH          = 'spanish';
+    public const TURKISH          = 'turkish';
+
+    private function __construct()
+    {
+    }
+}

+ 33 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/LanguageInflectorFactory.php

@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector;
+
+use Doctrine\Inflector\Rules\Ruleset;
+
+interface LanguageInflectorFactory
+{
+    /**
+     * Applies custom rules for singularisation
+     *
+     * @param bool $reset If true, will unset default inflections for all new rules
+     *
+     * @return $this
+     */
+    public function withSingularRules(?Ruleset $singularRules, bool $reset = false) : self;
+
+    /**
+     * Applies custom rules for pluralisation
+     *
+     * @param bool $reset If true, will unset default inflections for all new rules
+     *
+     * @return $this
+     */
+    public function withPluralRules(?Ruleset $pluralRules, bool $reset = false) : self;
+
+    /**
+     * Builds the inflector instance with all applicable rules
+     */
+    public function build() : Inflector;
+}

+ 13 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/NoopWordInflector.php

@@ -0,0 +1,13 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector;
+
+class NoopWordInflector implements WordInflector
+{
+    public function inflect(string $word) : string
+    {
+        return $word;
+    }
+}

+ 182 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Inflectible.php

@@ -0,0 +1,182 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\English;
+
+use Doctrine\Inflector\Rules\Pattern;
+use Doctrine\Inflector\Rules\Substitution;
+use Doctrine\Inflector\Rules\Transformation;
+use Doctrine\Inflector\Rules\Word;
+
+class Inflectible
+{
+    /**
+     * @return Transformation[]
+     */
+    public static function getSingular() : iterable
+    {
+        yield new Transformation(new Pattern('(s)tatuses$'), '\1\2tatus');
+        yield new Transformation(new Pattern('(s)tatus$'), '\1\2tatus');
+        yield new Transformation(new Pattern('(c)ampus$'), '\1\2ampus');
+        yield new Transformation(new Pattern('^(.*)(menu)s$'), '\1\2');
+        yield new Transformation(new Pattern('(quiz)zes$'), '\\1');
+        yield new Transformation(new Pattern('(matr)ices$'), '\1ix');
+        yield new Transformation(new Pattern('(vert|ind)ices$'), '\1ex');
+        yield new Transformation(new Pattern('^(ox)en'), '\1');
+        yield new Transformation(new Pattern('(alias)(es)*$'), '\1');
+        yield new Transformation(new Pattern('(buffal|her|potat|tomat|volcan)oes$'), '\1o');
+        yield new Transformation(new Pattern('(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$'), '\1us');
+        yield new Transformation(new Pattern('([ftw]ax)es'), '\1');
+        yield new Transformation(new Pattern('(analys|ax|cris|test|thes)es$'), '\1is');
+        yield new Transformation(new Pattern('(shoe|slave)s$'), '\1');
+        yield new Transformation(new Pattern('(o)es$'), '\1');
+        yield new Transformation(new Pattern('ouses$'), 'ouse');
+        yield new Transformation(new Pattern('([^a])uses$'), '\1us');
+        yield new Transformation(new Pattern('([m|l])ice$'), '\1ouse');
+        yield new Transformation(new Pattern('(x|ch|ss|sh)es$'), '\1');
+        yield new Transformation(new Pattern('(m)ovies$'), '\1\2ovie');
+        yield new Transformation(new Pattern('(s)eries$'), '\1\2eries');
+        yield new Transformation(new Pattern('([^aeiouy]|qu)ies$'), '\1y');
+        yield new Transformation(new Pattern('([lr])ves$'), '\1f');
+        yield new Transformation(new Pattern('(tive)s$'), '\1');
+        yield new Transformation(new Pattern('(hive)s$'), '\1');
+        yield new Transformation(new Pattern('(drive)s$'), '\1');
+        yield new Transformation(new Pattern('(dive)s$'), '\1');
+        yield new Transformation(new Pattern('(olive)s$'), '\1');
+        yield new Transformation(new Pattern('([^fo])ves$'), '\1fe');
+        yield new Transformation(new Pattern('(^analy)ses$'), '\1sis');
+        yield new Transformation(new Pattern('(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$'), '\1\2sis');
+        yield new Transformation(new Pattern('(tax)a$'), '\1on');
+        yield new Transformation(new Pattern('(c)riteria$'), '\1riterion');
+        yield new Transformation(new Pattern('([ti])a$'), '\1um');
+        yield new Transformation(new Pattern('(p)eople$'), '\1\2erson');
+        yield new Transformation(new Pattern('(m)en$'), '\1an');
+        yield new Transformation(new Pattern('(c)hildren$'), '\1\2hild');
+        yield new Transformation(new Pattern('(f)eet$'), '\1oot');
+        yield new Transformation(new Pattern('(n)ews$'), '\1\2ews');
+        yield new Transformation(new Pattern('eaus$'), 'eau');
+        yield new Transformation(new Pattern('s$'), '');
+    }
+
+    /**
+     * @return Transformation[]
+     */
+    public static function getPlural() : iterable
+    {
+        yield new Transformation(new Pattern('(s)tatus$'), '\1\2tatuses');
+        yield new Transformation(new Pattern('(quiz)$'), '\1zes');
+        yield new Transformation(new Pattern('^(ox)$'), '\1\2en');
+        yield new Transformation(new Pattern('([m|l])ouse$'), '\1ice');
+        yield new Transformation(new Pattern('(matr|vert|ind)(ix|ex)$'), '\1ices');
+        yield new Transformation(new Pattern('(x|ch|ss|sh)$'), '\1es');
+        yield new Transformation(new Pattern('([^aeiouy]|qu)y$'), '\1ies');
+        yield new Transformation(new Pattern('(hive|gulf)$'), '\1s');
+        yield new Transformation(new Pattern('(?:([^f])fe|([lr])f)$'), '\1\2ves');
+        yield new Transformation(new Pattern('sis$'), 'ses');
+        yield new Transformation(new Pattern('([ti])um$'), '\1a');
+        yield new Transformation(new Pattern('(tax)on$'), '\1a');
+        yield new Transformation(new Pattern('(c)riterion$'), '\1riteria');
+        yield new Transformation(new Pattern('(p)erson$'), '\1eople');
+        yield new Transformation(new Pattern('(m)an$'), '\1en');
+        yield new Transformation(new Pattern('(c)hild$'), '\1hildren');
+        yield new Transformation(new Pattern('(f)oot$'), '\1eet');
+        yield new Transformation(new Pattern('(buffal|her|potat|tomat|volcan)o$'), '\1\2oes');
+        yield new Transformation(new Pattern('(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$'), '\1i');
+        yield new Transformation(new Pattern('us$'), 'uses');
+        yield new Transformation(new Pattern('(alias)$'), '\1es');
+        yield new Transformation(new Pattern('(analys|ax|cris|test|thes)is$'), '\1es');
+        yield new Transformation(new Pattern('s$'), 's');
+        yield new Transformation(new Pattern('^$'), '');
+        yield new Transformation(new Pattern('$'), 's');
+    }
+
+    /**
+     * @return Substitution[]
+     */
+    public static function getIrregular() : iterable
+    {
+        yield new Substitution(new Word('atlas'), new Word('atlases'));
+        yield new Substitution(new Word('axe'), new Word('axes'));
+        yield new Substitution(new Word('beef'), new Word('beefs'));
+        yield new Substitution(new Word('brother'), new Word('brothers'));
+        yield new Substitution(new Word('cafe'), new Word('cafes'));
+        yield new Substitution(new Word('chateau'), new Word('chateaux'));
+        yield new Substitution(new Word('niveau'), new Word('niveaux'));
+        yield new Substitution(new Word('child'), new Word('children'));
+        yield new Substitution(new Word('canvas'), new Word('canvases'));
+        yield new Substitution(new Word('cookie'), new Word('cookies'));
+        yield new Substitution(new Word('corpus'), new Word('corpuses'));
+        yield new Substitution(new Word('cow'), new Word('cows'));
+        yield new Substitution(new Word('criterion'), new Word('criteria'));
+        yield new Substitution(new Word('curriculum'), new Word('curricula'));
+        yield new Substitution(new Word('demo'), new Word('demos'));
+        yield new Substitution(new Word('domino'), new Word('dominoes'));
+        yield new Substitution(new Word('echo'), new Word('echoes'));
+        yield new Substitution(new Word('foot'), new Word('feet'));
+        yield new Substitution(new Word('fungus'), new Word('fungi'));
+        yield new Substitution(new Word('ganglion'), new Word('ganglions'));
+        yield new Substitution(new Word('gas'), new Word('gases'));
+        yield new Substitution(new Word('genie'), new Word('genies'));
+        yield new Substitution(new Word('genus'), new Word('genera'));
+        yield new Substitution(new Word('goose'), new Word('geese'));
+        yield new Substitution(new Word('graffito'), new Word('graffiti'));
+        yield new Substitution(new Word('hippopotamus'), new Word('hippopotami'));
+        yield new Substitution(new Word('hoof'), new Word('hoofs'));
+        yield new Substitution(new Word('human'), new Word('humans'));
+        yield new Substitution(new Word('iris'), new Word('irises'));
+        yield new Substitution(new Word('larva'), new Word('larvae'));
+        yield new Substitution(new Word('leaf'), new Word('leaves'));
+        yield new Substitution(new Word('lens'), new Word('lenses'));
+        yield new Substitution(new Word('loaf'), new Word('loaves'));
+        yield new Substitution(new Word('man'), new Word('men'));
+        yield new Substitution(new Word('medium'), new Word('media'));
+        yield new Substitution(new Word('memorandum'), new Word('memoranda'));
+        yield new Substitution(new Word('money'), new Word('monies'));
+        yield new Substitution(new Word('mongoose'), new Word('mongooses'));
+        yield new Substitution(new Word('motto'), new Word('mottoes'));
+        yield new Substitution(new Word('move'), new Word('moves'));
+        yield new Substitution(new Word('mythos'), new Word('mythoi'));
+        yield new Substitution(new Word('niche'), new Word('niches'));
+        yield new Substitution(new Word('nucleus'), new Word('nuclei'));
+        yield new Substitution(new Word('numen'), new Word('numina'));
+        yield new Substitution(new Word('occiput'), new Word('occiputs'));
+        yield new Substitution(new Word('octopus'), new Word('octopuses'));
+        yield new Substitution(new Word('opus'), new Word('opuses'));
+        yield new Substitution(new Word('ox'), new Word('oxen'));
+        yield new Substitution(new Word('passerby'), new Word('passersby'));
+        yield new Substitution(new Word('penis'), new Word('penises'));
+        yield new Substitution(new Word('person'), new Word('people'));
+        yield new Substitution(new Word('plateau'), new Word('plateaux'));
+        yield new Substitution(new Word('runner-up'), new Word('runners-up'));
+        yield new Substitution(new Word('safe'), new Word('safes'));
+        yield new Substitution(new Word('sex'), new Word('sexes'));
+        yield new Substitution(new Word('soliloquy'), new Word('soliloquies'));
+        yield new Substitution(new Word('son-in-law'), new Word('sons-in-law'));
+        yield new Substitution(new Word('syllabus'), new Word('syllabi'));
+        yield new Substitution(new Word('testis'), new Word('testes'));
+        yield new Substitution(new Word('thief'), new Word('thieves'));
+        yield new Substitution(new Word('tooth'), new Word('teeth'));
+        yield new Substitution(new Word('tornado'), new Word('tornadoes'));
+        yield new Substitution(new Word('trilby'), new Word('trilbys'));
+        yield new Substitution(new Word('turf'), new Word('turfs'));
+        yield new Substitution(new Word('valve'), new Word('valves'));
+        yield new Substitution(new Word('volcano'), new Word('volcanoes'));
+        yield new Substitution(new Word('abuse'), new Word('abuses'));
+        yield new Substitution(new Word('avalanche'), new Word('avalanches'));
+        yield new Substitution(new Word('cache'), new Word('caches'));
+        yield new Substitution(new Word('criterion'), new Word('criteria'));
+        yield new Substitution(new Word('curve'), new Word('curves'));
+        yield new Substitution(new Word('emphasis'), new Word('emphases'));
+        yield new Substitution(new Word('foe'), new Word('foes'));
+        yield new Substitution(new Word('grave'), new Word('graves'));
+        yield new Substitution(new Word('hoax'), new Word('hoaxes'));
+        yield new Substitution(new Word('medium'), new Word('media'));
+        yield new Substitution(new Word('neurosis'), new Word('neuroses'));
+        yield new Substitution(new Word('save'), new Word('saves'));
+        yield new Substitution(new Word('wave'), new Word('waves'));
+        yield new Substitution(new Word('oasis'), new Word('oases'));
+        yield new Substitution(new Word('valve'), new Word('valves'));
+        yield new Substitution(new Word('zombie'), new Word('zombies'));
+    }
+}

+ 21 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/InflectorFactory.php

@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\English;
+
+use Doctrine\Inflector\GenericLanguageInflectorFactory;
+use Doctrine\Inflector\Rules\Ruleset;
+
+final class InflectorFactory extends GenericLanguageInflectorFactory
+{
+    protected function getSingularRuleset() : Ruleset
+    {
+        return Rules::getSingularRuleset();
+    }
+
+    protected function getPluralRuleset() : Ruleset
+    {
+        return Rules::getPluralRuleset();
+    }
+}

+ 31 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Rules.php

@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\English;
+
+use Doctrine\Inflector\Rules\Patterns;
+use Doctrine\Inflector\Rules\Ruleset;
+use Doctrine\Inflector\Rules\Substitutions;
+use Doctrine\Inflector\Rules\Transformations;
+
+final class Rules
+{
+    public static function getSingularRuleset() : Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getSingular()),
+            new Patterns(...Uninflected::getSingular()),
+            (new Substitutions(...Inflectible::getIrregular()))->getFlippedSubstitutions()
+        );
+    }
+
+    public static function getPluralRuleset() : Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getPlural()),
+            new Patterns(...Uninflected::getPlural()),
+            new Substitutions(...Inflectible::getIrregular())
+        );
+    }
+}

+ 193 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Uninflected.php

@@ -0,0 +1,193 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\English;
+
+use Doctrine\Inflector\Rules\Pattern;
+
+final class Uninflected
+{
+    /**
+     * @return Pattern[]
+     */
+    public static function getSingular() : iterable
+    {
+        yield from self::getDefault();
+
+        yield new Pattern('.*ss');
+        yield new Pattern('clothes');
+        yield new Pattern('data');
+        yield new Pattern('fascia');
+        yield new Pattern('fuchsia');
+        yield new Pattern('galleria');
+        yield new Pattern('mafia');
+        yield new Pattern('militia');
+        yield new Pattern('pants');
+        yield new Pattern('petunia');
+        yield new Pattern('sepia');
+        yield new Pattern('trivia');
+        yield new Pattern('utopia');
+    }
+
+    /**
+     * @return Pattern[]
+     */
+    public static function getPlural() : iterable
+    {
+        yield from self::getDefault();
+
+        yield new Pattern('people');
+        yield new Pattern('trivia');
+        yield new Pattern('\w+ware$');
+        yield new Pattern('media');
+    }
+
+    /**
+     * @return Pattern[]
+     */
+    private static function getDefault() : iterable
+    {
+        yield new Pattern('\w+media');
+        yield new Pattern('advice');
+        yield new Pattern('aircraft');
+        yield new Pattern('amoyese');
+        yield new Pattern('art');
+        yield new Pattern('audio');
+        yield new Pattern('baggage');
+        yield new Pattern('bison');
+        yield new Pattern('borghese');
+        yield new Pattern('bream');
+        yield new Pattern('breeches');
+        yield new Pattern('britches');
+        yield new Pattern('buffalo');
+        yield new Pattern('butter');
+        yield new Pattern('cantus');
+        yield new Pattern('carp');
+        yield new Pattern('chassis');
+        yield new Pattern('clippers');
+        yield new Pattern('clothing');
+        yield new Pattern('coal');
+        yield new Pattern('cod');
+        yield new Pattern('coitus');
+        yield new Pattern('compensation');
+        yield new Pattern('congoese');
+        yield new Pattern('contretemps');
+        yield new Pattern('coreopsis');
+        yield new Pattern('corps');
+        yield new Pattern('cotton');
+        yield new Pattern('data');
+        yield new Pattern('debris');
+        yield new Pattern('deer');
+        yield new Pattern('diabetes');
+        yield new Pattern('djinn');
+        yield new Pattern('education');
+        yield new Pattern('eland');
+        yield new Pattern('elk');
+        yield new Pattern('emoji');
+        yield new Pattern('equipment');
+        yield new Pattern('evidence');
+        yield new Pattern('faroese');
+        yield new Pattern('feedback');
+        yield new Pattern('fish');
+        yield new Pattern('flounder');
+        yield new Pattern('flour');
+        yield new Pattern('foochowese');
+        yield new Pattern('food');
+        yield new Pattern('furniture');
+        yield new Pattern('gallows');
+        yield new Pattern('genevese');
+        yield new Pattern('genoese');
+        yield new Pattern('gilbertese');
+        yield new Pattern('gold');
+        yield new Pattern('headquarters');
+        yield new Pattern('herpes');
+        yield new Pattern('hijinks');
+        yield new Pattern('homework');
+        yield new Pattern('hottentotese');
+        yield new Pattern('impatience');
+        yield new Pattern('information');
+        yield new Pattern('innings');
+        yield new Pattern('jackanapes');
+        yield new Pattern('jeans');
+        yield new Pattern('jedi');
+        yield new Pattern('kiplingese');
+        yield new Pattern('knowledge');
+        yield new Pattern('kongoese');
+        yield new Pattern('leather');
+        yield new Pattern('love');
+        yield new Pattern('lucchese');
+        yield new Pattern('luggage');
+        yield new Pattern('mackerel');
+        yield new Pattern('Maltese');
+        yield new Pattern('management');
+        yield new Pattern('metadata');
+        yield new Pattern('mews');
+        yield new Pattern('money');
+        yield new Pattern('moose');
+        yield new Pattern('mumps');
+        yield new Pattern('music');
+        yield new Pattern('nankingese');
+        yield new Pattern('news');
+        yield new Pattern('nexus');
+        yield new Pattern('niasese');
+        yield new Pattern('nutrition');
+        yield new Pattern('offspring');
+        yield new Pattern('oil');
+        yield new Pattern('patience');
+        yield new Pattern('pekingese');
+        yield new Pattern('piedmontese');
+        yield new Pattern('pincers');
+        yield new Pattern('pistoiese');
+        yield new Pattern('plankton');
+        yield new Pattern('pliers');
+        yield new Pattern('pokemon');
+        yield new Pattern('police');
+        yield new Pattern('polish');
+        yield new Pattern('portuguese');
+        yield new Pattern('proceedings');
+        yield new Pattern('progress');
+        yield new Pattern('rabies');
+        yield new Pattern('rain');
+        yield new Pattern('research');
+        yield new Pattern('rhinoceros');
+        yield new Pattern('rice');
+        yield new Pattern('salmon');
+        yield new Pattern('sand');
+        yield new Pattern('sarawakese');
+        yield new Pattern('scissors');
+        yield new Pattern('sea[- ]bass');
+        yield new Pattern('series');
+        yield new Pattern('shavese');
+        yield new Pattern('shears');
+        yield new Pattern('sheep');
+        yield new Pattern('siemens');
+        yield new Pattern('silk');
+        yield new Pattern('sms');
+        yield new Pattern('soap');
+        yield new Pattern('social media');
+        yield new Pattern('spam');
+        yield new Pattern('species');
+        yield new Pattern('staff');
+        yield new Pattern('sugar');
+        yield new Pattern('swine');
+        yield new Pattern('talent');
+        yield new Pattern('toothpaste');
+        yield new Pattern('traffic');
+        yield new Pattern('travel');
+        yield new Pattern('trousers');
+        yield new Pattern('trout');
+        yield new Pattern('tuna');
+        yield new Pattern('us');
+        yield new Pattern('vermontese');
+        yield new Pattern('vinegar');
+        yield new Pattern('weather');
+        yield new Pattern('wenchowese');
+        yield new Pattern('wheat');
+        yield new Pattern('whiting');
+        yield new Pattern('wildebeest');
+        yield new Pattern('wood');
+        yield new Pattern('wool');
+        yield new Pattern('yengeese');
+    }
+}

+ 49 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Inflectible.php

@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\French;
+
+use Doctrine\Inflector\Rules\Pattern;
+use Doctrine\Inflector\Rules\Substitution;
+use Doctrine\Inflector\Rules\Transformation;
+use Doctrine\Inflector\Rules\Word;
+
+class Inflectible
+{
+    /**
+     * @return Transformation[]
+     */
+    public static function getSingular() : iterable
+    {
+        yield new Transformation(new Pattern('/(b|cor|ém|gemm|soupir|trav|vant|vitr)aux$/'), '\1ail');
+        yield new Transformation(new Pattern('/ails$/'), 'ail');
+        yield new Transformation(new Pattern('/(journ|chev)aux$/'), '\1al');
+        yield new Transformation(new Pattern('/(bijou|caillou|chou|genou|hibou|joujou|pou|au|eu|eau)x$/'), '\1');
+        yield new Transformation(new Pattern('/s$/'), '');
+    }
+
+    /**
+     * @return Transformation[]
+     */
+    public static function getPlural() : iterable
+    {
+        yield new Transformation(new Pattern('/(s|x|z)$/'), '\1');
+        yield new Transformation(new Pattern('/(b|cor|ém|gemm|soupir|trav|vant|vitr)ail$/'), '\1aux');
+        yield new Transformation(new Pattern('/ail$/'), 'ails');
+        yield new Transformation(new Pattern('/al$/'), 'aux');
+        yield new Transformation(new Pattern('/(bleu|émeu|landau|lieu|pneu|sarrau)$/'), '\1s');
+        yield new Transformation(new Pattern('/(bijou|caillou|chou|genou|hibou|joujou|pou|au|eu|eau)$/'), '\1x');
+        yield new Transformation(new Pattern('/$/'), 's');
+    }
+
+    /**
+     * @return Substitution[]
+     */
+    public static function getIrregular() : iterable
+    {
+        yield new Substitution(new Word('monsieur'), new Word('messieurs'));
+        yield new Substitution(new Word('madame'), new Word('mesdames'));
+        yield new Substitution(new Word('mademoiselle'), new Word('mesdemoiselles'));
+    }
+}

+ 21 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/InflectorFactory.php

@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\French;
+
+use Doctrine\Inflector\GenericLanguageInflectorFactory;
+use Doctrine\Inflector\Rules\Ruleset;
+
+final class InflectorFactory extends GenericLanguageInflectorFactory
+{
+    protected function getSingularRuleset() : Ruleset
+    {
+        return Rules::getSingularRuleset();
+    }
+
+    protected function getPluralRuleset() : Ruleset
+    {
+        return Rules::getPluralRuleset();
+    }
+}

+ 31 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Rules.php

@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\French;
+
+use Doctrine\Inflector\Rules\Patterns;
+use Doctrine\Inflector\Rules\Ruleset;
+use Doctrine\Inflector\Rules\Substitutions;
+use Doctrine\Inflector\Rules\Transformations;
+
+final class Rules
+{
+    public static function getSingularRuleset() : Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getSingular()),
+            new Patterns(...Uninflected::getSingular()),
+            (new Substitutions(...Inflectible::getIrregular()))->getFlippedSubstitutions()
+        );
+    }
+
+    public static function getPluralRuleset() : Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getPlural()),
+            new Patterns(...Uninflected::getPlural()),
+            new Substitutions(...Inflectible::getIrregular())
+        );
+    }
+}

+ 34 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Uninflected.php

@@ -0,0 +1,34 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\French;
+
+use Doctrine\Inflector\Rules\Pattern;
+
+final class Uninflected
+{
+    /**
+     * @return Pattern[]
+     */
+    public static function getSingular() : iterable
+    {
+        yield from self::getDefault();
+    }
+
+    /**
+     * @return Pattern[]
+     */
+    public static function getPlural() : iterable
+    {
+        yield from self::getDefault();
+    }
+
+    /**
+     * @return Pattern[]
+     */
+    private static function getDefault() : iterable
+    {
+        yield new Pattern('');
+    }
+}

+ 40 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Inflectible.php

@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\NorwegianBokmal;
+
+use Doctrine\Inflector\Rules\Pattern;
+use Doctrine\Inflector\Rules\Substitution;
+use Doctrine\Inflector\Rules\Transformation;
+use Doctrine\Inflector\Rules\Word;
+
+class Inflectible
+{
+    /**
+     * @return Transformation[]
+     */
+    public static function getSingular() : iterable
+    {
+        yield new Transformation(new Pattern('/re$/i'), 'r');
+        yield new Transformation(new Pattern('/er$/i'), '');
+    }
+
+    /**
+     * @return Transformation[]
+     */
+    public static function getPlural() : iterable
+    {
+        yield new Transformation(new Pattern('/e$/i'), 'er');
+        yield new Transformation(new Pattern('/r$/i'), 're');
+        yield new Transformation(new Pattern('/$/'), 'er');
+    }
+
+    /**
+     * @return Substitution[]
+     */
+    public static function getIrregular() : iterable
+    {
+        yield new Substitution(new Word('konto'), new Word('konti'));
+    }
+}

+ 21 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/InflectorFactory.php

@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\NorwegianBokmal;
+
+use Doctrine\Inflector\GenericLanguageInflectorFactory;
+use Doctrine\Inflector\Rules\Ruleset;
+
+final class InflectorFactory extends GenericLanguageInflectorFactory
+{
+    protected function getSingularRuleset() : Ruleset
+    {
+        return Rules::getSingularRuleset();
+    }
+
+    protected function getPluralRuleset() : Ruleset
+    {
+        return Rules::getPluralRuleset();
+    }
+}

+ 31 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Rules.php

@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\NorwegianBokmal;
+
+use Doctrine\Inflector\Rules\Patterns;
+use Doctrine\Inflector\Rules\Ruleset;
+use Doctrine\Inflector\Rules\Substitutions;
+use Doctrine\Inflector\Rules\Transformations;
+
+final class Rules
+{
+    public static function getSingularRuleset() : Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getSingular()),
+            new Patterns(...Uninflected::getSingular()),
+            (new Substitutions(...Inflectible::getIrregular()))->getFlippedSubstitutions()
+        );
+    }
+
+    public static function getPluralRuleset() : Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getPlural()),
+            new Patterns(...Uninflected::getPlural()),
+            new Substitutions(...Inflectible::getIrregular())
+        );
+    }
+}

+ 36 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Uninflected.php

@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\NorwegianBokmal;
+
+use Doctrine\Inflector\Rules\Pattern;
+
+final class Uninflected
+{
+    /**
+     * @return Pattern[]
+     */
+    public static function getSingular() : iterable
+    {
+        yield from self::getDefault();
+    }
+
+    /**
+     * @return Pattern[]
+     */
+    public static function getPlural() : iterable
+    {
+        yield from self::getDefault();
+    }
+
+    /**
+     * @return Pattern[]
+     */
+    private static function getDefault() : iterable
+    {
+        yield new Pattern('barn');
+        yield new Pattern('fjell');
+        yield new Pattern('hus');
+    }
+}

+ 42 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Pattern.php

@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules;
+
+use function preg_match;
+
+final class Pattern
+{
+    /** @var string */
+    private $pattern;
+
+    /** @var string */
+    private $regex;
+
+    public function __construct(string $pattern)
+    {
+        $this->pattern = $pattern;
+
+        if (isset($this->pattern[0]) && $this->pattern[0] === '/') {
+            $this->regex = $this->pattern;
+        } else {
+            $this->regex = '/' . $this->pattern . '/i';
+        }
+    }
+
+    public function getPattern() : string
+    {
+        return $this->pattern;
+    }
+
+    public function getRegex() : string
+    {
+        return $this->regex;
+    }
+
+    public function matches(string $word) : bool
+    {
+        return preg_match($this->getRegex(), $word) === 1;
+    }
+}

+ 34 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Patterns.php

@@ -0,0 +1,34 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules;
+
+use function array_map;
+use function implode;
+use function preg_match;
+
+class Patterns
+{
+    /** @var Pattern[] */
+    private $patterns;
+
+    /** @var string */
+    private $regex;
+
+    public function __construct(Pattern ...$patterns)
+    {
+        $this->patterns = $patterns;
+
+        $patterns = array_map(static function (Pattern $pattern) : string {
+            return $pattern->getPattern();
+        }, $this->patterns);
+
+        $this->regex = '/^(?:' . implode('|', $patterns) . ')$/i';
+    }
+
+    public function matches(string $word) : bool
+    {
+        return preg_match($this->regex, $word, $regs) === 1;
+    }
+}

+ 104 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Inflectible.php

@@ -0,0 +1,104 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\Portuguese;
+
+use Doctrine\Inflector\Rules\Pattern;
+use Doctrine\Inflector\Rules\Substitution;
+use Doctrine\Inflector\Rules\Transformation;
+use Doctrine\Inflector\Rules\Word;
+
+class Inflectible
+{
+    /**
+     * @return Transformation[]
+     */
+    public static function getSingular() : iterable
+    {
+        yield new Transformation(new Pattern('/^(g|)ases$/i'), '\1ás');
+        yield new Transformation(new Pattern('/(japon|escoc|ingl|dinamarqu|fregu|portugu)eses$/i'), '\1ês');
+        yield new Transformation(new Pattern('/(ae|ao|oe)s$/'), 'ao');
+        yield new Transformation(new Pattern('/(ãe|ão|õe)s$/'), 'ão');
+        yield new Transformation(new Pattern('/^(.*[^s]s)es$/i'), '\1');
+        yield new Transformation(new Pattern('/sses$/i'), 'sse');
+        yield new Transformation(new Pattern('/ns$/i'), 'm');
+        yield new Transformation(new Pattern('/(r|t|f|v)is$/i'), '\1il');
+        yield new Transformation(new Pattern('/uis$/i'), 'ul');
+        yield new Transformation(new Pattern('/ois$/i'), 'ol');
+        yield new Transformation(new Pattern('/eis$/i'), 'ei');
+        yield new Transformation(new Pattern('/éis$/i'), 'el');
+        yield new Transformation(new Pattern('/([^p])ais$/i'), '\1al');
+        yield new Transformation(new Pattern('/(r|z)es$/i'), '\1');
+        yield new Transformation(new Pattern('/^(á|gá)s$/i'), '\1s');
+        yield new Transformation(new Pattern('/([^ê])s$/i'), '\1');
+    }
+
+    /**
+     * @return Transformation[]
+     */
+    public static function getPlural() : iterable
+    {
+        yield new Transformation(new Pattern('/^(alem|c|p)ao$/i'), '\1aes');
+        yield new Transformation(new Pattern('/^(irm|m)ao$/i'), '\1aos');
+        yield new Transformation(new Pattern('/ao$/i'), 'oes');
+        yield new Transformation(new Pattern('/^(alem|c|p)ão$/i'), '\1ães');
+        yield new Transformation(new Pattern('/^(irm|m)ão$/i'), '\1ãos');
+        yield new Transformation(new Pattern('/ão$/i'), 'ões');
+        yield new Transformation(new Pattern('/^(|g)ás$/i'), '\1ases');
+        yield new Transformation(new Pattern('/^(japon|escoc|ingl|dinamarqu|fregu|portugu)ês$/i'), '\1eses');
+        yield new Transformation(new Pattern('/m$/i'), 'ns');
+        yield new Transformation(new Pattern('/([^aeou])il$/i'), '\1is');
+        yield new Transformation(new Pattern('/ul$/i'), 'uis');
+        yield new Transformation(new Pattern('/ol$/i'), 'ois');
+        yield new Transformation(new Pattern('/el$/i'), 'eis');
+        yield new Transformation(new Pattern('/al$/i'), 'ais');
+        yield new Transformation(new Pattern('/(z|r)$/i'), '\1es');
+        yield new Transformation(new Pattern('/(s)$/i'), '\1');
+        yield new Transformation(new Pattern('/$/'), 's');
+    }
+
+    /**
+     * @return Substitution[]
+     */
+    public static function getIrregular() : iterable
+    {
+        yield new Substitution(new Word('abdomen'), new Word('abdomens'));
+        yield new Substitution(new Word('alemão'), new Word('alemães'));
+        yield new Substitution(new Word('artesã'), new Word('artesãos'));
+        yield new Substitution(new Word('álcool'), new Word('álcoois'));
+        yield new Substitution(new Word('árvore'), new Word('árvores'));
+        yield new Substitution(new Word('bencão'), new Word('bencãos'));
+        yield new Substitution(new Word('cão'), new Word('cães'));
+        yield new Substitution(new Word('campus'), new Word('campi'));
+        yield new Substitution(new Word('cadáver'), new Word('cadáveres'));
+        yield new Substitution(new Word('capelão'), new Word('capelães'));
+        yield new Substitution(new Word('capitão'), new Word('capitães'));
+        yield new Substitution(new Word('chão'), new Word('chãos'));
+        yield new Substitution(new Word('charlatão'), new Word('charlatães'));
+        yield new Substitution(new Word('cidadão'), new Word('cidadãos'));
+        yield new Substitution(new Word('consul'), new Word('consules'));
+        yield new Substitution(new Word('cristão'), new Word('cristãos'));
+        yield new Substitution(new Word('difícil'), new Word('difíceis'));
+        yield new Substitution(new Word('email'), new Word('emails'));
+        yield new Substitution(new Word('escrivão'), new Word('escrivães'));
+        yield new Substitution(new Word('fóssil'), new Word('fósseis'));
+        yield new Substitution(new Word('gás'), new Word('gases'));
+        yield new Substitution(new Word('germens'), new Word('germen'));
+        yield new Substitution(new Word('grão'), new Word('grãos'));
+        yield new Substitution(new Word('hífen'), new Word('hífens'));
+        yield new Substitution(new Word('irmão'), new Word('irmãos'));
+        yield new Substitution(new Word('liquens'), new Word('liquen'));
+        yield new Substitution(new Word('mal'), new Word('males'));
+        yield new Substitution(new Word('mão'), new Word('mãos'));
+        yield new Substitution(new Word('orfão'), new Word('orfãos'));
+        yield new Substitution(new Word('país'), new Word('países'));
+        yield new Substitution(new Word('pai'), new Word('pais'));
+        yield new Substitution(new Word('pão'), new Word('pães'));
+        yield new Substitution(new Word('projétil'), new Word('projéteis'));
+        yield new Substitution(new Word('réptil'), new Word('répteis'));
+        yield new Substitution(new Word('sacristão'), new Word('sacristães'));
+        yield new Substitution(new Word('sotão'), new Word('sotãos'));
+        yield new Substitution(new Word('tabelião'), new Word('tabeliães'));
+    }
+}

+ 21 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/InflectorFactory.php

@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\Portuguese;
+
+use Doctrine\Inflector\GenericLanguageInflectorFactory;
+use Doctrine\Inflector\Rules\Ruleset;
+
+final class InflectorFactory extends GenericLanguageInflectorFactory
+{
+    protected function getSingularRuleset() : Ruleset
+    {
+        return Rules::getSingularRuleset();
+    }
+
+    protected function getPluralRuleset() : Ruleset
+    {
+        return Rules::getPluralRuleset();
+    }
+}

+ 31 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Rules.php

@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\Portuguese;
+
+use Doctrine\Inflector\Rules\Patterns;
+use Doctrine\Inflector\Rules\Ruleset;
+use Doctrine\Inflector\Rules\Substitutions;
+use Doctrine\Inflector\Rules\Transformations;
+
+final class Rules
+{
+    public static function getSingularRuleset() : Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getSingular()),
+            new Patterns(...Uninflected::getSingular()),
+            (new Substitutions(...Inflectible::getIrregular()))->getFlippedSubstitutions()
+        );
+    }
+
+    public static function getPluralRuleset() : Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getPlural()),
+            new Patterns(...Uninflected::getPlural()),
+            new Substitutions(...Inflectible::getIrregular())
+        );
+    }
+}

+ 38 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Uninflected.php

@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\Portuguese;
+
+use Doctrine\Inflector\Rules\Pattern;
+
+final class Uninflected
+{
+    /**
+     * @return Pattern[]
+     */
+    public static function getSingular() : iterable
+    {
+        yield from self::getDefault();
+    }
+
+    /**
+     * @return Pattern[]
+     */
+    public static function getPlural() : iterable
+    {
+        yield from self::getDefault();
+    }
+
+    /**
+     * @return Pattern[]
+     */
+    private static function getDefault() : iterable
+    {
+        yield new Pattern('tórax');
+        yield new Pattern('tênis');
+        yield new Pattern('ônibus');
+        yield new Pattern('lápis');
+        yield new Pattern('fênix');
+    }
+}

+ 39 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Ruleset.php

@@ -0,0 +1,39 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules;
+
+class Ruleset
+{
+    /** @var Transformations */
+    private $regular;
+
+    /** @var Patterns */
+    private $uninflected;
+
+    /** @var Substitutions */
+    private $irregular;
+
+    public function __construct(Transformations $regular, Patterns $uninflected, Substitutions $irregular)
+    {
+        $this->regular     = $regular;
+        $this->uninflected = $uninflected;
+        $this->irregular   = $irregular;
+    }
+
+    public function getRegular() : Transformations
+    {
+        return $this->regular;
+    }
+
+    public function getUninflected() : Patterns
+    {
+        return $this->uninflected;
+    }
+
+    public function getIrregular() : Substitutions
+    {
+        return $this->irregular;
+    }
+}

+ 53 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Inflectible.php

@@ -0,0 +1,53 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\Spanish;
+
+use Doctrine\Inflector\Rules\Pattern;
+use Doctrine\Inflector\Rules\Substitution;
+use Doctrine\Inflector\Rules\Transformation;
+use Doctrine\Inflector\Rules\Word;
+
+class Inflectible
+{
+    /**
+     * @return Transformation[]
+     */
+    public static function getSingular() : iterable
+    {
+        yield new Transformation(new Pattern('/ereses$/'), 'erés');
+        yield new Transformation(new Pattern('/iones$/'), 'ión');
+        yield new Transformation(new Pattern('/ces$/'), 'z');
+        yield new Transformation(new Pattern('/es$/'), '');
+        yield new Transformation(new Pattern('/s$/'), '');
+    }
+
+    /**
+     * @return Transformation[]
+     */
+    public static function getPlural() : iterable
+    {
+        yield new Transformation(new Pattern('/ú([sn])$/i'), 'u\1es');
+        yield new Transformation(new Pattern('/ó([sn])$/i'), 'o\1es');
+        yield new Transformation(new Pattern('/í([sn])$/i'), 'i\1es');
+        yield new Transformation(new Pattern('/é([sn])$/i'), 'e\1es');
+        yield new Transformation(new Pattern('/á([sn])$/i'), 'a\1es');
+        yield new Transformation(new Pattern('/z$/i'), 'ces');
+        yield new Transformation(new Pattern('/([aeiou]s)$/i'), '\1');
+        yield new Transformation(new Pattern('/([^aeéiou])$/i'), '\1es');
+        yield new Transformation(new Pattern('/$/'), 's');
+    }
+
+    /**
+     * @return Substitution[]
+     */
+    public static function getIrregular() : iterable
+    {
+        yield new Substitution(new Word('el'), new Word('los'));
+        yield new Substitution(new Word('papá'), new Word('papás'));
+        yield new Substitution(new Word('mamá'), new Word('mamás'));
+        yield new Substitution(new Word('sofá'), new Word('sofás'));
+        yield new Substitution(new Word('mes'), new Word('meses'));
+    }
+}

+ 21 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/InflectorFactory.php

@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\Spanish;
+
+use Doctrine\Inflector\GenericLanguageInflectorFactory;
+use Doctrine\Inflector\Rules\Ruleset;
+
+final class InflectorFactory extends GenericLanguageInflectorFactory
+{
+    protected function getSingularRuleset() : Ruleset
+    {
+        return Rules::getSingularRuleset();
+    }
+
+    protected function getPluralRuleset() : Ruleset
+    {
+        return Rules::getPluralRuleset();
+    }
+}

+ 31 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Rules.php

@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\Spanish;
+
+use Doctrine\Inflector\Rules\Patterns;
+use Doctrine\Inflector\Rules\Ruleset;
+use Doctrine\Inflector\Rules\Substitutions;
+use Doctrine\Inflector\Rules\Transformations;
+
+final class Rules
+{
+    public static function getSingularRuleset() : Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getSingular()),
+            new Patterns(...Uninflected::getSingular()),
+            (new Substitutions(...Inflectible::getIrregular()))->getFlippedSubstitutions()
+        );
+    }
+
+    public static function getPluralRuleset() : Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getPlural()),
+            new Patterns(...Uninflected::getPlural()),
+            new Substitutions(...Inflectible::getIrregular())
+        );
+    }
+}

+ 36 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Uninflected.php

@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\Spanish;
+
+use Doctrine\Inflector\Rules\Pattern;
+
+final class Uninflected
+{
+    /**
+     * @return Pattern[]
+     */
+    public static function getSingular() : iterable
+    {
+        yield from self::getDefault();
+    }
+
+    /**
+     * @return Pattern[]
+     */
+    public static function getPlural() : iterable
+    {
+        yield from self::getDefault();
+    }
+
+    /**
+     * @return Pattern[]
+     */
+    private static function getDefault() : iterable
+    {
+        yield new Pattern('lunes');
+        yield new Pattern('rompecabezas');
+        yield new Pattern('crisis');
+    }
+}

+ 30 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Substitution.php

@@ -0,0 +1,30 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules;
+
+final class Substitution
+{
+    /** @var Word */
+    private $from;
+
+    /** @var Word */
+    private $to;
+
+    public function __construct(Word $from, Word $to)
+    {
+        $this->from = $from;
+        $this->to   = $to;
+    }
+
+    public function getFrom() : Word
+    {
+        return $this->from;
+    }
+
+    public function getTo() : Word
+    {
+        return $this->to;
+    }
+}

+ 56 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Substitutions.php

@@ -0,0 +1,56 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules;
+
+use Doctrine\Inflector\WordInflector;
+use function strtolower;
+use function strtoupper;
+use function substr;
+
+class Substitutions implements WordInflector
+{
+    /** @var Substitution[] */
+    private $substitutions;
+
+    public function __construct(Substitution ...$substitutions)
+    {
+        foreach ($substitutions as $substitution) {
+            $this->substitutions[$substitution->getFrom()->getWord()] = $substitution;
+        }
+    }
+
+    public function getFlippedSubstitutions() : Substitutions
+    {
+        $substitutions = [];
+
+        foreach ($this->substitutions as $substitution) {
+            $substitutions[] = new Substitution(
+                $substitution->getTo(),
+                $substitution->getFrom()
+            );
+        }
+
+        return new Substitutions(...$substitutions);
+    }
+
+    public function inflect(string $word) : string
+    {
+        $lowerWord = strtolower($word);
+
+        if (isset($this->substitutions[$lowerWord])) {
+            $firstLetterUppercase = $lowerWord[0] !== $word[0];
+
+            $toWord = $this->substitutions[$lowerWord]->getTo()->getWord();
+
+            if ($firstLetterUppercase) {
+                return strtoupper($toWord[0]) . substr($toWord, 1);
+            }
+
+            return $toWord;
+        }
+
+        return $word;
+    }
+}

+ 38 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformation.php

@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules;
+
+use Doctrine\Inflector\WordInflector;
+use function preg_replace;
+
+final class Transformation implements WordInflector
+{
+    /** @var Pattern */
+    private $pattern;
+
+    /** @var string */
+    private $replacement;
+
+    public function __construct(Pattern $pattern, string $replacement)
+    {
+        $this->pattern     = $pattern;
+        $this->replacement = $replacement;
+    }
+
+    public function getPattern() : Pattern
+    {
+        return $this->pattern;
+    }
+
+    public function getReplacement() : string
+    {
+        return $this->replacement;
+    }
+
+    public function inflect(string $word) : string
+    {
+        return (string) preg_replace($this->pattern->getRegex(), $this->replacement, $word);
+    }
+}

+ 29 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformations.php

@@ -0,0 +1,29 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules;
+
+use Doctrine\Inflector\WordInflector;
+
+class Transformations implements WordInflector
+{
+    /** @var Transformation[] */
+    private $transformations;
+
+    public function __construct(Transformation ...$transformations)
+    {
+        $this->transformations = $transformations;
+    }
+
+    public function inflect(string $word) : string
+    {
+        foreach ($this->transformations as $transformation) {
+            if ($transformation->getPattern()->matches($word)) {
+                return $transformation->inflect($word);
+            }
+        }
+
+        return $word;
+    }
+}

+ 40 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Inflectible.php

@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\Turkish;
+
+use Doctrine\Inflector\Rules\Pattern;
+use Doctrine\Inflector\Rules\Substitution;
+use Doctrine\Inflector\Rules\Transformation;
+use Doctrine\Inflector\Rules\Word;
+
+class Inflectible
+{
+    /**
+     * @return Transformation[]
+     */
+    public static function getSingular() : iterable
+    {
+        yield new Transformation(new Pattern('/l[ae]r$/i'), '');
+    }
+
+    /**
+     * @return Transformation[]
+     */
+    public static function getPlural() : iterable
+    {
+        yield new Transformation(new Pattern('/([eöiü][^aoıueöiü]{0,6})$/u'), '\1ler');
+        yield new Transformation(new Pattern('/([aoıu][^aoıueöiü]{0,6})$/u'), '\1lar');
+    }
+
+    /**
+     * @return Substitution[]
+     */
+    public static function getIrregular() : iterable
+    {
+        yield new Substitution(new Word('ben'), new Word('biz'));
+        yield new Substitution(new Word('sen'), new Word('siz'));
+        yield new Substitution(new Word('o'), new Word('onlar'));
+    }
+}

+ 21 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/InflectorFactory.php

@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\Turkish;
+
+use Doctrine\Inflector\GenericLanguageInflectorFactory;
+use Doctrine\Inflector\Rules\Ruleset;
+
+final class InflectorFactory extends GenericLanguageInflectorFactory
+{
+    protected function getSingularRuleset() : Ruleset
+    {
+        return Rules::getSingularRuleset();
+    }
+
+    protected function getPluralRuleset() : Ruleset
+    {
+        return Rules::getPluralRuleset();
+    }
+}

+ 31 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Rules.php

@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\Turkish;
+
+use Doctrine\Inflector\Rules\Patterns;
+use Doctrine\Inflector\Rules\Ruleset;
+use Doctrine\Inflector\Rules\Substitutions;
+use Doctrine\Inflector\Rules\Transformations;
+
+final class Rules
+{
+    public static function getSingularRuleset() : Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getSingular()),
+            new Patterns(...Uninflected::getSingular()),
+            (new Substitutions(...Inflectible::getIrregular()))->getFlippedSubstitutions()
+        );
+    }
+
+    public static function getPluralRuleset() : Ruleset
+    {
+        return new Ruleset(
+            new Transformations(...Inflectible::getPlural()),
+            new Patterns(...Uninflected::getPlural()),
+            new Substitutions(...Inflectible::getIrregular())
+        );
+    }
+}

+ 36 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Uninflected.php

@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules\Turkish;
+
+use Doctrine\Inflector\Rules\Pattern;
+
+final class Uninflected
+{
+    /**
+     * @return Pattern[]
+     */
+    public static function getSingular() : iterable
+    {
+        yield from self::getDefault();
+    }
+
+    /**
+     * @return Pattern[]
+     */
+    public static function getPlural() : iterable
+    {
+        yield from self::getDefault();
+    }
+
+    /**
+     * @return Pattern[]
+     */
+    private static function getDefault() : iterable
+    {
+        yield new Pattern('lunes');
+        yield new Pattern('rompecabezas');
+        yield new Pattern('crisis');
+    }
+}

+ 21 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Word.php

@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector\Rules;
+
+class Word
+{
+    /** @var string */
+    private $word;
+
+    public function __construct(string $word)
+    {
+        $this->word = $word;
+    }
+
+    public function getWord() : string
+    {
+        return $this->word;
+    }
+}

+ 55 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/RulesetInflector.php

@@ -0,0 +1,55 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector;
+
+use Doctrine\Inflector\Rules\Ruleset;
+use function array_merge;
+
+/**
+ * Inflects based on multiple rulesets.
+ *
+ * Rules:
+ * - If the word matches any uninflected word pattern, it is not inflected
+ * - The first ruleset that returns a different value for an irregular word wins
+ * - The first ruleset that returns a different value for a regular word wins
+ * - If none of the above match, the word is left as-is
+ */
+class RulesetInflector implements WordInflector
+{
+    /** @var Ruleset[] */
+    private $rulesets;
+
+    public function __construct(Ruleset $ruleset, Ruleset ...$rulesets)
+    {
+        $this->rulesets = array_merge([$ruleset], $rulesets);
+    }
+
+    public function inflect(string $word) : string
+    {
+        if ($word === '') {
+            return '';
+        }
+
+        foreach ($this->rulesets as $ruleset) {
+            if ($ruleset->getUninflected()->matches($word)) {
+                return $word;
+            }
+
+            $inflected = $ruleset->getIrregular()->inflect($word);
+
+            if ($inflected !== $word) {
+                return $inflected;
+            }
+
+            $inflected = $ruleset->getRegular()->inflect($word);
+
+            if ($inflected !== $word) {
+                return $inflected;
+            }
+        }
+
+        return $word;
+    }
+}

+ 10 - 0
vendor/doctrine/inflector/lib/Doctrine/Inflector/WordInflector.php

@@ -0,0 +1,10 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Inflector;
+
+interface WordInflector
+{
+    public function inflect(string $word) : string;
+}

+ 13 - 0
vendor/doctrine/inflector/phpstan.neon.dist

@@ -0,0 +1,13 @@
+includes:
+    - vendor/phpstan/phpstan-phpunit/extension.neon
+    - vendor/phpstan/phpstan-phpunit/rules.neon
+    - vendor/phpstan/phpstan-strict-rules/rules.neon
+
+parameters:
+    level: 7
+    paths:
+      - lib
+      - tests
+
+    excludes_analyse:
+        - %rootDir%/../../../tests/Doctrine/Tests/Common/*

+ 0 - 0
vendor/doctrine/instantiator/.doctrine-project.json


Some files were not shown because too many files changed in this diff