Rewrote Base62 algorithm to avoid errors with very big numbers.

This commit is contained in:
Pachira 2024-12-22 02:02:53 +00:00
parent 469391f9b6
commit 004156712b
3 changed files with 18 additions and 38 deletions

View file

@ -1 +1 @@
0.2410.630140
0.2410.830201

View file

@ -1,7 +1,7 @@
<?php
// IntegerBaseConverter.php
// Created: 2024-07-31
// Updated: 2024-08-04
// Updated: 2024-12-22
namespace Index;
@ -11,7 +11,7 @@ use InvalidArgumentException;
* Provides methods for encoding integers in different bases.
*/
class IntegerBaseConverter {
private int $maxBase;
private int $base;
/**
* @param string $characterSet Specifies the provided character set, must be a string consisting out of at least two unique characters.
@ -27,38 +27,27 @@ class IntegerBaseConverter {
if(XString::countUnique($characterSet) !== $length)
throw new InvalidArgumentException('$characterSet must contain a set of entirely unique characters');
$this->maxBase = $length;
$this->base = $length;
}
/**
* Converts a positive base10 integer to another base.
*
* @param int $integer The integer to encode.
* @param int $toBase Target base, 0 for the base of the provided character set.
* @return string The encoded data, as a string.
*/
public function encode(int $integer, int $toBase = 0): string {
public function encode(int $integer): string {
if($integer < 0)
throw new InvalidArgumentException('$integer contains a negative value, which cannot be represented');
if($toBase === 0)
$toBase = $this->maxBase;
else {
if($toBase < 2)
throw new InvalidArgumentException('lowest supported value for $toBase is 2');
if($toBase > $this->maxBase)
throw new InvalidArgumentException('specified $toBase value is greater than the amount of characters in the provided character set');
}
if($integer === 0)
return '';
return $this->characterSet[0];
$output = '';
for($i = floor(log10($integer) / log10($toBase)); $i >= 0; --$i) {
$exp = $toBase ** $i;
$index = (int)floor($integer / $exp);
$output .= $this->characterSet[$index];
$integer -= $index * $exp;
while($integer > 0) {
$remainder = $integer % $this->base;
$output = $this->characterSet[$remainder] . $output;
$integer = intdiv($integer, $this->base);
}
return $output;
@ -68,33 +57,22 @@ class IntegerBaseConverter {
* Converts another base encoded integer to a base10 integer.
*
* @param string $string The encoded integer.
* @param int $fromBase Source base, 0 for the base of the provided character set.
* @return int|false Returns the decoded integer or false on failure.
*/
public function decode(string $string, int $fromBase = 0): int|false {
if($fromBase === 0)
$fromBase = $this->maxBase;
else {
if($fromBase < 2)
throw new InvalidArgumentException('lowest supported value for $fromBase is 2');
if($fromBase > $this->maxBase)
throw new InvalidArgumentException('specified $fromBase value is greater than the amount of characters in the provided character set');
}
public function decode(string $string): int|false {
if($string === '')
return 0;
$output = 0;
$length = strlen($string) - 1;
for($i = 0; $i <= $length; ++$i) {
for($i = 0; $i < strlen($string); ++$i) {
$pos = strpos($this->characterSet, $string[$i]);
if($pos === false)
return false;
$output += $pos * ($fromBase ** ($length - $i));
$output = $output * $this->base + $pos;
}
return (int)$output;
return $output;
}
}

View file

@ -1,7 +1,7 @@
<?php
// Base62Test.php
// Created: 2022-01-28
// Updated: 2024-07-31
// Updated: 2024-12-22
declare(strict_types=1);
@ -14,16 +14,18 @@ final class Base62Test extends TestCase {
public const TESTS = [
['aaaaaa', 9311514030],
['Soap', 12962613],
['', 0],
['0', 0],
['1', 1],
['kPH', 80085],
['UC', 3510],
['p', 25],
['6zi', 25252],
['1ly7vk', 1234567890],
['15vjOLmUu1', 14739082838046309],
];
public function testDecode(): void {
$this->assertEquals(0, XNumber::fromBase62(''));
foreach(self::TESTS as $test)
$this->assertEquals($test[1], XNumber::fromBase62($test[0]));
}