Skip to content

Commit

Permalink
Enhancement: Implement MersenneTwisterIntegerGenerator
Browse files Browse the repository at this point in the history
  • Loading branch information
localheinz committed Sep 19, 2023
1 parent 6eda5c1 commit 6849a57
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/Faker/Generator/IntegerGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Faker\Generator;

/**
* @experimental
*/
interface IntegerGenerator
{
/**
* Returns an integer.
*/
public function integer(): int;

/**
* Returns an integer that is between minimum and maximum (inclusive).
*
* @throws \InvalidArgumentException
*/
public function integerBetween(int $minimum, int $maximum): int;
}
44 changes: 44 additions & 0 deletions src/Faker/Generator/MersenneTwisterIntegerGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Faker\Generator;

/**
* @experimental
*/
final class MersenneTwisterIntegerGenerator implements SeedableIntegerGenerator
{
public function integer(): int
{
return mt_rand();
}

public function integerBetween(int $minimum, int $maximum): int
{
if ($minimum > $maximum) {
throw new \InvalidArgumentException(sprintf(
'Minimum value %d should not be greater than the maximum value %d.',
$minimum,
$maximum,
));
}

$largestInteger = mt_getrandmax();

if ($maximum > $largestInteger) {
throw new \InvalidArgumentException(sprintf(
'Maximum value %d should not be greater than %d.',
$maximum,
$largestInteger,
));
}

return mt_rand($minimum, $maximum);
}

public function seed(int $value): void
{
mt_srand($value);
}
}
16 changes: 16 additions & 0 deletions src/Faker/Generator/SeedableIntegerGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Faker\Generator;

/**
* @experimental
*/
interface SeedableIntegerGenerator extends IntegerGenerator
{
/**
* Seeds the number generator.
*/
public function seed(int $value): void;
}
106 changes: 106 additions & 0 deletions test/Faker/Generator/MersenneTwisterIntegerGeneratorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

declare(strict_types=1);

namespace Faker\Test\Generator;

use Faker\Generator;
use PHPUnit\Framework;

/**
* @covers \Faker\Generator\MersenneTwisterIntegerGenerator
*/
final class MersenneTwisterIntegerGeneratorTest extends Framework\TestCase
{
public function testIntegerBetweenReturnsIntegerBetweenMinAndMax(): void
{
$min = 3;
$max = 1000;

$generator = new Generator\MersenneTwisterIntegerGenerator();

$values = self::generateValues(
static function () use ($generator, $min, $max): int {
return $generator->integerBetween(
$min,
$max,
);
},
100,
);

self::assertValuesSatisfySpecification(
static function (int $value) use ($min, $max): bool {
return $value >= $min && $value <= $max;
},
$values,
);
}

public function testSeedSeedsIntegerGenerator(): void
{
$value = 9001;

$generator = new Generator\MersenneTwisterIntegerGenerator();

$generator->seed($value);

$first = self::generateValues(
static function () use ($generator): int {
return $generator->integer();
},
100,
);

$generator->seed($value);

$second = self::generateValues(
static function () use ($generator): int {
return $generator->integer();
},
100,
);

self::assertSame($first, $second);
}

/**
* @param \Closure(): mixed $generator
*
* @throws \InvalidArgumentException
*
* @return list<mixed>
*/
private static function generateValues(
\Closure $generator,
int $count
): array {
if ($count < 1) {
throw new \InvalidArgumentException(sprintf(
'Count needs to be greater than 0, got %d instead',
$count,
));
}

return array_map(static function () use ($generator) {
return $generator();
}, range(0, $count - 1));
}

/**
* @param \Closure(mixed):bool $specification
* @param list<mixed> $values
*
* @throws \InvalidArgumentException
*/
private static function assertValuesSatisfySpecification(
\Closure $specification,
array $values
): void {
$invalidValues = array_filter($values, static function ($value) use ($specification): bool {
return $specification($value) === false;
});

self::assertCount(0, $invalidValues, 'Failed asserting that all generated values satisfy the specification.');
}
}

0 comments on commit 6849a57

Please sign in to comment.