From 094e5ae472176f5418c0e9f01a0ed73cc94c9997 Mon Sep 17 00:00:00 2001 From: Roberto Butti Date: Sun, 23 Nov 2025 21:55:20 +0100 Subject: [PATCH 1/3] php 8.5 tests --- .github/workflows/run-tests.yml | 4 ++-- CHANGELOG.md | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 5ea2be3..f351370 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,10 +9,10 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest, windows-latest] - php: [8.1, 8.2, 8.3, 8.4] + php: [8.1, 8.2, 8.3, 8.4, 8.5] exclude: - os: windows-latest - php: [8.1, 8.2, 8.4] + php: [8.1, 8.2, 8.3, 8.5] name: P${{ matrix.php }} - ${{ matrix.os }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f41e68..b0218b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.2.0 - 2025-11-23 +- Welcome to PHP 8.5 +- Upgrading to PHPstan new rules (offsetAccess) + ## 1.1.4 - 2025-04-25 - Adding `fmean()` method for computing the arithmetic mean with float numbers. From a5b5c5935748f6609cfbf6844d0e8cac470dce33 Mon Sep 17 00:00:00 2001 From: Roberto Butti Date: Thu, 19 Feb 2026 23:26:05 +0100 Subject: [PATCH 2/3] from pestphp to phpunit , from pnit to phpcsfixer --- .github/workflows/dependabot-auto-merge.yml | 5 +- .github/workflows/run-tests.yml | 4 +- .github/workflows/static-code-analysis.yml | 2 +- .php-cs-fixer.dist.php | 13 + composer.json | 17 +- phpstan.neon | 4 + phpunit.xml.dist | 4 +- pint.json | 3 - tests/ArchTest.php | 24 - tests/Datasets/Input.php | 10 - tests/FreqTest.php | 252 +++---- tests/FrequenciesTest.php | 139 ++-- tests/MathTest.php | 19 +- tests/NormalDistTest.php | 139 ++-- tests/Pest.php | 1 - tests/StatDatasetTest.php | 123 ++- tests/StatFromCsvTest.php | 70 +- tests/StatTest.php | 796 ++++++++++---------- tests/StatisticTest.php | 402 +++++----- 19 files changed, 976 insertions(+), 1051 deletions(-) create mode 100644 .php-cs-fixer.dist.php delete mode 100644 pint.json delete mode 100644 tests/ArchTest.php delete mode 100644 tests/Datasets/Input.php delete mode 100644 tests/Pest.php diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 1a13177..86849c7 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -10,20 +10,19 @@ jobs: runs-on: ubuntu-latest if: ${{ github.actor == 'dependabot[bot]' }} steps: - - name: Dependabot metadata id: metadata uses: dependabot/fetch-metadata@v2.4.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - + - name: Auto-merge Dependabot PRs for semver-minor updates if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} run: gh pr merge --auto --merge "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - + - name: Auto-merge Dependabot PRs for semver-patch updates if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} run: gh pr merge --auto --merge "$PR_URL" diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index f351370..be27980 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,10 +9,10 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest, windows-latest] - php: [8.1, 8.2, 8.3, 8.4, 8.5] + php: [8.2, 8.3, 8.4, 8.5] exclude: - os: windows-latest - php: [8.1, 8.2, 8.3, 8.5] + php: [8.2, 8.3, 8.5] name: P${{ matrix.php }} - ${{ matrix.os }} diff --git a/.github/workflows/static-code-analysis.yml b/.github/workflows/static-code-analysis.yml index 9c03042..0cf0078 100644 --- a/.github/workflows/static-code-analysis.yml +++ b/.github/workflows/static-code-analysis.yml @@ -9,7 +9,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest] - php: [8.3] + php: [8.4] stability: [prefer-stable] name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..dc09a5c --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,13 @@ +in([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]); + +return (new PhpCsFixer\Config()) + ->setRules([ + '@PER-CS' => true, + ]) + ->setFinder($finder); diff --git a/composer.json b/composer.json index ff19a54..bb56a58 100644 --- a/composer.json +++ b/composer.json @@ -15,12 +15,13 @@ } ], "require": { - "php": "^8.1|^8.2|^8.3|^8.4" + "php": "^8.2|^8.3|^8.4" }, "require-dev": { - "laravel/pint": "^1.18", - "pestphp/pest": "^2.0", + "friendsofphp/php-cs-fixer": "^3.65", "phpstan/phpstan": "^2", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^11.0", "rector/rector": "^2" }, "autoload": { @@ -34,9 +35,9 @@ } }, "scripts": { - "format": "vendor/bin/pint", - "test": "vendor/bin/pest", - "test-coverage": "vendor/bin/pest --coverage", + "format": "vendor/bin/php-cs-fixer fix", + "test": "vendor/bin/phpunit", + "test-coverage": "vendor/bin/phpunit --coverage-text", "static-code": "vendor/bin/phpstan analyse -c phpstan.neon", "rector-dry-run": "rector process --dry-run", "rector": "rector process", @@ -48,9 +49,7 @@ }, "config": { "sort-packages": true, - "allow-plugins": { - "pestphp/pest-plugin": true - } + "allow-plugins": {} }, "minimum-stability": "dev", "prefer-stable": true diff --git a/phpstan.neon b/phpstan.neon index 7d86171..b21a638 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,9 @@ +includes: + - vendor/phpstan/phpstan-phpunit/extension.neon + parameters: level: 8 treatPhpDocTypesAsCertain: false paths: - src + - tests diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 1c22a9c..a4ff289 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,7 +1,7 @@ - tests + tests diff --git a/pint.json b/pint.json deleted file mode 100644 index f9ec356..0000000 --- a/pint.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "preset": "per" -} diff --git a/tests/ArchTest.php b/tests/ArchTest.php deleted file mode 100644 index 64ea6de..0000000 --- a/tests/ArchTest.php +++ /dev/null @@ -1,24 +0,0 @@ -expect(['dd', 'dump', 'echo', 'print_r']) - ->not->toBeUsed(); - -test('to be final') - ->expect('HiFolks\Statistics') - ->classes() - ->not->toBeFinal(); - -test('make') - ->expect('HiFolks\Statistics\Statistics') - ->toHaveMethod('make'); - -test('constructor') - ->expect('HiFolks\Statistics\Statistics') - ->toHaveConstructor(); - -/* -test('strict') - ->expect('HiFolks\Statistics') - ->toUseStrictTypes(); -*/ diff --git a/tests/Datasets/Input.php b/tests/Datasets/Input.php deleted file mode 100644 index 38fdb11..0000000 --- a/tests/Datasets/Input.php +++ /dev/null @@ -1,10 +0,0 @@ -assertEquals([4 => 2, 3 => 1, 1 => 1, 2 => 1], Freq::frequencies([1, 2, 3, 4, 4])); + $this->assertEquals([], Freq::frequencies([])); + + $result = Freq::frequencies(['red', 'blue', 'blue', 'red', 'green', 'red', 'red']); + $this->assertEquals(['red' => 4, 'blue' => 2, 'green' => 1], $result); + $this->assertCount(3, $result); + $this->assertEquals(4, $result['red']); + $this->assertEquals(2, $result['blue']); + $this->assertEquals(1, $result['green']); + + $result = Freq::frequencies([2.1, 2.7, 1.4, 2.45], true); + $this->assertEquals([2 => 3, 1 => 1], $result); + $this->assertCount(2, $result); + } + + public function test_can_calculate_relative_freq_table(): void + { + $this->assertEquals([4 => 40, 3 => 20, 1 => 20, 2 => 20], Freq::relativeFrequencies([1, 2, 3, 4, 4])); + $this->assertEquals([], Freq::relativeFrequencies([])); + + $result = Freq::relativeFrequencies(['red', 'blue', 'blue', 'red', 'green', 'red', 'red'], 2); + $this->assertEquals(['red' => 57.14, 'blue' => 28.57, 'green' => 14.29], $result); + $this->assertCount(3, $result); + $this->assertEquals(57.14, $result['red']); + $this->assertEquals(28.57, $result['blue']); + $this->assertEquals(14.29, $result['green']); + + $result = Freq::relativeFrequencies([2.1, 2.7, 1.4, 2.45], 1); + $this->assertEquals([2 => 75, 1 => 25], $result); + $this->assertCount(2, $result); + } + + public function test_can_calculate_grouped_frequency_table(): void + { + $data = [1, 1, 1, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 12, 12, + 13, 14, 14, 15, 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, ]; + + $table = Freq::frequencyTable($data, 7); + $this->assertCount(6, $table); + $this->assertEquals([1 => 3, 4 => 6, 7 => 10, 10 => 5, 13 => 5, 16 => 9], $table); + $this->assertEquals(count($data), array_sum($table)); + + $table = Freq::frequencyTable($data, 6); + $this->assertCount(6, $table); + $this->assertEquals([1 => 3, 4 => 6, 7 => 10, 10 => 5, 13 => 5, 16 => 9], $table); + $this->assertEquals(count($data), array_sum($table)); + + $table = Freq::frequencyTable($data, 8); + $this->assertCount(6, $table); + $this->assertEquals([1 => 3, 4 => 6, 7 => 10, 10 => 5, 13 => 5, 16 => 9], $table); + $this->assertEquals(count($data), array_sum($table)); + + $table = Freq::frequencyTable($data, 3); + $this->assertCount(3, $table); + $this->assertEquals([1 => 9, 7 => 15, 13 => 14], $table); + $this->assertEquals(count($data), array_sum($table)); + + $table = Freq::frequencyTable($data); + $this->assertCount(18, $table); + $this->assertEquals([1 => 3, 2 => 0, 3 => 0, 4 => 2, 5 => 3, 6 => 1, 7 => 1, 8 => 3, 9 => 6, + 10 => 2, 11 => 1, 12 => 2, 13 => 1, 14 => 2, 15 => 2, 16 => 4, 17 => 3, 18 => 2, ], $table); + } + + public function test_can_calculate_grouped_frequency_table_by_size(): void + { + $data = [1, 1, 1, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 12, 12, + 13, 14, 14, 15, 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, ]; + + $table = Freq::frequencyTableBySize($data, 4); + $this->assertCount(5, $table); + $this->assertEquals([1 => 5, 5 => 8, 9 => 11, 13 => 9, 17 => 5], $table); + $this->assertEquals(count($data), array_sum($table)); + + $table = Freq::frequencyTableBySize($data, 5); + $this->assertCount(4, $table); + $this->assertEquals([1 => 8, 6 => 13, 11 => 8, 16 => 9], $table); + $this->assertEquals(count($data), array_sum($table)); -it('can calculate freq table (static)', function () { - expect( - Freq::frequencies([1, 2, 3, 4, 4]), - )->toMatchArray([4 => 2, 3 => 1, 1 => 1, 2 => 1]); - expect( - Freq::frequencies([]), - )->toMatchArray([]); - $result = Freq::frequencies(['red', 'blue', 'blue', 'red', 'green', 'red', 'red']); - expect( - $result, - )->toMatchArray(['red' => 4, 'blue' => 2, 'green' => 1]); - expect( - $result, - )->toHaveCount(3); - - expect( - $result['red'], - )->toEqual(4); - expect( - $result['blue'], - )->toEqual(2); - expect( - $result['green'], - )->toEqual(1); - - $result = Freq::frequencies([2.1, 2.7, 1.4, 2.45], true); - expect( - $result, - )->toMatchArray([2 => 3, 1 => 1]); - expect( - $result, - )->toHaveCount(2); -}); - -it('can calculate relativefreq table (static)', function () { - expect( - Freq::relativeFrequencies([1, 2, 3, 4, 4]), - )->toMatchArray([4 => 40, 3 => 20, 1 => 20, 2 => 20]); - expect( - Freq::relativeFrequencies([]), - )->toMatchArray([]); - $result = Freq::relativeFrequencies(['red', 'blue', 'blue', 'red', 'green', 'red', 'red'], 2); - expect( - $result, - )->toMatchArray(['red' => 57.14, 'blue' => 28.57, 'green' => 14.29]); - expect( - $result, - )->toHaveCount(3); - - expect( - $result['red'], - )->toEqual(57.14); - expect( - $result['blue'], - )->toEqual(28.57); - expect( - $result['green'], - )->toEqual(14.29); - - $result = Freq::relativeFrequencies([2.1, 2.7, 1.4, 2.45], true); - expect( - $result, - )->toMatchArray([2 => 75, 1 => 25]); - expect( - $result, - )->toHaveCount(2); -}); - -it('can calculate grouped frequency table (static)', function () { - $data = [1, 1, 1, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 12, 12, - 13, 14, 14, 15, 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, ]; - $table = Freq::frequencyTable($data, 7); - expect( - $table, - )->toHaveCount(6); - expect( - $table, - )->toMatchArray([1 => 3, 4 => 6, 7 => 10, 10 => 5, 13 => 5, 16 => 9]); - expect( - array_sum($table), - )->toEqual(count($data)); - - $table = Freq::frequencyTable($data, 6); - expect( - $table, - )->toHaveCount(6); - expect( - $table, - )->toMatchArray([1 => 3, 4 => 6, 7 => 10, 10 => 5, 13 => 5, 16 => 9]); - expect( - array_sum($table), - )->toEqual(count($data)); - - $table = Freq::frequencyTable($data, 8); - expect( - $table, - )->toHaveCount(6); - expect( - $table, - )->toMatchArray([1 => 3, 4 => 6, 7 => 10, 10 => 5, 13 => 5, 16 => 9]); - expect( - array_sum($table), - )->toEqual(count($data)); - - $table = Freq::frequencyTable($data, 3); - expect( - $table, - )->toHaveCount(3); - expect( - $table, - )->toMatchArray([1 => 9, 7 => 15, 13 => 14]); - expect( - array_sum($table), - )->toEqual(count($data)); - - $table = Freq::frequencyTable($data); - expect( - $table, - )->toHaveCount(18); - expect( - $table, - )->toMatchArray([1 => 3, 2 => 0, 3 => 0, 4 => 2, 5 => 3, 6 => 1, 7 => 1, 8 => 3, 9 => 6, - 10 => 2, 11 => 1, 12 => 2, 13 => 1, 14 => 2, 15 => 2, 16 => 4, 17 => 3, 18 => 2, ]); -}); - -it('can calculate grouped frequency table by size (static)', function () { - $data = [1, 1, 1, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 12, 12, - 13, 14, 14, 15, 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, ]; - $table = Freq::frequencyTableBySize($data, 4); - expect( - $table, - )->toHaveCount(5); - expect( - $table, - )->toMatchArray([1 => 5, 5 => 8, 9 => 11, 13 => 9, 17 => 5]); - expect( - array_sum($table), - )->toEqual(count($data)); - - $table = Freq::frequencyTableBySize($data, 5); - expect( - $table, - )->toHaveCount(4); - expect( - $table, - )->toMatchArray([1 => 8, 6 => 13, 11 => 8, 16 => 9]); - expect( - array_sum($table), - )->toEqual(count($data)); - - $table = Freq::frequencyTableBySize($data, 8); - expect( - $table, - )->toHaveCount(3); - expect( - $table, - )->toMatchArray([1 => 13, 9 => 20, 17 => 5]); - expect( - array_sum($table), - )->toEqual(count($data)); -}); + $table = Freq::frequencyTableBySize($data, 8); + $this->assertCount(3, $table); + $this->assertEquals([1 => 13, 9 => 20, 17 => 5], $table); + $this->assertEquals(count($data), array_sum($table)); + } +} diff --git a/tests/FrequenciesTest.php b/tests/FrequenciesTest.php index cd131a8..8cf0adc 100644 --- a/tests/FrequenciesTest.php +++ b/tests/FrequenciesTest.php @@ -1,82 +1,85 @@ frequencies(); - expect($a[92])->toEqual(2); - expect($a)->toHaveCount(11); -}); - -it('can calculate relative frequencies', function () { - $s = Statistics::make( - [3, 4, 3, 1], - ); - $a = $s->relativeFrequencies(); - expect($a[3])->toEqual(50); - expect($a)->toHaveCount(3); - expect($s->originalArray())->toHaveCount(4); -}); +class FrequenciesTest extends TestCase +{ + public function test_can_calculate_frequencies(): void + { + $s = Statistics::make( + [98, 90, 70, 18, 92, 92, 55, 83, 45, 95, 88, 76], + ); + $a = $s->frequencies(); + $this->assertEquals(2, $a[92]); + $this->assertCount(11, $a); + } -it('can calculate cumulative frequencies', function () { - $s = Statistics::make( - [3, 4, 3, 1], - ); - $a = $s->cumulativeFrequencies(); + public function test_can_calculate_relative_frequencies(): void + { + $s = Statistics::make( + [3, 4, 3, 1], + ); + $a = $s->relativeFrequencies(); + $this->assertEquals(50, $a[3]); + $this->assertCount(3, $a); + $this->assertCount(4, $s->originalArray()); + } - expect($a[3])->toEqual(3); - expect($a)->toHaveCount(3); - expect($s->originalArray())->toHaveCount(4); -}); + public function test_can_calculate_cumulative_frequencies(): void + { + $s = Statistics::make( + [3, 4, 3, 1], + ); + $a = $s->cumulativeFrequencies(); + $this->assertEquals(3, $a[3]); + $this->assertCount(3, $a); + $this->assertCount(4, $s->originalArray()); + } -it('can calculate cumulative relative frequencies', function () { - $s = Statistics::make( - [3, 4, 3, 1], - ); - $a = $s->cumulativeRelativeFrequencies(); + public function test_can_calculate_cumulative_relative_frequencies(): void + { + $s = Statistics::make( + [3, 4, 3, 1], + ); + $a = $s->cumulativeRelativeFrequencies(); + $this->assertEquals(75, $a[3]); + $this->assertCount(3, $a); + $this->assertCount(4, $s->originalArray()); + } - expect($a[3])->toEqual(75); - expect($a)->toHaveCount(3); - expect($s->originalArray())->toHaveCount(4); -}); + public function test_can_calculate_first_quartile(): void + { + $s = Statistics::make([3, 4, 3, 1]); + $this->assertEquals(1.5, $s->firstQuartile()); -it('can calculate firstQuartile', function () { - $s = Statistics::make( - [3, 4, 3, 1], - ); - $a = $s->firstQuartile(); - expect($a)->toEqual(1.5); + $s = Statistics::make([3, 4, 3]); + $this->assertEquals(3, $s->firstQuartile()); + } - $s = Statistics::make( - [3, 4, 3], - ); - $a = $s->firstQuartile(); - expect($a)->toEqual(3); + public function test_can_calculate_first_quartile_with_empty_array(): void + { + $s = Statistics::make([]); + $this->expectException(InvalidDataInputException::class); + $s->firstQuartile(); + } - $s = Statistics::make( - [], - ); - expect(fn() => $s->firstQuartile())->toThrow(InvalidDataInputException::class); -}); -it('can calculate thirdQuartile', function () { - $s = Statistics::make( - [3, 4, 3, 1], - ); - $a = $s->thirdQuartile(); - expect($a)->toEqual(3.75); + public function test_can_calculate_third_quartile(): void + { + $s = Statistics::make([3, 4, 3, 1]); + $this->assertEquals(3.75, $s->thirdQuartile()); - $s = Statistics::make( - [3, 4, 3], - ); - $a = $s->thirdQuartile(); - expect($a)->toEqual(4); + $s = Statistics::make([3, 4, 3]); + $this->assertEquals(4, $s->thirdQuartile()); + } - $s = Statistics::make( - [], - ); - expect(fn() => $s->thirdQuartile())->toThrow(InvalidDataInputException::class); -}); + public function test_can_calculate_third_quartile_with_empty_array(): void + { + $s = Statistics::make([]); + $this->expectException(InvalidDataInputException::class); + $s->thirdQuartile(); + } +} diff --git a/tests/MathTest.php b/tests/MathTest.php index b9d152e..c1ff12e 100644 --- a/tests/MathTest.php +++ b/tests/MathTest.php @@ -1,10 +1,17 @@ toBeTrue(); - expect(Math::isOdd(0))->toBeFalse(); - expect(Math::isOdd(-5))->toBeTrue(); - expect(Math::isOdd(-2))->toBeFalse(); -}); +class MathTest extends TestCase +{ + public function test_is_odd(): void + { + $this->assertTrue(Math::isOdd(1)); + $this->assertFalse(Math::isOdd(0)); + $this->assertTrue(Math::isOdd(-5)); + $this->assertFalse(Math::isOdd(-2)); + } +} diff --git a/tests/NormalDistTest.php b/tests/NormalDistTest.php index f23bff5..9885f7a 100644 --- a/tests/NormalDistTest.php +++ b/tests/NormalDistTest.php @@ -1,87 +1,58 @@ getMean(), - )->toEqual(1060); - expect( - $nd->getSigma(), - )->toEqual(195); - -}); -it('can calculate normal dist cdf', function (): void { - $nd = new NormalDist(1060, 195); - expect( - round($nd->cdf(1200 + 0.5) - $nd->cdf(1100 - 0.5), 3), - )->toEqual(0.184); -}); - -it('can calculate normal dist pdf', function (): void { - $nd = new NormalDist(10, 2); - expect( - $nd->pdfRounded(12, 3), - )->toEqual(0.121); - expect( - $nd->pdfRounded(12, 2), - )->toEqual(0.12); -}); - -it(' load normal dist from samples', function (): void { - // NormalDist.from_samples([2.5, 3.1, 2.1, 2.4, 2.7, 3.5]) - // NormalDist(mu=2.716666666666667, sigma=0.5076087732365021) - $samples = [2.5, 3.1, 2.1, 2.4, 2.7, 3.5]; - $normalDist = NormalDist::fromSamples($samples); - - expect( - $normalDist->getMeanRounded(5), - )->toEqual(2.71667); - expect( - $normalDist->getSigmaRounded(5), - )->toEqual(0.50761); -}); - - -it(' add to Normal Dist', function (): void { - $birth_weights = NormalDist::fromSamples([2.5, 3.1, 2.1, 2.4, 2.7, 3.5]); - $drug_effects = new NormalDist(0.4, 0.15); - $combined = $birth_weights->add($drug_effects); - expect( - $combined->getMeanRounded(1), - )->toEqual(3.1); - expect( - $combined->getSigmaRounded(1), - )->toEqual(0.5); +namespace HiFolks\Statistics\Tests; - expect( - $birth_weights->getMeanRounded(5), - )->toEqual(2.71667); - expect( - $birth_weights->getSigmaRounded(5), - )->toEqual(0.50761); - - -}); - - -it(' multiply Normal Dist', function (): void { - $tempFebruaryCelsius = new NormalDist(5, 2.5); # Celsius - $tempFebFahrenheit = $tempFebruaryCelsius->multiply(9 / 5)->add(32); # Fahrenheit - expect( - $tempFebFahrenheit->getMeanRounded(1), - )->toEqual(41.0); - expect( - $tempFebFahrenheit->getSigmaRounded(1), - )->toEqual(4.5); - - expect( - $tempFebruaryCelsius->getMeanRounded(1), - )->toEqual(5.0); - expect( - $tempFebruaryCelsius->getSigmaRounded(1), - )->toEqual(2.5); - - -}); +use HiFolks\Statistics\NormalDist; +use PHPUnit\Framework\TestCase; + +class NormalDistTest extends TestCase +{ + public function test_init_normal_dist(): void + { + $nd = new NormalDist(1060, 195); + $this->assertEquals(1060, $nd->getMean()); + $this->assertEquals(195, $nd->getSigma()); + } + + public function test_can_calculate_normal_dist_cdf(): void + { + $nd = new NormalDist(1060, 195); + $this->assertEquals(0.184, round($nd->cdf(1200 + 0.5) - $nd->cdf(1100 - 0.5), 3)); + } + + public function test_can_calculate_normal_dist_pdf(): void + { + $nd = new NormalDist(10, 2); + $this->assertEquals(0.121, $nd->pdfRounded(12, 3)); + $this->assertEquals(0.12, $nd->pdfRounded(12, 2)); + } + + public function test_load_normal_dist_from_samples(): void + { + $samples = [2.5, 3.1, 2.1, 2.4, 2.7, 3.5]; + $normalDist = NormalDist::fromSamples($samples); + $this->assertEquals(2.71667, $normalDist->getMeanRounded(5)); + $this->assertEquals(0.50761, $normalDist->getSigmaRounded(5)); + } + + public function test_add_to_normal_dist(): void + { + $birth_weights = NormalDist::fromSamples([2.5, 3.1, 2.1, 2.4, 2.7, 3.5]); + $drug_effects = new NormalDist(0.4, 0.15); + $combined = $birth_weights->add($drug_effects); + $this->assertEquals(3.1, $combined->getMeanRounded(1)); + $this->assertEquals(0.5, $combined->getSigmaRounded(1)); + $this->assertEquals(2.71667, $birth_weights->getMeanRounded(5)); + $this->assertEquals(0.50761, $birth_weights->getSigmaRounded(5)); + } + + public function test_multiply_normal_dist(): void + { + $tempFebruaryCelsius = new NormalDist(5, 2.5); + $tempFebFahrenheit = $tempFebruaryCelsius->multiply(9 / 5)->add(32); + $this->assertEquals(41.0, $tempFebFahrenheit->getMeanRounded(1)); + $this->assertEquals(4.5, $tempFebFahrenheit->getSigmaRounded(1)); + $this->assertEquals(5.0, $tempFebruaryCelsius->getMeanRounded(1)); + $this->assertEquals(2.5, $tempFebruaryCelsius->getSigmaRounded(1)); + } +} diff --git a/tests/Pest.php b/tests/Pest.php deleted file mode 100644 index b3d9bbc..0000000 --- a/tests/Pest.php +++ /dev/null @@ -1 +0,0 @@ -assertEquals(2.8, Stat::mean([1, 2, 3, 4, 4])); + $this->assertEquals(2.625, Stat::mean([-1.0, 2.5, 3.25, 5.75])); + } + + public function test_mean_chain(): void + { + $this->assertEquals(2.8, Stat::mean([1, 2, 3, 4, 4])); + $this->assertEquals(2.625, Stat::mean([-1.0, 2.5, 3.25, 5.75])); + } + + /** @param array $input */ + #[DataProvider('meanDatasetProvider')] + public function test_mean_dataset(array $input, float $result): void + { + $this->assertEquals($result, Stat::mean($input)); + } + + /** @return array, float}> */ + public static function meanDatasetProvider(): array + { + return [ + [[1, 2, 3, 4, 4], 2.8], + [[-1.0, 2.5, 3.25, 5.75], 2.625], + ]; + } + + /** @param array $input */ + #[DataProvider('dynamicOperationProvider')] + public function test_dynamic_operation(string $methodName, array $input, float $result): void + { + $this->assertEquals($result, Stat::$methodName($input)); + } + + /** @return array, float}> */ + public static function dynamicOperationProvider(): array + { + return [ + ['mean', [1, 2, 3, 4, 4], 2.8], + ['mean', [-1.0, 2.5, 3.25, 5.75], 2.625], + ['median', [1, 3, 5], 3], + ['median', [1, 3, 5, 7], 4], + ['medianLow', [1, 3, 5], 3], + ['medianLow', [1, 3, 5, 7], 3], + ]; + } + + /** @param array $input */ + #[DataProvider('externalDatasetProvider')] + public function test_dynamic_operation_with_external_dataset(string $methodName, array $input, float $result): void + { + $this->assertEquals( + $result, + Stat::$methodName($input), + ); + + $this->assertEquals( + $result, + Stat::$methodName($input), + ); + } -describe('Calculating Stat operation', function () { - it('Mean', function () { - expect(Stat::mean([1, 2, 3, 4, 4]))->toEqual(2.8); - expect(Stat::mean([-1.0, 2.5, 3.25, 5.75]))->toEqual(2.625); - }); - - it('Mean chain expect', function () { - expect(Stat::mean([1, 2, 3, 4, 4])) - ->toEqual(2.8) - ->and(Stat::mean([-1.0, 2.5, 3.25, 5.75])) - ->toEqual(2.625); - }); - - it('Mean dataset', function (array $input, float $result) { - expect(Stat::mean($input))->toEqual($result); - })->with([ - [[1, 2, 3, 4, 4], 2.8], - [[-1.0, 2.5, 3.25, 5.75], 2.625], - ]); - - it('Dynamic operation', function (string $methodName, array $input, float $result) { - expect(call_user_func([Stat::class, $methodName], $input))->toEqual($result); - })->with([ - ["mean", [1, 2, 3, 4, 4], 2.8], - ["mean", [-1.0, 2.5, 3.25, 5.75], 2.625], - ["median", [1, 3, 5], 3], - ["median", [1, 3, 5, 7], 4], - ["medianLow", [1, 3, 5], 3], - ["medianLow", [1, 3, 5, 7], 3], - ]); - - it('Dynamic operation with external dataset', function (string $methodName, array $input, float $result) { - expect( - call_user_func("HiFolks\Statistics\Stat::" . $methodName, $input), - )->toEqual($result); - - expect( - call_user_func([Stat::class, $methodName], $input), - )->toEqual($result); - })->with('input1'); -}); + /** @return array, float}> */ + public static function externalDatasetProvider(): array + { + return [ + ['mean', [1, 2, 3, 4, 4], 2.8], + ['mean', [-1.0, 2.5, 3.25, 5.75], 2.625], + ['median', [1, 3, 5], 3], + ['median', [1, 3, 5, 7], 4], + ['medianLow', [1, 3, 5], 3], + ['medianLow', [1, 3, 5, 7], 3], + ]; + } +} diff --git a/tests/StatFromCsvTest.php b/tests/StatFromCsvTest.php index 2ec7b17..cc3c96c 100644 --- a/tests/StatFromCsvTest.php +++ b/tests/StatFromCsvTest.php @@ -1,41 +1,47 @@ toEqual(3); - $row++; - if ($row === 1) { - continue; + if (($handle = fopen(getcwd() . '/tests/data/income.data.csv', 'r')) !== false) { + $x = []; + $y = []; + while (($data = fgetcsv( + $handle, + 1000, + separator: ',', + enclosure: '"', + escape: "", + )) !== false) { + $num = count($data); + $this->assertEquals(3, $num); + $row++; + if ($row === 1) { + continue; + } + $income = floatval($data[1]); + $x[] = $income; + $happiness = floatval($data[2]); + $y[] = $happiness; + $this->assertIsFloat($income); + $this->assertGreaterThan(0, $income); + $this->assertIsFloat($happiness); } - $income = floatval($data[1]); - $x[] = $income; - $happiness = floatval($data[2]); - $y[] = $happiness; - expect($income)->toBeFloat(); - expect($income)->toBeGreaterThan(0); - expect($happiness)->toBeFloat(); + [$slope, $intercept] = Stat::linearRegression($x, $y); + $this->assertEquals(0.71383, round($slope, 5)); + $this->assertEquals(0.20427, round($intercept, 5)); + + fclose($handle); } - [$slope, $intercept] = Stat::linearRegression($x, $y); - expect(round($slope, 5))->toEqual(0.71383); - expect(round($intercept, 5))->toEqual(0.20427); - //expect(round(Stat::median($x), 5))->toEqual(0); - fclose($handle); + $this->assertEquals(499, $row); } - - expect($row)->toEqual(499); -}); +} diff --git a/tests/StatTest.php b/tests/StatTest.php index 3ef4f40..4d6ec3c 100644 --- a/tests/StatTest.php +++ b/tests/StatTest.php @@ -1,418 +1,396 @@ assertEquals(2.8, Stat::mean([1, 2, 3, 4, 4])); + $this->assertEquals(2.625, Stat::mean([-1.0, 2.5, 3.25, 5.75])); + $this->expectException(InvalidDataInputException::class); + Stat::mean([]); + } + + public function test_calculates_fmean(): void + { + $this->assertEquals(2.8, Stat::mean([1, 2, 3, 4, 4])); + $this->assertEquals(2.625, Stat::mean([-1.0, 2.5, 3.25, 5.75])); + + $result = Stat::fmean([3.5, 4.0, 5.25]); + $this->assertIsFloat($result); + $this->assertEquals(4.25, $result); + + $result = Stat::fmean([85, 92, 83, 91], [0.20, 0.20, 0.30, 0.30], 2); + $this->assertIsFloat($result); + $this->assertEquals(87.6, $result); + + $result = Stat::fmean([3.5, 4.0, 5.25], [1, 2, 1]); + $this->assertIsFloat($result); + $this->assertEquals(4.1875, $result); + + $result = Stat::fmean([3.5, 4.0, 5.25], precision: 2); + $this->assertIsFloat($result); + $this->assertEquals(4.25, $result); + + $result = Stat::fmean([3.5, 4.0, 5.25], [1, 2, 1], precision: 3); + $this->assertIsFloat($result); + $this->assertEquals(4.188, $result); + } + + public function test_calculates_fmean_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::mean([]); + } + + public function test_calculates_median(): void + { + $this->assertEquals(3, Stat::median([1, 3, 5])); + $this->assertEquals(4, Stat::median([1, 3, 5, 7])); + $this->assertEquals(1001, Stat::median([1001, 999, 998, 1001, 1002])); + $this->assertEquals(1001.5, Stat::median([1001, 999, 998, 1003, 1002, 1003])); + $this->assertEquals(7, Stat::median([1, 3, 5, 7, 9, 11, 13])); + $this->assertEquals(6, Stat::median([1, 3, 5, 7, 9, 11])); + $this->assertEquals(1.05, Stat::median([-11, 5.5, -3.4, 7.1, -9, 22])); + $this->assertEquals(0, Stat::median([-1, -2, -3, -4, 4, 3, 2, 1])); + } + + public function test_calculates_median_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::median([]); + } + + public function test_calculates_median_low(): void + { + $this->assertEquals(3, Stat::medianLow([1, 3, 5])); + $this->assertEquals(3, Stat::medianLow([1, 3, 5, 7])); + $this->assertEquals(1001, Stat::medianLow([1001, 999, 998, 1003, 1002, 1003])); + } + + public function test_calculates_median_low_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::medianLow([]); + } + + public function test_calculates_median_high(): void + { + $this->assertEquals(3, Stat::medianHigh([1, 3, 5])); + $this->assertEquals(5, Stat::medianHigh([1, 3, 5, 7])); + $this->assertEquals(1002, Stat::medianHigh([1001, 999, 998, 1003, 1002, 1003])); + } + + public function test_calculates_median_high_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::medianHigh([]); + } + + public function test_calculates_mode(): void + { + $this->assertEquals(3, Stat::mode([1, 1, 2, 3, 3, 3, 3, 4])); + $this->assertNull(Stat::mode([1, 2, 3])); + $this->assertEquals('red', Stat::mode(['red', 'blue', 'blue', 'red', 'green', 'red', 'red'])); + } + + public function test_calculates_mode_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::mode([]); + } + + public function test_calculates_multimode(): void + { + $this->assertEquals([3], Stat::multimode([1, 1, 2, 3, 3, 3, 3, 4])); + $this->assertEquals([1, 3], Stat::multimode([1, 1, 2, 3, 3, 3, 3, 1, 1, 4])); + $result = Stat::multimode(str_split('aabbbbccddddeeffffgg')); + $this->assertNotNull($result); + $this->assertEquals(['b', 'd', 'f'], $result); + $this->assertCount(3, $result); + $this->assertEquals('b', $result[0]); + $this->assertEquals('d', $result[1]); + $this->assertEquals('f', $result[2]); + } + + public function test_calculates_multimode_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::multimode([]); + } + + public function test_calculates_population_standard_deviation(): void + { + $this->assertEquals(0.986893273527251, Stat::pstdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])); + $this->assertEquals(2.4495, Stat::pstdev([1, 2, 4, 5, 8], 4)); + $this->assertEquals(0, Stat::pstdev([1])); + $this->assertEquals(0.8291562, Stat::pstdev([1, 2, 3, 3], 7)); + } + + public function test_calculates_population_standard_deviation_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::pstdev([]); + } + + public function test_calculates_sample_standard_deviation(): void + { + $this->assertEquals(1.0810874155219827, Stat::stdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])); + $this->assertEquals(2, Stat::stdev([1, 2, 2, 4, 6])); + $this->assertEquals(2.7386, Stat::stdev([1, 2, 4, 5, 8], 4)); + } + + public function test_calculates_sample_standard_deviation_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::stdev([]); + } + + public function test_calculates_sample_standard_deviation_with_single_element(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::stdev([1]); + } + + public function test_calculates_variance(): void + { + $this->assertEquals(1.3720238095238095, Stat::variance([2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5])); + } + + public function test_calculates_pvariance(): void + { + $this->assertEquals(1.25, Stat::pvariance([0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25])); + $this->assertEquals(0.6875, Stat::pvariance([1, 2, 3, 3])); + } + + public function test_calculates_geometric_mean(): void + { + $this->assertEquals(36, Stat::geometricMean([54, 24, 36], 2)); + } + + public function test_calculates_geometric_mean_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::geometricMean([]); + } + + public function test_calculates_harmonic_mean(): void + { + $this->assertEquals(48, Stat::harmonicMean([40, 60], round: 2)); + $this->assertEquals(0, Stat::harmonicMean([10, 100, 0, 1])); + $this->assertEquals(56, Stat::harmonicMean([40, 60], [5, 30])); + $this->assertEquals(52.2, Stat::harmonicMean([60, 40], [7, 3], 1)); + } + + public function test_calculates_harmonic_mean_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::harmonicMean([]); + } + + public function test_calculates_quantiles(): void + { + $q = Stat::quantiles([98, 90, 70, 18, 92, 92, 55, 83, 45, 95, 88, 76]); + $this->assertEquals(58.75, $q[0]); + $this->assertEquals(85.5, $q[1]); + $this->assertEquals(92, $q[2]); + + $q = Stat::quantiles([98, 90, 70, 18, 92, 92, 55, 83, 45, 95, 88]); + $this->assertEquals(55, $q[0]); + $this->assertEquals(88, $q[1]); + $this->assertEquals(92, $q[2]); + + $q = Stat::quantiles([1, 2]); + $this->assertEquals(0.75, $q[0]); + $this->assertEquals(1.5, $q[1]); + $this->assertEquals(2.25, $q[2]); + + $q = Stat::quantiles([1, 2, 4]); + $this->assertEquals(1, $q[0]); + $this->assertEquals(2, $q[1]); + $this->assertEquals(4, $q[2]); + } + + public function test_calculates_quantiles_with_too_few_elements(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::quantiles([1]); + } + + public function test_calculates_quantiles_with_invalid_n(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::quantiles([1, 2, 3], 0); + } + + public function test_calculates_first_quartile(): void + { + $q = Stat::firstQuartile([98, 90, 70, 18, 92, 92, 55, 83, 45, 95, 88, 76]); + $this->assertEquals(58.75, $q); + } + + public function test_calculates_first_quartile_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::firstQuartile([]); + } + + public function test_calculates_covariance(): void + { + $covariance = Stat::covariance( + [1, 2, 3, 4, 5, 6, 7, 8, 9], + [1, 2, 3, 1, 2, 3, 1, 2, 3], + ); + $this->assertEquals(0.75, $covariance); + + $covariance = Stat::covariance( + [9, 8, 7, 6, 5, 4, 3, 2, 1], + [1, 2, 3, 4, 5, 6, 7, 8, 9], + ); + $this->assertEquals(-7.5, $covariance); -it('calculates mean (static)', function () { - expect( - Stat::mean([1, 2, 3, 4, 4]), - )->toEqual(2.8); - expect( - Stat::mean([-1.0, 2.5, 3.25, 5.75]), - )->toEqual(2.625); - expect( - fn() => Stat::mean([]), - )->toThrow(InvalidDataInputException::class); -}); - -it('calculates fmean (static)', function () { - expect( - Stat::mean([1, 2, 3, 4, 4]), - )->toEqual(2.8); - expect( - Stat::mean([-1.0, 2.5, 3.25, 5.75]), - )->toEqual(2.625); - expect( - fn() => Stat::mean([]), - )->toThrow(InvalidDataInputException::class); - expect( - Stat::fmean([3.5, 4.0, 5.25]), - )->toBeFloat()->toEqual(4.25); - expect( - Stat::fmean([85, 92, 83, 91], [0.20, 0.20, 0.30, 0.30], 2), - )->toBeFloat()->toEqual(87.6); - expect( - Stat::fmean([3.5, 4.0, 5.25], [1, 2, 1]), - )->toBeFloat()->toEqual(4.1875); - expect( - Stat::fmean([3.5, 4.0, 5.25], precision: 2), - )->toBeFloat()->toEqual(4.25); - expect( - Stat::fmean([3.5, 4.0, 5.25], [1, 2, 1], precision: 3), - )->toBeFloat()->toEqual(4.188); - -}); - -it('calculates median (static)', function () { - expect( - Stat::median([1, 3, 5]), - )->toEqual(3); - expect( - Stat::median([1, 3, 5, 7]), - )->toEqual(4); - expect( - Stat::median([1001, 999, 998, 1001, 1002]), - )->toEqual(1001); - expect( - Stat::median([1001, 999, 998, 1003, 1002, 1003]), - )->toEqual(1001.5); - expect( - Stat::median([1, 3, 5, 7, 9, 11, 13]), - )->toEqual(7); - expect( - Stat::median([1, 3, 5, 7, 9, 11]), - )->toEqual(6); - expect( - Stat::median([-11, 5.5, -3.4, 7.1, -9, 22]), - )->toEqual(1.05); - expect( - Stat::median([-1, -2, -3, -4, 4, 3, 2, 1]), - )->toEqual(0); - expect( - fn() => Stat::median([]), - )->toThrow(InvalidDataInputException::class); -}); -it('calculates median low (static)', function () { - expect( - Stat::medianLow([1, 3, 5]), - )->toEqual(3); - expect( - Stat::medianLow([1, 3, 5, 7]), - )->toEqual(3); - expect( - Stat::medianLow([1001, 999, 998, 1003, 1002, 1003]), - )->toEqual(1001); - expect( - fn() => Stat::medianLow([]), - )->toThrow(InvalidDataInputException::class); -}); -it('calculates median high (static)', function () { - expect( - Stat::medianHigh([1, 3, 5]), - )->toEqual(3); - expect( - Stat::medianHigh([1, 3, 5, 7]), - )->toEqual(5); - expect( - Stat::medianHigh([1001, 999, 998, 1003, 1002, 1003]), - )->toEqual(1002); - expect( - fn() => Stat::medianHigh([]), - )->toThrow(InvalidDataInputException::class); -}); - -it('calculates mode (static)', function () { - expect( - Stat::mode([1, 1, 2, 3, 3, 3, 3, 4]), - )->toEqual(3); - expect( - fn() => Stat::mode([]), - )->toThrow(InvalidDataInputException::class); - expect( - Stat::mode([1, 2, 3]), - )->toBeNull(); - expect( - Stat::mode(['red', 'blue', 'blue', 'red', 'green', 'red', 'red']), - )->toEqual('red'); -}); - -it('calculates multimode (static)', function () { - expect( - Stat::multimode([1, 1, 2, 3, 3, 3, 3, 4]), - )->toMatchArray([3]); - expect( - Stat::multimode([1, 1, 2, 3, 3, 3, 3, 1, 1, 4]), - )->toMatchArray([1, 3]); - $result = Stat::multimode(str_split('aabbbbccddddeeffffgg')); - expect( - $result, - )->toMatchArray(['b', 'd', 'f']); - expect( - $result, - )->toHaveCount(3); - - expect( - $result[0], - )->toEqual('b'); - expect( - $result[1], - )->toEqual('d'); - expect( - $result[2], - )->toEqual('f'); - expect( - fn() => Stat::multimode([]), - )->toThrow(InvalidDataInputException::class); -}); -it('calculates Population standard deviation (static)', function () { - expect( - Stat::pstdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75]), - )->toEqual(0.986893273527251); - expect( - Stat::pstdev([1, 2, 4, 5, 8], 4), - )->toEqual(2.4495); - expect( - fn() => Stat::pstdev([]), - )->toThrow(InvalidDataInputException::class); - expect( - Stat::pstdev([1]), - )->toEqual(0); - expect( - Stat::pstdev([1, 2, 3, 3], 7), - )->toEqual(0.8291562); -}); -it('calculates Sample standard deviation (static)', function () { - expect( - Stat::stdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75]), - )->toEqual(1.0810874155219827); - expect( - Stat::stdev([1, 2, 2, 4, 6]), - )->toEqual(2); - expect( - Stat::stdev([1, 2, 4, 5, 8], 4), - )->toEqual(2.7386); - expect( - fn() => Stat::stdev([]), - )->toThrow(InvalidDataInputException::class); - expect( - fn() => Stat::stdev([1]), - )->toThrow(InvalidDataInputException::class); -}); - -it('calculates variance (static)', function () { - expect( - Stat::variance([2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]), - )->toEqual(1.3720238095238095); -}); - -it('calculates pvariance (static)', function () { - expect( - Stat::pvariance([0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25]), - )->toEqual(1.25); - expect( - Stat::pvariance([1, 2, 3, 3]), - )->toEqual(0.6875); -}); - -it('calculates geometric mean (static)', function () { - expect( - Stat::geometricMean([54, 24, 36], 2), - )->toEqual(36); - expect( - fn() => Stat::geometricMean([]), - )->toThrow(InvalidDataInputException::class); -}); -it('calculates harmonic mean (static)', function () { - expect( - Stat::harmonicMean([40, 60], round: 2), - )->toEqual(48); - expect( - Stat::harmonicMean([10, 100, 0, 1]), - )->toEqual(0); - expect( - Stat::harmonicMean([40, 60], [5, 30]), - )->toEqual(56); - expect( - Stat::harmonicMean([60, 40], [7, 3], 1), - )->toEqual(52.2); - expect( - fn() => Stat::harmonicMean([]), - )->toThrow(InvalidDataInputException::class); -}); - -it('calculates quantiles (static)', function () { - $q = Stat::quantiles([98, 90, 70, 18, 92, 92, 55, 83, 45, 95, 88, 76]); - expect($q[0])->toEqual(58.75); - expect($q[1])->toEqual(85.5); - expect($q[2])->toEqual(92); - $q = Stat::quantiles([98, 90, 70, 18, 92, 92, 55, 83, 45, 95, 88]); - expect($q[0])->toEqual(55); - expect($q[1])->toEqual(88); - expect($q[2])->toEqual(92); - $q = Stat::quantiles([1, 2]); - expect($q[0])->toEqual(0.75); - expect($q[1])->toEqual(1.5); - expect($q[2])->toEqual(2.25); - $q = Stat::quantiles([1, 2, 4]); - expect($q[0])->toEqual(1); - expect($q[1])->toEqual(2); - expect($q[2])->toEqual(4); - expect( - fn() => Stat::quantiles([1]), - )->toThrow(InvalidDataInputException::class); - expect( - fn() => Stat::quantiles([1, 2, 3], 0), - )->toThrow(InvalidDataInputException::class); -}); - -it('calculates first quartiles (static)', function () { - $q = Stat::firstQuartile([98, 90, 70, 18, 92, 92, 55, 83, 45, 95, 88, 76]); - expect($q)->toEqual(58.75); - expect( - fn() => Stat::firstQuartile([]), - )->toThrow(InvalidDataInputException::class); -}); - -it('calculates covariance (static)', function () { - $covariance = Stat::covariance( - [1, 2, 3, 4, 5, 6, 7, 8, 9], - [1, 2, 3, 1, 2, 3, 1, 2, 3], - ); - expect($covariance)->toEqual(0.75); - - $covariance = Stat::covariance( - [9, 8, 7, 6, 5, 4, 3, 2, 1], - [1, 2, 3, 4, 5, 6, 7, 8, 9], - ); - expect($covariance)->toEqual(-7.5); - - $covariance = Stat::covariance( - [1, 2, 3, 4, 5, 6, 7, 8, 9], - [9, 8, 7, 6, 5, 4, 3, 2, 1], - ); - expect($covariance)->toEqual(-7.5); -}); - -it('calculates covariance, wrong usage (static)', function () { - expect( - fn() => Stat::covariance( + $covariance = Stat::covariance( + [1, 2, 3, 4, 5, 6, 7, 8, 9], + [9, 8, 7, 6, 5, 4, 3, 2, 1], + ); + $this->assertEquals(-7.5, $covariance); + } + + public function test_calculates_covariance_wrong_usage(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::covariance( [9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 2, 3, 4, 5, 6, 7, 8], - ), - )->toThrow(InvalidDataInputException::class); - - expect( - fn() => Stat::covariance( - [], - [], - ), - )->toThrow(InvalidDataInputException::class); - - expect( - fn() => Stat::covariance( - [3], - [3], - ), - )->toThrow(InvalidDataInputException::class); - - expect( - fn() => Stat::covariance( - ['a', 1], - ['b', 2], - ), - )->toThrow(InvalidDataInputException::class); - - expect( - fn() => Stat::covariance( - [3, 1], - ['b', 2], - ), - )->toThrow(InvalidDataInputException::class); -}); - -it('calculates correlation (static)', function () { - $correlation = Stat::correlation( - [1, 2, 3, 4, 5, 6, 7, 8, 9], - [1, 2, 3, 4, 5, 6, 7, 8, 9], - ); - expect($correlation)->toBeFloat(); - expect($correlation)->toEqual(1); - - $correlation = Stat::correlation( - [1, 2, 3, 4, 5, 6, 7, 8, 9], - [9, 8, 7, 6, 5, 4, 3, 2, 1], - ); - expect($correlation)->toBeFloat(); - expect($correlation)->toEqual(-1); - - $correlation = Stat::correlation( - [3, 6, 9], - [70, 75, 80], - ); - expect($correlation)->toBeFloat(); - expect($correlation)->toEqual(1); - - $correlation = Stat::correlation( - [20, 23, 8, 29, 14, 11, 11, 20, 17, 17], - [30, 35, 21, 33, 33, 26, 22, 31, 33, 36], - ); - expect($correlation)->toBeFloat(); - expect($correlation)->toEqual(0.71); -}); - -it('calculates correlation, wrong usage (static)', function () { - expect( - fn() => Stat::correlation( + ); + } + + public function test_calculates_covariance_with_empty_arrays(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::covariance([], []); + } + + public function test_calculates_covariance_with_single_element(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::covariance([3], [3]); + } + + public function test_calculates_covariance_with_non_numeric_first(): void + { + $this->expectException(InvalidDataInputException::class); + // Intentionally passing non-numeric values to test exception handling + Stat::covariance(['a', 1], ['b', 2]); // @phpstan-ignore argument.type, argument.type + } + + public function test_calculates_covariance_with_non_numeric_second(): void + { + $this->expectException(InvalidDataInputException::class); + // Intentionally passing non-numeric values to test exception handling + Stat::covariance([3, 1], ['b', 2]); // @phpstan-ignore argument.type + } + + public function test_calculates_correlation(): void + { + $correlation = Stat::correlation( + [1, 2, 3, 4, 5, 6, 7, 8, 9], + [1, 2, 3, 4, 5, 6, 7, 8, 9], + ); + $this->assertIsFloat($correlation); + $this->assertEquals(1, $correlation); + + $correlation = Stat::correlation( + [1, 2, 3, 4, 5, 6, 7, 8, 9], + [9, 8, 7, 6, 5, 4, 3, 2, 1], + ); + $this->assertIsFloat($correlation); + $this->assertEquals(-1, $correlation); + + $correlation = Stat::correlation( + [3, 6, 9], + [70, 75, 80], + ); + $this->assertIsFloat($correlation); + $this->assertEquals(1, $correlation); + + $correlation = Stat::correlation( + [20, 23, 8, 29, 14, 11, 11, 20, 17, 17], + [30, 35, 21, 33, 33, 26, 22, 31, 33, 36], + ); + $this->assertIsFloat($correlation); + $this->assertEquals(0.71, $correlation); + } + + public function test_calculates_correlation_wrong_usage_different_lengths(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::correlation( [9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 2, 3, 4, 5, 6, 7, 8], - ), - )->toThrow(InvalidDataInputException::class); - - expect( - fn() => Stat::correlation( - [], - [], - ), - )->toThrow(InvalidDataInputException::class); - - expect( - fn() => Stat::correlation( - [3], - [3], - ), - )->toThrow(InvalidDataInputException::class); - - expect( - fn() => Stat::correlation( - [3, 1, 2], - [2, 2, 2], - ), - )->toThrow(InvalidDataInputException::class); -}); - -it('calculates linear regression (static)', function () { - [$slope, $intercept] = Stat::linearRegression( - [1971, 1975, 1979, 1982, 1983], - [1, 2, 3, 4, 5], - ); - expect($slope)->toBeFloat(); - expect($slope)->toEqual(0.31); - - expect($intercept)->toBeFloat(); - expect(round($intercept, 2))->toEqual(-610.18); - - [$slope, $intercept] = Stat::linearRegression( - [1971, 1975, 1979, 1982, 1983], - [1, 2, 1, 3, 1], - ); - expect($slope)->toBeFloat(); - expect($slope)->toEqual(0.05); - - expect($intercept)->toBeFloat(); - expect($intercept)->toEqual(-97.3); - - expect(round($slope * 2019 + $intercept))->toEqual(4); -}); - -it('calculates linear regression with not valid input (static)', function () { - expect( - fn() => Stat::linearRegression( - [3], - [2], - ), - )->toThrow(InvalidDataInputException::class); - - expect( - fn() => Stat::linearRegression( - [3, 3, 3, 3], - [2, 1, 1, 1, 1], - ), - )->toThrow(InvalidDataInputException::class); - - expect( - fn() => Stat::linearRegression( - [3, 3, 3, 3, 3], - [1, 1, 1, 1, 1], - ), - )->toThrow(InvalidDataInputException::class); -}); + ); + } + + public function test_calculates_correlation_wrong_usage_empty(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::correlation([], []); + } + + public function test_calculates_correlation_wrong_usage_single(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::correlation([3], [3]); + } + + public function test_calculates_correlation_wrong_usage_constant(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::correlation([3, 1, 2], [2, 2, 2]); + } + + public function test_calculates_linear_regression(): void + { + [$slope, $intercept] = Stat::linearRegression( + [1971, 1975, 1979, 1982, 1983], + [1, 2, 3, 4, 5], + ); + $this->assertIsFloat($slope); + $this->assertEquals(0.31, $slope); + $this->assertIsFloat($intercept); + $this->assertEquals(-610.18, round($intercept, 2)); + + [$slope, $intercept] = Stat::linearRegression( + [1971, 1975, 1979, 1982, 1983], + [1, 2, 1, 3, 1], + ); + $this->assertIsFloat($slope); + $this->assertEquals(0.05, $slope); + $this->assertIsFloat($intercept); + $this->assertEquals(-97.3, $intercept); + $this->assertEquals(4, round($slope * 2019 + $intercept)); + } + + public function test_calculates_linear_regression_with_single_element(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::linearRegression([3], [2]); + } + + public function test_calculates_linear_regression_with_different_lengths(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::linearRegression([3, 3, 3, 3], [2, 1, 1, 1, 1]); + } + + public function test_calculates_linear_regression_with_constant_x(): void + { + $this->expectException(InvalidDataInputException::class); + Stat::linearRegression([3, 3, 3, 3, 3], [1, 1, 1, 1, 1]); + } +} diff --git a/tests/StatisticTest.php b/tests/StatisticTest.php index 50c9dc3..b14a7b0 100644 --- a/tests/StatisticTest.php +++ b/tests/StatisticTest.php @@ -1,199 +1,211 @@ count())->toEqual(12); - expect($s->median())->toEqual(85.5); - expect($s->firstQuartile())->toEqual(58.75); - expect($s->thirdQuartile())->toEqual(92); - expect($s->interquartileRange())->toEqual(33.25); - - expect($s->originalArray())->toHaveCount(12); - - $s = Statistics::make( - [98, 90, 70, 18, 92, 92, 55, 83, 45, 95, 88], - ); - expect($s->count())->toEqual(11); - expect($s->median())->toEqual(88); - expect($s->firstQuartile())->toEqual(55); - expect($s->thirdQuartile())->toEqual(92); - expect($s->interquartileRange())->toEqual(37); - expect($s->originalArray())->toHaveCount(11); -}); - -it('can calculate statistics again', function (): void { - // https://www.youtube.com/watch?v=6z7B7ADL6Lw&ab_channel=TheMathsProf - $s = Statistics::make( - [3, 5, 4, 7, 5, 2], - ); - expect($s->count())->toEqual(6); - expect($s->mean())->toEqual(13 / 3); - expect($s->median())->toEqual(4.5); - expect($s->mode())->toEqual(5); - expect($s->min())->toEqual(2); - expect($s->max())->toEqual(7); - expect($s->range())->toEqual(5); - expect($s->firstQuartile())->toEqual(2.75); - expect($s->thirdQuartile())->toEqual(5.5); -}); - -it('can calculate statistics again and again', function (): void { - // https://www.purplemath.com/modules/meanmode.htm - $s = Statistics::make( - [13, 18, 13, 14, 13, 16, 14, 21, 13], - ); - expect($s->count())->toEqual(9); - expect($s->mean())->toEqual(15); - expect($s->median())->toEqual(14); - expect($s->mode())->toEqual(13); - expect($s->min())->toEqual(13); - expect($s->max())->toEqual(21); - expect($s->range())->toEqual(8); - expect($s->firstQuartile())->toEqual(13); - expect($s->thirdQuartile())->toEqual(17); - - $s = Statistics::make( - [1, 2, 4, 7], - ); - expect($s->count())->toEqual(4); - expect($s->mean())->toEqual(3.5); - expect($s->median())->toEqual(3); - expect($s->mode())->toBeNull(); - expect($s->min())->toEqual(1); - expect($s->max())->toEqual(7); - expect($s->range())->toEqual(6); -}); - -it('can strip zeros', function (): void { // https://www.youtube.com/watch?v=6z7B7ADL6Lw&ab_channel=TheMathsProf - $s = Statistics::make( - [3, 5, 0, 0.1, 4, 7, 5, 2], - )->stripZeroes(); - expect($s->count())->toEqual(7); -}); -it('can calculate mean', function (): void { // https://www.youtube.com/watch?v=6z7B7ADL6Lw&ab_channel=TheMathsProf - $s = Statistics::make( - [3, 5, 4, 7, 5, 2], - ); - expect($s->count())->toEqual(6); - expect($s->mean())->toEqual(13 / 3); - $s = Statistics::make( - [], - ); - expect($s->count())->toEqual(0); - expect(fn(): float|int|null => $s->mean())->toThrow(InvalidDataInputException::class); -}); - -it('can calculate mean again', function (): void { // https://docs.python.org/3/library/statistics.html#statistics.mean - $s = Statistics::make( - [1, 2, 3, 4, 4], - ); - expect($s->mean())->toEqual(2.8); - - $s = Statistics::make( - [-1.0, 2.5, 3.25, 5.75], - ); - expect($s->mean())->toEqual(2.625); - - $s = Statistics::make( - [0.5, 0.75, 0.625, 0.375], - ); - expect($s->mean())->toEqual(0.5625); - - $s = Statistics::make( - [3.5, 4.0, 5.25], - ); - expect($s->mean())->toEqual(4.25); -}); - -it('can valuesToString', function (): void { - $s = Statistics::make( - [1, 2, 3, 4, 4], - ); - expect($s->valuesToString(false))->toEqual('1,2,3,4,4'); - expect($s->valuesToString(3))->toEqual('1,2,3'); -}); - -it('calculates Population standard deviation', function (): void { - expect( - Statistics::make( - [1.5, 2.5, 2.5, 2.75, 3.25, 4.75], - )->pstdev(), - )->toEqual(0.986893273527251); - expect( - Statistics::make([1, 2, 4, 5, 8])->pstdev(4), - )->toEqual(2.4495); - expect( - fn(): float => Statistics::make([])->pstdev(), - )->toThrow(InvalidDataInputException::class); - expect( - Statistics::make([1])->pstdev(), - )->toEqual(0); - expect( - Statistics::make([1, 2, 3, 3])->pstdev(7), - )->toEqual(0.8291562); -}); -it('calculates Sample standard deviation', function (): void { - expect( - Statistics::make([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])->stdev(), - )->toEqual(1.0810874155219827); - expect( - Statistics::make([1, 2, 2, 4, 6])->stdev(), - )->toEqual(2); - expect( - Statistics::make([1, 2, 4, 5, 8])->stdev(4), - )->toEqual(2.7386); - expect( - fn(): float => Statistics::make([])->stdev(), - )->toThrow(InvalidDataInputException::class); - expect( - fn(): float => Statistics::make([1])->stdev(), - )->toThrow(InvalidDataInputException::class); -}); - -it('calculates variance', function (): void { - expect( - Statistics::make([2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5])->variance(), - )->toEqual(1.3720238095238095); -}); - -it('calculates pvariance', function (): void { - expect( - Statistics::make([0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25])->pvariance(), - )->toEqual(1.25); - expect( - Statistics::make([1, 2, 3, 3])->pvariance(), - )->toEqual(0.6875); -}); - -it('calculates geometric mean', function (): void { - expect( - Statistics::make([54, 24, 36])->geometricMean(2), - )->toEqual(36); - expect( - Statistics::make([4, 8, 3, 9, 17])->geometricMean(2), - )->toEqual(6.81); - expect( - fn(): float => Statistics::make([])->geometricMean(), - )->toThrow(InvalidDataInputException::class); -}); - -it('calculates harmonic mean', function (): void { - expect( - Statistics::make([40, 60])->harmonicMean(1), - )->toEqual(48.0); - expect( - fn(): float => Statistics::make([])->harmonicMean(), - )->toThrow(InvalidDataInputException::class); -}); - -it('can distinct numeric array', function (): void { - expect(Statistics::make([1, 2, 3])->numericalArray())->toEqual([1, 2, 3]); - expect(Statistics::make([1, '2', 3])->numericalArray())->toEqual([1, '2', 3]); - expect(Statistics::make([])->numericalArray())->toEqual([]); - expect(fn(): array => Statistics::make([1, 'some string', 3])->numericalArray())->toThrow(InvalidDataInputException::class); -}); +use PHPUnit\Framework\TestCase; + +class StatisticTest extends TestCase +{ + public function test_can_calculate_statistics(): void + { + $s = Statistics::make( + [98, 90, 70, 18, 92, 92, 55, 83, 45, 95, 88, 76], + ); + $this->assertEquals(12, $s->count()); + $this->assertEquals(85.5, $s->median()); + $this->assertEquals(58.75, $s->firstQuartile()); + $this->assertEquals(92, $s->thirdQuartile()); + $this->assertEquals(33.25, $s->interquartileRange()); + $this->assertCount(12, $s->originalArray()); + + $s = Statistics::make( + [98, 90, 70, 18, 92, 92, 55, 83, 45, 95, 88], + ); + $this->assertEquals(11, $s->count()); + $this->assertEquals(88, $s->median()); + $this->assertEquals(55, $s->firstQuartile()); + $this->assertEquals(92, $s->thirdQuartile()); + $this->assertEquals(37, $s->interquartileRange()); + $this->assertCount(11, $s->originalArray()); + } + + public function test_can_calculate_statistics_again(): void + { + $s = Statistics::make( + [3, 5, 4, 7, 5, 2], + ); + $this->assertEquals(6, $s->count()); + $this->assertEquals(13 / 3, $s->mean()); + $this->assertEquals(4.5, $s->median()); + $this->assertEquals(5, $s->mode()); + $this->assertEquals(2, $s->min()); + $this->assertEquals(7, $s->max()); + $this->assertEquals(5, $s->range()); + $this->assertEquals(2.75, $s->firstQuartile()); + $this->assertEquals(5.5, $s->thirdQuartile()); + } + + public function test_can_calculate_statistics_again_and_again(): void + { + $s = Statistics::make( + [13, 18, 13, 14, 13, 16, 14, 21, 13], + ); + $this->assertEquals(9, $s->count()); + $this->assertEquals(15, $s->mean()); + $this->assertEquals(14, $s->median()); + $this->assertEquals(13, $s->mode()); + $this->assertEquals(13, $s->min()); + $this->assertEquals(21, $s->max()); + $this->assertEquals(8, $s->range()); + $this->assertEquals(13, $s->firstQuartile()); + $this->assertEquals(17, $s->thirdQuartile()); + + $s = Statistics::make( + [1, 2, 4, 7], + ); + $this->assertEquals(4, $s->count()); + $this->assertEquals(3.5, $s->mean()); + $this->assertEquals(3, $s->median()); + $this->assertNull($s->mode()); + $this->assertEquals(1, $s->min()); + $this->assertEquals(7, $s->max()); + $this->assertEquals(6, $s->range()); + } + + public function test_can_strip_zeros(): void + { + $s = Statistics::make( + [3, 5, 0, 0.1, 4, 7, 5, 2], + )->stripZeroes(); + $this->assertEquals(7, $s->count()); + } + + public function test_can_calculate_mean(): void + { + $s = Statistics::make( + [3, 5, 4, 7, 5, 2], + ); + $this->assertEquals(6, $s->count()); + $this->assertEquals(13 / 3, $s->mean()); + + $s = Statistics::make([]); + $this->assertEquals(0, $s->count()); + $this->expectException(InvalidDataInputException::class); + $s->mean(); + } + + public function test_can_calculate_mean_again(): void + { + $s = Statistics::make([1, 2, 3, 4, 4]); + $this->assertEquals(2.8, $s->mean()); + + $s = Statistics::make([-1.0, 2.5, 3.25, 5.75]); + $this->assertEquals(2.625, $s->mean()); + + $s = Statistics::make([0.5, 0.75, 0.625, 0.375]); + $this->assertEquals(0.5625, $s->mean()); + + $s = Statistics::make([3.5, 4.0, 5.25]); + $this->assertEquals(4.25, $s->mean()); + } + + public function test_can_values_to_string(): void + { + $s = Statistics::make([1, 2, 3, 4, 4]); + $this->assertEquals('1,2,3,4,4', $s->valuesToString(false)); + $this->assertEquals('1,2,3', $s->valuesToString(3)); + } + + public function test_calculates_population_standard_deviation(): void + { + $this->assertEquals( + 0.986893273527251, + Statistics::make([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])->pstdev(), + ); + $this->assertEquals( + 2.4495, + Statistics::make([1, 2, 4, 5, 8])->pstdev(4), + ); + $this->assertEquals(0, Statistics::make([1])->pstdev()); + $this->assertEquals(0.8291562, Statistics::make([1, 2, 3, 3])->pstdev(7)); + } + + public function test_calculates_population_standard_deviation_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Statistics::make([])->pstdev(); + } + + public function test_calculates_sample_standard_deviation(): void + { + $this->assertEquals( + 1.0810874155219827, + Statistics::make([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])->stdev(), + ); + $this->assertEquals(2, Statistics::make([1, 2, 2, 4, 6])->stdev()); + $this->assertEquals(2.7386, Statistics::make([1, 2, 4, 5, 8])->stdev(4)); + } + + public function test_calculates_sample_standard_deviation_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Statistics::make([])->stdev(); + } + + public function test_calculates_sample_standard_deviation_with_single_element(): void + { + $this->expectException(InvalidDataInputException::class); + Statistics::make([1])->stdev(); + } + + public function test_calculates_variance(): void + { + $this->assertEquals( + 1.3720238095238095, + Statistics::make([2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5])->variance(), + ); + } + + public function test_calculates_pvariance(): void + { + $this->assertEquals( + 1.25, + Statistics::make([0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25])->pvariance(), + ); + $this->assertEquals(0.6875, Statistics::make([1, 2, 3, 3])->pvariance()); + } + + public function test_calculates_geometric_mean(): void + { + $this->assertEquals(36, Statistics::make([54, 24, 36])->geometricMean(2)); + $this->assertEquals(6.81, Statistics::make([4, 8, 3, 9, 17])->geometricMean(2)); + } + + public function test_calculates_geometric_mean_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Statistics::make([])->geometricMean(); + } + + public function test_calculates_harmonic_mean(): void + { + $this->assertEquals(48.0, Statistics::make([40, 60])->harmonicMean(1)); + } + + public function test_calculates_harmonic_mean_with_empty_array(): void + { + $this->expectException(InvalidDataInputException::class); + Statistics::make([])->harmonicMean(); + } + + public function test_can_distinct_numeric_array(): void + { + $this->assertEquals([1, 2, 3], Statistics::make([1, 2, 3])->numericalArray()); + $this->assertEquals([1, '2', 3], Statistics::make([1, '2', 3])->numericalArray()); + $this->assertEquals([], Statistics::make([])->numericalArray()); + $this->expectException(InvalidDataInputException::class); + Statistics::make([1, 'some string', 3])->numericalArray(); + } +} From 4ffce5e7182a278d667b79d3507915b395e15efc Mon Sep 17 00:00:00 2001 From: Roberto Butti Date: Thu, 19 Feb 2026 23:30:06 +0100 Subject: [PATCH 3/3] Update run-tests.yml --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index be27980..e88d975 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -25,7 +25,7 @@ jobs: with: php-version: ${{ matrix.php }} extensions: dom, curl, libxml, mbstring, zip, pcntl, bcmath, soap, intl, iconv, fileinfo - coverage: none + coverage: xdebug - name: Setup problem matchers run: |