Added generic IntegerBaseConverter implementation and merged Base62 class with XNumber.
The Base62 implementation was always a bit misleading because both UriBase64 and Base32 handle any binary data whereas Base62 was only for converting integers. This shake-up should lessen that confusion.
This commit is contained in:
parent
94cd311f8c
commit
4a40868f58
5 changed files with 135 additions and 61 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.2407.311838
|
||||
0.2407.311904
|
||||
|
|
100
src/IntegerBaseConverter.php
Normal file
100
src/IntegerBaseConverter.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
// IntegerBaseConverter.php
|
||||
// Created: 2024-07-31
|
||||
// Updated: 2024-07-31
|
||||
|
||||
namespace Index;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Provides methods for encoding integers in different bases.
|
||||
*/
|
||||
class IntegerBaseConverter {
|
||||
private int $maxBase;
|
||||
|
||||
/**
|
||||
* @param string $characterSet Specifies the provided character set, must be a string consisting out of at least two unique characters.
|
||||
* @throws InvalidArgumentException If $characterSet contains less than two characters.
|
||||
* @throws InvalidArgumentException If $characterSet contains duplicate characters.
|
||||
*/
|
||||
public function __construct(
|
||||
private string $characterSet
|
||||
) {
|
||||
$length = strlen($characterSet);
|
||||
if($length < 2)
|
||||
throw new InvalidArgumentException('$characterSet must contain at least two characters');
|
||||
if(XString::countUnique($characterSet) !== $length)
|
||||
throw new InvalidArgumentException('$characterSet must contain a set of entirely unique characters');
|
||||
|
||||
$this->maxBase = $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 {
|
||||
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 '';
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
}
|
||||
|
||||
if($string === '')
|
||||
return 0;
|
||||
|
||||
$output = 0;
|
||||
$length = strlen($string) - 1;
|
||||
|
||||
for($i = 0; $i <= $length; ++$i) {
|
||||
$pos = strpos($this->characterSet, $string[$i]);
|
||||
if($pos === false)
|
||||
return false;
|
||||
|
||||
$output += $pos * ($fromBase ** ($length - $i));
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
<?php
|
||||
// Base62.php
|
||||
// Created: 2022-01-28
|
||||
// Updated: 2024-07-31
|
||||
|
||||
namespace Index\Serialisation;
|
||||
|
||||
/**
|
||||
* Provides a Base62 serialiser.
|
||||
*/
|
||||
final class Base62 {
|
||||
private const CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
private const BASE = 62;
|
||||
|
||||
/**
|
||||
* Converts a base10 integer to a base62 integer.
|
||||
*
|
||||
* @param int $integer The integer to encode.
|
||||
* @return string The encoded data, as a string.
|
||||
*/
|
||||
public static function encode(int $integer): string {
|
||||
$output = '';
|
||||
|
||||
for($i = floor(log10($integer) / log10(self::BASE)); $i >= 0; --$i) {
|
||||
$index = (int)floor($integer / (self::BASE ** $i));
|
||||
$output .= substr(self::CHARS, $index, 1);
|
||||
$integer -= $index * (self::BASE ** $i);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a base62 integer to a base10 integer.
|
||||
*
|
||||
* @param string $string The encoded integer.
|
||||
* @return int|false Returns the decoded integer or false on failure.
|
||||
*/
|
||||
public static function decode(string $string): int|false {
|
||||
$output = 0;
|
||||
$length = strlen($string) - 1;
|
||||
|
||||
for($i = 0; $i <= $length; ++$i) {
|
||||
$pos = strpos(self::CHARS, $string[$i]);
|
||||
if($pos === false)
|
||||
return false;
|
||||
|
||||
$output += $pos * (self::BASE ** ($length - $i));
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
|
@ -1,16 +1,43 @@
|
|||
<?php
|
||||
// XNumber.php
|
||||
// Created: 2023-09-15
|
||||
// Updated: 2023-11-09
|
||||
// Updated: 2024-07-31
|
||||
|
||||
namespace Index;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Provides various helper methods for numbers.
|
||||
*/
|
||||
final class XNumber {
|
||||
public const BASE62 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
|
||||
private static ?IntegerBaseConverter $base62Converter = null;
|
||||
|
||||
private static function getBase62Converter(): IntegerBaseConverter {
|
||||
self::$base62Converter ??= new IntegerBaseConverter(self::BASE62);
|
||||
return self::$base62Converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a base10 integer to a base62 integer.
|
||||
*
|
||||
* @param int $integer The integer to encode.
|
||||
* @return string The encoded data, as a string.
|
||||
*/
|
||||
public static function toBase62(int $integer): string {
|
||||
return self::getBase62Converter()->encode($integer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a base62 integer to a base10 integer.
|
||||
*
|
||||
* @param string $string The encoded integer.
|
||||
* @return int|false Returns the decoded integer or false on failure.
|
||||
*/
|
||||
public static function fromBase62(string $string): int|false {
|
||||
return self::getBase62Converter()->decode($string);
|
||||
}
|
||||
|
||||
public static function weighted(float $num1, float $num2, float $weight): float {
|
||||
$weight = min(1, max(0, $weight));
|
||||
return ($num1 * $weight) + ($num2 * (1 - $weight));
|
||||
|
|
|
@ -7,9 +7,9 @@ declare(strict_types=1);
|
|||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use Index\Serialisation\Base62;
|
||||
use Index\XNumber;
|
||||
|
||||
#[CoversClass(Base62::class)]
|
||||
#[CoversClass(XNumber::class)]
|
||||
final class Base62Test extends TestCase {
|
||||
public const TESTS = [
|
||||
['aaaaaa', 9311514030],
|
||||
|
@ -25,11 +25,11 @@ final class Base62Test extends TestCase {
|
|||
|
||||
public function testDecode(): void {
|
||||
foreach(self::TESTS as $test)
|
||||
$this->assertEquals($test[1], Base62::decode($test[0]));
|
||||
$this->assertEquals($test[1], XNumber::fromBase62($test[0]));
|
||||
}
|
||||
|
||||
public function testEncode(): void {
|
||||
foreach(self::TESTS as $test)
|
||||
$this->assertEquals($test[0], Base62::encode($test[1]));
|
||||
$this->assertEquals($test[0], XNumber::toBase62($test[1]));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue