index/src/IntegerBaseConverter.php

101 lines
3.4 KiB
PHP

<?php
// IntegerBaseConverter.php
// Created: 2024-07-31
// Updated: 2024-08-04
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 (int)$output;
}
}