implement JS password scoring, td_stock approve logicc
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZxcvbnPhp\Math;
|
||||
|
||||
use ZxcvbnPhp\Math\Impl\BinomialProviderPhp73Gmp;
|
||||
use ZxcvbnPhp\Math\Impl\BinomialProviderFloat64;
|
||||
use ZxcvbnPhp\Math\Impl\BinomialProviderInt64;
|
||||
|
||||
class Binomial
|
||||
{
|
||||
private static $provider = null;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
throw new \LogicException(__CLASS__ . " is static");
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate binomial coefficient (n choose k).
|
||||
*
|
||||
* @param int $n
|
||||
* @param int $k
|
||||
* @return float
|
||||
*/
|
||||
public static function binom(int $n, int $k): float
|
||||
{
|
||||
return self::getProvider()->binom($n, $k);
|
||||
}
|
||||
|
||||
public static function getProvider(): BinomialProvider
|
||||
{
|
||||
if (self::$provider === null) {
|
||||
self::$provider = self::initProvider();
|
||||
}
|
||||
|
||||
return self::$provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getUsableProviderClasses(): array
|
||||
{
|
||||
// In order of priority. The first provider with a value of true will be used.
|
||||
$possibleProviderClasses = [
|
||||
BinomialProviderPhp73Gmp::class => function_exists('gmp_binomial'),
|
||||
BinomialProviderInt64::class => PHP_INT_SIZE >= 8,
|
||||
BinomialProviderFloat64::class => PHP_FLOAT_DIG >= 15,
|
||||
];
|
||||
|
||||
$possibleProviderClasses = array_filter($possibleProviderClasses);
|
||||
|
||||
return array_keys($possibleProviderClasses);
|
||||
}
|
||||
|
||||
private static function initProvider(): BinomialProvider
|
||||
{
|
||||
$providerClasses = self::getUsableProviderClasses();
|
||||
|
||||
if (!$providerClasses) {
|
||||
throw new \LogicException("No valid providers");
|
||||
}
|
||||
|
||||
$bestProviderClass = reset($providerClasses);
|
||||
|
||||
return new $bestProviderClass();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZxcvbnPhp\Math;
|
||||
|
||||
interface BinomialProvider
|
||||
{
|
||||
/**
|
||||
* Calculate binomial coefficient (n choose k).
|
||||
*
|
||||
* @param int $n
|
||||
* @param int $k
|
||||
* @return float
|
||||
*/
|
||||
public function binom(int $n, int $k): float;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZxcvbnPhp\Math\Impl;
|
||||
|
||||
use ZxcvbnPhp\Math\BinomialProvider;
|
||||
|
||||
abstract class AbstractBinomialProvider implements BinomialProvider
|
||||
{
|
||||
public function binom(int $n, int $k): float
|
||||
{
|
||||
if ($k < 0 || $n < 0) {
|
||||
throw new \DomainException("n and k must be non-negative");
|
||||
}
|
||||
|
||||
if ($k > $n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// $k and $n - $k will always produce the same value, so use smaller of the two
|
||||
$k = min($k, $n - $k);
|
||||
|
||||
return $this->calculate($n, $k);
|
||||
}
|
||||
|
||||
abstract protected function calculate(int $n, int $k): float;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZxcvbnPhp\Math\Impl;
|
||||
|
||||
abstract class AbstractBinomialProviderWithFallback extends AbstractBinomialProvider
|
||||
{
|
||||
/**
|
||||
* @var AbstractBinomialProvider|null
|
||||
*/
|
||||
private $fallback = null;
|
||||
|
||||
protected function calculate(int $n, int $k): float
|
||||
{
|
||||
return $this->tryCalculate($n, $k) ?? $this->getFallbackProvider()->calculate($n, $k);
|
||||
}
|
||||
|
||||
abstract protected function tryCalculate(int $n, int $k): ?float;
|
||||
|
||||
abstract protected function initFallbackProvider(): AbstractBinomialProvider;
|
||||
|
||||
protected function getFallbackProvider(): AbstractBinomialProvider
|
||||
{
|
||||
if ($this->fallback === null) {
|
||||
$this->fallback = $this->initFallbackProvider();
|
||||
}
|
||||
|
||||
return $this->fallback;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZxcvbnPhp\Math\Impl;
|
||||
|
||||
class BinomialProviderFloat64 extends AbstractBinomialProvider
|
||||
{
|
||||
protected function calculate(int $n, int $k): float
|
||||
{
|
||||
$c = 1.0;
|
||||
|
||||
for ($i = 1; $i <= $k; $i++, $n--) {
|
||||
// We're aiming for $c * $n / $i, but the $c * $n part could cause us to lose precision, so use $c / $i * $n instead. The caveat
|
||||
// here is that in order to get a precise answer, we need to minimize the chances of going above ~2^52. This is mitigated
|
||||
// somewhat by dealing with whole part and the remainder separately, but it's not perfect and could overflow in practice, which
|
||||
// would result in a loss of precision.
|
||||
$c = floor($c / $i) * $n + floor(fmod($c, $i) * $n / $i);
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZxcvbnPhp\Math\Impl;
|
||||
|
||||
use TypeError;
|
||||
|
||||
class BinomialProviderInt64 extends AbstractBinomialProviderWithFallback
|
||||
{
|
||||
protected function initFallbackProvider(): AbstractBinomialProvider
|
||||
{
|
||||
return new BinomialProviderFloat64();
|
||||
}
|
||||
|
||||
protected function tryCalculate(int $n, int $k): ?float
|
||||
{
|
||||
try {
|
||||
$c = 1;
|
||||
|
||||
for ($i = 1; $i <= $k; $i++, $n--) {
|
||||
// We're aiming for $c * $n / $i, but the $c * $n part could overflow, so use $c / $i * $n instead. The caveat here is that in
|
||||
// order to get a precise answer, we need to avoid floats, which means we need to deal with whole part and the remainder
|
||||
// separately.
|
||||
$c = intdiv($c, $i) * $n + intdiv($c % $i * $n, $i);
|
||||
}
|
||||
|
||||
return (float)$c;
|
||||
} catch (TypeError $ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZxcvbnPhp\Math\Impl;
|
||||
|
||||
class BinomialProviderPhp73Gmp extends AbstractBinomialProvider
|
||||
{
|
||||
/**
|
||||
* @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection
|
||||
* @noinspection PhpComposerExtensionStubsInspection
|
||||
*/
|
||||
protected function calculate(int $n, int $k): float
|
||||
{
|
||||
return (float)gmp_strval(gmp_binomial($n, $k));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user