123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787 |
- <?php
- namespace MathPHP\Tests\Probability;
- use MathPHP\Probability\Combinatorics;
- use MathPHP\Exception;
- class CombinatoricsTest extends \PHPUnit\Framework\TestCase
- {
- /**
- * @test factorial
- * @dataProvider dataProviderForFactorialPermutations
- * @param int $n
- * @param float $expected
- * @throws \Exception
- */
- public function testFactorial(int $n, float $expected)
- {
- // When
- $factorial = Combinatorics::factorial($n);
- // Then
- $this->assertEquals($expected, $factorial);
- }
- /**
- * @test factorial bounds exception
- * @throws \Exception
- */
- public function testFactorialBoundsException()
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::factorial(-1);
- }
- /**
- * @test doubleFactorial
- * @dataProvider dataProviderForDoubleFactorial
- * @param int $n
- * @param float $expected
- * @throws \Exception
- */
- public function testDoubleFactorial(int $n, float $expected)
- {
- // When
- $doubleFactorial = Combinatorics::doubleFactorial($n);
- // Then
- $this->assertEquals($expected, $doubleFactorial);
- }
- /**
- * @return array [n, doubleFactorial]
- */
- public function dataProviderForDoubleFactorial(): array
- {
- return [
- [0, 1],
- [1, 1],
- [2, 2],
- [3, 3],
- [4, 8],
- [5, 15],
- [6, 48],
- [7, 105],
- [8, 384],
- [9, 945],
- [10, 3840],
- [11, 10395],
- [12, 46080],
- [13, 135135],
- [14, 645120],
- ];
- }
- /**
- * @test doubleFactorial n less than zero
- * @throws \Exception
- */
- public function testDoubleFactorialExceptionNLessThanZero()
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::doubleFactorial(-1);
- }
- /**
- * @test risingFactorial
- * @dataProvider dataProviderForRisingFactorial
- * @param int $x
- * @param int $n
- * @param float $expected
- * @throws \Exception
- */
- public function testRisingFactorial(int $x, int $n, float $expected)
- {
- // When
- $risingFactorial = Combinatorics::risingFactorial($x, $n);
- // Then
- $this->assertEquals($expected, $risingFactorial);
- }
- /**
- * @return array [x, n, risingFactorial]
- */
- public function dataProviderForRisingFactorial(): array
- {
- return [
- [5, 0, 1],
- [5, 1, 5],
- [5, 2, 30],
- [5, 3, 210],
- [4, 4, 840],
- [3, 5, 2520],
- [2, 6, 5040],
- ];
- }
- /**
- * @test risingFactorial n less than zero
- * @throws \Exception
- */
- public function testRisingFactorialExceptionNLessThanZero()
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::risingFactorial(5, -1);
- }
- /**
- * @test fallingFactorial
- * @dataProvider dataProviderForFallingFactorial
- * @param int $x
- * @param int $n
- * @param float $expected
- * @throws \Exception
- */
- public function testFallingFactorial(int $x, int $n, float $expected)
- {
- // When
- $fallingFactorial = Combinatorics::fallingFactorial($x, $n);
- // Then
- $this->assertEquals($expected, $fallingFactorial);
- }
- /**
- * @return array [x, n, fallingFactorial]
- */
- public function dataProviderForFallingFactorial(): array
- {
- return [
- [5, 0, 1],
- [5, 1, 5],
- [5, 2, 20],
- [5, 3, 60],
- [5, 4, 120],
- [5, 5, 120],
- [5, 6, 0],
- [4, 3, 24],
- [4, 4, 24],
- [4, 5, 0],
- [8, 5, 6720],
- [3, 5, 0],
- [2, 6, 0],
- ];
- }
- /**
- * @test fallingFactorial n less than zero
- * @throws \Exception
- */
- public function testFallingFactorialExceptionNLessThanZero()
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::fallingFactorial(5, -1);
- }
- /**
- * @test subfactorial
- * @dataProvider dataProviderForSubfactorial
- * @param int $n
- * @param float $!n
- * @throws \Exception
- */
- public function testSubfactorial(int $n, float $!n)
- {
- // When
- $subfactorial = Combinatorics::subfactorial($n);
- // Then
- $this->assertEqualsWithDelta($!n, $subfactorial, 0.000000001);
- }
- /**
- * @return array [n, !n]
- */
- public function dataProviderForSubfactorial(): array
- {
- return [
- [0, 1],
- [1, 0],
- [2, 1],
- [3, 2],
- [4, 9],
- [5, 44],
- [6, 265],
- [7, 1854],
- [8, 14833],
- [9, 133496],
- [10, 1334961],
- ];
- }
- /**
- * @test subfactorial n less than zero
- * @throws \Exception
- */
- public function testSubactorialExceptionNLessThanZero()
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::subfactorial(-1);
- }
- /**
- * @test permutations
- * @dataProvider dataProviderForFactorialPermutations
- * @param int $n
- * @param float $expected
- * @throws \Exception
- */
- public function testPermutations(int $n, float $expected)
- {
- // When
- $permutations = Combinatorics::permutations($n);
- // Then
- $this->assertEquals($expected, $permutations);
- }
- /**
- * @test permutations bounds exception
- * @throws \Exception
- */
- public function testPermutationsBoundsException()
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::permutations(-1);
- }
- /**
- * @return array [n, permutations]
- */
- public function dataProviderForFactorialPermutations(): array
- {
- return [
- [1, 1],
- [2, 2],
- [3, 6],
- [4, 24],
- [5, 120],
- [6, 720],
- [7, 5040],
- [8, 40320],
- [9, 362880],
- [10, 3628800],
- [11, 39916800],
- [12, 479001600],
- [13, 6227020800],
- [14, 87178291200],
- [15, 1307674368000],
- [16, 20922789888000],
- [17, 355687428096000],
- [18, 6402373705728000],
- [19, 121645100408832000],
- [20, 2432902008176640000],
- ];
- }
- /**
- * @test permutations choose k
- * @dataProvider dataProviderForPermutationsChooseK
- * @param int $n
- * @param int $k
- * @param float $nPk
- * @throws \Exception
- */
- public function testPermutationsChooseK(int $n, int $k, float $nPk)
- {
- // When
- $permutations = Combinatorics::permutations($n, $k);
- // Then
- $this->assertEquals($nPk, $permutations);
- }
- /**
- * @return array [n, k, permutations]
- */
- public function dataProviderForPermutationsChooseK(): array
- {
- return [
- [10, 0, 1],
- [10, 1, 10],
- [10, 2, 90],
- [10, 3, 720],
- [10, 4, 5040],
- [10, 5, 30240],
- [10, 6, 151200],
- [10, 7, 604800],
- [10, 8, 1814400],
- [10, 9, 3628800],
- [10, 10, 3628800],
- [ 5, 3, 60],
- [ 6, 4, 360],
- [16, 3, 3360],
- [20, 3, 6840],
- [23, 5, 4037880],
- ];
- }
- /**
- * @test permutations choose k bounds exception
- * @throws \Exception
- */
- public function testPermutationsChooseKBoundsException()
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::permutations(-1, 3);
- }
- /**
- * @test permutations choose k - k greater than n exception
- * @throws \Exception
- */
- public function testPermutationsChooseKKGreaterThanNException()
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::permutations(3, 4);
- }
- /**
- * @test combinations
- * @dataProvider dataProviderForCombinations
- * @param int $n
- * @param int $r
- * @param float $expected
- * @throws \Exception
- */
- public function testCombinations(int $n, int $r, float $expected)
- {
- // When
- $combinations = Combinatorics::combinations($n, $r);
- // Then
- $this->assertEquals($expected, $combinations);
- }
- /**
- * Test data produced with Python scipy.special.comb(n, k, exact=True, repetition=False)
- * @return array [n, r, combinations]
- */
- public function dataProviderForCombinations(): array
- {
- return [
- [10, 0, 1],
- [10, 1, 10],
- [10, 2, 45],
- [10, 3, 120],
- [10, 4, 210],
- [10, 5, 252],
- [10, 6, 210],
- [10, 7, 120],
- [10, 8, 45],
- [10, 9, 10],
- [10, 10, 1],
- [ 5, 3, 10],
- [ 6, 4, 15],
- [16, 3, 560],
- [20, 3, 1140],
- [35, 20, 3247943160],
- [35, 25, 183579396],
- ];
- }
- /**
- * @test combinations with large floating point overflow result
- * @dataProvider dataProviderForCombinationsWithLargeFloatingPointOverflowResult
- * @param int $n
- * @param int $r
- * @param float $expected
- * @param float ε
- * @throws \Exception
- */
- public function testCombinationsWithLargeFloatingPointOverflowResult(int $n, int $r, float $expected, float $ε)
- {
- // When
- $combinations = Combinatorics::combinations($n, $r);
- // Then
- $this->assertEqualsWithDelta($expected, $combinations, $ε);
- }
- /**
- * Test data produced with Python scipy.special.comb(n, k, exact=False, repetition=False)
- * @return array [n, r, combinations, ε]
- */
- public function dataProviderForCombinationsWithLargeFloatingPointOverflowResult(): array
- {
- return [
- [70, 30, 5.534774005814348e+19, 0],
- [100, 50, 1.0089134454556415e+29, 1e14],
- ];
- }
- /**
- * @test combinations n less than zero
- * @throws \Exception
- */
- public function testCombinationsExceptionNLessThanZero()
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::combinations(-1, 2);
- }
- /**
- * @test combinations r larger than n
- * @throws \Exception
- */
- public function testCombinationsExceptionRLargerThanN()
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::combinations(1, 2);
- }
- /**
- * @test combinations with repetition
- * @dataProvider dataProviderForCombinationsWithRepetition
- * @param int $n
- * @param int $r
- * @param float $expected
- * @throws \Exception
- */
- public function testCombinationsWithRepetition(int $n, int $r, float $expected)
- {
- // When
- $combinations = Combinatorics::combinations($n, $r, Combinatorics::REPETITION);
- // Then
- $this->assertEquals($expected, $combinations);
- }
- /**
- * @test combinations with repetition bounds exception
- * @throws \Exception
- */
- public function testCombinationsWithRepetitionBoundsException()
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::combinations(-1, 3, Combinatorics::REPETITION);
- }
- /**
- * Test data produced with Python scipy.special.comb(n, k, exact=True, repetition=True)
- * @return array [n, r, combinations]
- */
- public function dataProviderForCombinationsWithRepetition(): array
- {
- return [
- [10, 0, 1],
- [10, 1, 10],
- [10, 2, 55],
- [10, 3, 220],
- [10, 4, 715],
- [10, 5, 2002],
- [10, 6, 5005],
- [10, 7, 11440],
- [10, 8, 24310],
- [10, 9, 48620],
- [10, 10, 92378],
- [5, 3, 35],
- [5, 7, 330],
- [6, 4, 126],
- [16, 3, 816],
- [20, 3, 1540],
- [21, 20, 137846528820],
- [35, 25, 30284005485024837],
- ];
- }
- /**
- * @test combinations with repetition with large floating point overflow result
- * @dataProvider dataProviderForCombinationsWithRepetitionWithLargeFloatingPointOverflowResult
- * @param int $n
- * @param int $r
- * @param float $expected
- * @param float ε
- * @throws \Exception
- */
- public function testCombinationsWithRepetitionWithLargeFloatingPointOverflowResult(int $n, int $r, float $expected, float $ε)
- {
- // When
- $combinations = Combinatorics::combinations($n, $r, Combinatorics::REPETITION);
- // Then
- $this->assertEqualsWithDelta($expected, $combinations, $ε);
- }
- /**
- * Test data produced with Python scipy.special.comb(n, k, exact=False, repetition=True)
- * @return array [n, r, combinations, ε]
- */
- public function dataProviderForCombinationsWithRepetitionWithLargeFloatingPointOverflowResult(): array
- {
- return [
- [70, 30, 2.0560637875127662e+25, 1e10],
- [100, 50, 1.341910727315462e+40, 1e25],
- ];
- }
- /**
- * @test centralBinomialCoefficient
- * @dataProvider dataProviderForCentralBinomialCoefficient
- * @param int $n
- * @param float $!n
- * @throws \Exception
- */
- public function testCentralBinomialCoefficient(int $n, float $!n)
- {
- // When
- $binomial = Combinatorics::centralBinomialCoefficient($n);
- // Then
- $this->assertEqualsWithDelta($!n, $binomial, 0.000000001);
- }
- /**
- * @return array [n, !n]
- */
- public function dataProviderForCentralBinomialCoefficient(): array
- {
- return [
- [0, 1],
- [1, 2],
- [2, 6],
- [3, 20],
- [4, 70],
- [5, 252],
- [6, 924],
- [7, 3432],
- [8, 12870],
- [9, 48620],
- [10, 184756],
- ];
- }
- /**
- * @test centralBinomialCoefficient n less than zero
- * @throws \Exception
- */
- public function testCentralBinomialCoefficientExceptionNLessThanZero()
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::centralBinomialCoefficient(-1);
- }
- /**
- * @test catalanNumber
- * @dataProvider dataProviderForCatalanNumber
- * @param int $n
- * @param float $!n
- * @throws \Exception
- */
- public function testCatalanNumber(int $n, float $!n)
- {
- // When
- $catalanNumber = Combinatorics::catalanNumber($n);
- // Then
- $this->assertEqualsWithDelta($!n, $catalanNumber, 0.000000001);
- }
- /**
- * @return array [n, !n]
- */
- public function dataProviderForCatalanNumber(): array
- {
- return [
- [0, 1],
- [1, 1],
- [2, 2],
- [3, 5],
- [4, 14],
- [5, 42],
- [6, 132],
- [7, 429],
- [8, 1430],
- [9, 4862],
- [10, 16796],
- [11, 58786],
- ];
- }
- /**
- * @test catalanNumber n less than zero
- * @throws \Exception
- */
- public function testCatalanNumberExceptionNLessThanZero()
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::catalanNumber(-1);
- }
- /**
- * @test multinomial
- * @dataProvider dataProviderForMultinomialTheorem
- * @param array $groups
- * @param int $expected
- * @throws \Exception
- */
- public function testMultinomialTheorem(array $groups, int $expected)
- {
- // When
- $divisions = Combinatorics::multinomial($groups);
- // Then
- $this->assertEquals($expected, $divisions);
- }
- /**
- * @return array [groups, divisions]
- */
- public function dataProviderForMultinomialTheorem(): array
- {
- return [
- [[2, 0, 1], 3],
- [[1, 1, 1], 6],
- [[ 5, 2, 3 ], 2520],
- [[ 5, 5 ], 252],
- [[ 1, 4, 4, 2 ], 34650],
- [[3, 4, 5, 8], 3491888400],
- ];
- }
- /**
- * @test lahNumber
- * @dataProvider dataProviderForLahNumber
- * @param int $k
- * @param int $n
- * @param float $expected
- * @throws \Exception
- */
- public function testLahNumber(int $k, int $n, float $expected)
- {
- // When
- $lahNumber = Combinatorics::lahNumber($k, $n);
- // Then
- $this->assertEquals($expected, $lahNumber);
- }
- /**
- * @return array [k, n, lah]
- */
- public function dataProviderForLahNumber(): array
- {
- return [
- [1, 1, 1],
- [2, 1, 2],
- [2, 2, 1],
- [3, 1, 6],
- [3, 2, 6],
- [3, 3, 1],
- [4, 1, 24],
- [4, 2, 36],
- [4, 3, 12],
- [4, 4, 1],
- [5, 1, 120],
- [5, 2, 240],
- [5, 3, 120],
- [5, 4, 20],
- [5, 5, 1],
- [6, 1, 720],
- [6, 2, 1800],
- [6, 3, 1200],
- [6, 4, 300],
- [6, 5, 30],
- [6, 6, 1],
- [12, 1, 479001600],
- [12, 2, 2634508800],
- [12, 3, 4390848000],
- [12, 4, 3293136000],
- [12, 5, 1317254400],
- [12, 6, 307359360],
- [12, 7, 43908480],
- [12, 8, 3920400],
- [12, 9, 217800],
- [12, 10, 7260],
- [12, 11, 132],
- [12, 12, 1],
- ];
- }
- /**
- * @test lahNumber n or k less than one
- * @dataProvider dataProviderForLahNumberExceptionNOrKLessThanOne
- * @param int $n
- * @param int $k
- * @throws \Exception
- */
- public function testLahNumberExceptionNOrKLessThanOne(int $n, int $k)
- {
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::lahNumber($n, $k);
- }
- /**
- * @return array [n, k]
- */
- public function dataProviderForLahNumberExceptionNOrKLessThanOne(): array
- {
- return [
- [-1, 2],
- [2, -2],
- [-3, -3],
- ];
- }
- /**
- * @test lahNumber n less than k
- * @throws \Exception
- */
- public function testLahNumberExceptionNLessThanK()
- {
- // Given
- $k = 4;
- $n = 2;
- // Then
- $this->expectException(Exception\OutOfBoundsException::class);
- // When
- Combinatorics::lahNumber($n, $k);
- }
- }
|