diff --git a/README.md b/README.md index bb5bf0b..30f911e 100644 --- a/README.md +++ b/README.md @@ -9,28 +9,97 @@ [![Code Coverage](https://scrutinizer-ci.com/g/thunderer/Numbase/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/thunderer/Numbase/?branch=master) [![Code Climate](https://codeclimate.com/github/thunderer/Numbase/badges/gpa.svg)](https://codeclimate.com/github/thunderer/Numbase) -Numbase is a small utility for converting numbers between arbitrary bases. It uses PHP GMP extension to handle big integers. +Easily convert numbers between arbitrary bases and symbol sets. ## Requirements -No required dependencies, only PHP >=5.3 +This library requires PHP >=5.3 (namespaces) and GMP extension (for handling large numbers exceeding PHP capabilities). ## Installation -This library is available on Packagist under alias `thunderer/numbase`. Act accordingly. +This library is available on [Packagist](https://packagist.org/packages/thunderer/numbase) as `thunderer/numbase`. ## Usage +**The Simplest Way™** + +```php +use Thunder\Numbase\Numbase; + +$numbase = Numbase::createDefault(); + +// decimal 15 to hexadecimal number +assert('F' === $numbase->convert(15, 10, 16)); +// 64000 decimal to base 32000 +assert('20' === $numbase->convert(64000, 10, 32000)); +``` + +Regular usage (see Internals section for more options): + ```php use Thunder\Numbase\Numbase; -use Thunder\Numbase\Formatter\PermissiveFormatter; -use Thunder\Numbase\Symbols\Base62Symbols; -$numbase = new Numbase(new Base62Symbols(), new PermissiveFormatter()); +$base62 = new Base62Symbols(); +$numbase = new Numbase(new GmpConverter($base62), new StrictFormatter($base62)); + +// decimal 15 to hexadecimal number +assert('F' === $numbase->convert(15, 10, 16)); +``` + +**Showcase** + +Convert number to and from a different set of symbols: + +```php +$base10 = new Base10Symbols(); +$upper = new StringSymbols('!@#$%^&*()'); + +$numbase = new Numbase(new GmpDigits($base10), new StrictFormatter($upper)); + +assert('#!' === $numbase->convert('20', 10, 10)); +assert('-$!' === $numbase->convert('-30', 10, 10)); + +$numbase = new Numbase(new GmpDigits($upper), new StrictFormatter($base10)); + +assert('20' === $numbase->convert('#!', 10, 10)); +assert('-30' === $numbase->convert('-$!', 10, 10)); +``` + +Get array of digit values (for bases too large for any symbol set): + +```php +$numbase = new Numbase(new GmpDigits(new Base62Symbols()), new ArrayFormatter()); -assert('10' === $numbase->convert(16, 10, 16)); +// convert 10^12 to base 99: +assert(array('10', '61', '53', '3', '51', '60', '10') + === $numbase->convert('10000000000000', 10, 99)); ``` +## Internals + +Numbase is built upon several concepts: + +* **converters** that convert numbers to array of numbers of digits, +* **formatters** that take those arrays and return final numbers, +* **symbols** used in converters to check symbols values and to get digits symbols in formatters. + +There are several implementations of each concept bundled with this library, for example: + +* converters: + * **GmpConverter**: can convert any integer between any base greater than 2, uses `gmp_*()` functions, + * **GmpStrvalConverter**: uses `gmp_strval()` to convert between bases 2 and 62, + * **BaseConvertConverter**: uses `base_convert()` to convert between bases 2 and 32, +* formatters: + * **ArrayFormatter**: returns raw array of digits numbers, + * **StrictFormatter**: returns number as string, throws exception when digit is not found in symbols set, + * **FallbackFormatter**: returns number as string, but returns string with digit values separated by configured separator when any digit is not found in symbols set, +* symbols: + * **ArraySymbols**: takes associative `array(value => symbol)`, + * **Base62Symbols**: contains alphanumeric set of symbols `0-9A-Za-z up` to base 62, + * **StringSymbols**: takes string and splits it assigning consecutive values to each character. + +The named constructor `Numbase::createDefault()` uses `GmpConverter`, `StrictFormatter` and `Base62Symbols` as defaults. + ## License See LICENSE file in the main directory of this library. diff --git a/src/Converter/BaseConvertConverter.php b/src/Converter/BaseConvertConverter.php new file mode 100644 index 0000000..1983baf --- /dev/null +++ b/src/Converter/BaseConvertConverter.php @@ -0,0 +1,43 @@ + + */ +final class BaseConvertConverter implements ConverterInterface + { + private $symbols; + + public function __construct(SymbolsInterface $symbols) + { + $this->symbols = $symbols; + } + + public function convert($source, $sourceBase, $targetBase) + { + if($sourceBase < 2 || $targetBase < 2 || $sourceBase > 36 || $targetBase > 36) + { + $msg = 'Invalid source or target base, must be an integer between 2 and 36!'; + throw new \InvalidArgumentException(sprintf($msg)); + } + + $source = (string)$source; + if(0 === mb_strlen($source)) + { + $msg = 'How about a non-empty string?'; + throw new \InvalidArgumentException($msg); + } + + $digits = str_split((string)base_convert($source, $sourceBase, $targetBase), 1); + + // base_convert() returns lowercase characters so they need to be + // uppercased because lowercased come in latter + $digits = array_map('strtoupper', $digits); + $digits = array_map(array($this->symbols, 'getValue'), $digits); + + return array_map('strval', $digits); + } + } diff --git a/src/Converter/GmpConverter.php b/src/Converter/GmpConverter.php new file mode 100644 index 0000000..0043c26 --- /dev/null +++ b/src/Converter/GmpConverter.php @@ -0,0 +1,82 @@ + + */ +final class GmpConverter implements ConverterInterface + { + private $symbols; + + public function __construct(SymbolsInterface $symbols) + { + $this->symbols = $symbols; + } + + public function convert($source, $sourceBase, $targetBase) + { + if($sourceBase < 2 || $targetBase < 2) + { + $msg = 'Invalid source or target base, must be an integer greater than one!'; + throw new \InvalidArgumentException(sprintf($msg)); + } + + $source = (string)$source; + if(0 === mb_strlen($source)) + { + $msg = 'How about a non-empty string?'; + throw new \InvalidArgumentException($msg); + } + + $base10 = $this->convertToBase10($source, $sourceBase); + $digits = $this->computeBaseNDigits($base10, $targetBase); + + return $digits; + } + + private function convertToBase10($source, $sourceBase) + { + $int = gmp_init(0, 10); + $length = mb_strlen($source) - 1; + + for($i = 0; $i <= $length; $i++) + { + $pow = gmp_pow($sourceBase, $length - $i); + $mul = gmp_mul($this->symbols->getValue($source[$i]), $pow); + $int = gmp_add($int, $mul); + } + + return $int; + } + + private function computeBaseNDigits($number, $targetBase) + { + $digits = array(); + $length = $this->computeBaseNLength($number, $targetBase); + + for($i = 0; $i < $length; $i++) + { + $pow = gmp_pow($targetBase, $length - $i - 1); + $div = gmp_div($number, $pow, GMP_ROUND_ZERO); + $number = gmp_sub($number, gmp_mul($div, $pow)); + $digits[] = $div; + } + + return array_map('gmp_strval', $digits); + } + + private function computeBaseNLength($number, $targetBase) + { + $digits = 0; + + while(gmp_cmp($number, gmp_pow($targetBase, $digits)) != -1) + { + $digits++; + } + + return $digits ?: 1; + } + } diff --git a/src/Converter/GmpStrvalConverter.php b/src/Converter/GmpStrvalConverter.php new file mode 100644 index 0000000..af91f8b --- /dev/null +++ b/src/Converter/GmpStrvalConverter.php @@ -0,0 +1,39 @@ + + */ +final class GmpStrvalConverter implements ConverterInterface + { + private $symbols; + + public function __construct(SymbolsInterface $symbols) + { + $this->symbols = $symbols; + } + + public function convert($source, $sourceBase, $targetBase) + { + if($sourceBase < 2 || $targetBase < 2 || $sourceBase > 62 || $targetBase > 62) + { + $msg = 'Invalid source or target base, must be an integer between 2 and 62!'; + throw new \InvalidArgumentException(sprintf($msg)); + } + + $source = (string)$source; + if(0 === mb_strlen($source)) + { + $msg = 'How about a non-empty string?'; + throw new \InvalidArgumentException($msg); + } + + $digits = str_split((string)gmp_strval(gmp_init($source, $sourceBase), $targetBase), 1); + $digits = array_map(array($this->symbols, 'getValue'), $digits); + + return array_map('strval', $digits); + } + } diff --git a/src/ConverterInterface.php b/src/ConverterInterface.php new file mode 100644 index 0000000..adf95c4 --- /dev/null +++ b/src/ConverterInterface.php @@ -0,0 +1,19 @@ + + */ +interface ConverterInterface + { + /** + * Converts number from source to target base and return digits array + * + * @param string $number + * @param int $sourceBase + * @param int $targetBase + * + * @return array + */ + public function convert($number, $sourceBase, $targetBase); + } diff --git a/src/Formatter/DigitsFormatter.php b/src/Formatter/ArrayFormatter.php similarity index 50% rename from src/Formatter/DigitsFormatter.php rename to src/Formatter/ArrayFormatter.php index c439aa9..92de828 100644 --- a/src/Formatter/DigitsFormatter.php +++ b/src/Formatter/ArrayFormatter.php @@ -2,15 +2,19 @@ namespace Thunder\Numbase\Formatter; use Thunder\Numbase\FormatterInterface; -use Thunder\Numbase\SymbolsInterface; /** * @author Tomasz Kowalczyk */ -final class DigitsFormatter implements FormatterInterface +final class ArrayFormatter implements FormatterInterface { - public function format(array $digits, SymbolsInterface $symbols) + public function format(array $digits, $signed) { + if($signed) + { + $digits[0] = '-'.$digits[0]; + } + return $digits; } } diff --git a/src/Formatter/FallbackFormatter.php b/src/Formatter/FallbackFormatter.php new file mode 100644 index 0000000..bdaa60c --- /dev/null +++ b/src/Formatter/FallbackFormatter.php @@ -0,0 +1,33 @@ + + */ +final class FallbackFormatter implements FormatterInterface + { + private $symbols; + private $separator; + + public function __construct(SymbolsInterface $symbols, $separator) + { + $this->symbols = $symbols; + $this->separator = $separator; + } + + public function format(array $digits, $signed) + { + $sign = $signed ? '-' : ''; + try + { + return $sign.implode('', array_map(array($this->symbols, 'getSymbol'), $digits)); + } + catch(\InvalidArgumentException $e) + { + return $sign.implode($this->separator, $digits); + } + } + } diff --git a/src/Formatter/PermissiveFormatter.php b/src/Formatter/PermissiveFormatter.php deleted file mode 100644 index 3a69fa4..0000000 --- a/src/Formatter/PermissiveFormatter.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ -final class PermissiveFormatter implements FormatterInterface - { - private $separator; - - public function __construct($separator) - { - $this->separator = $separator; - } - - public function format(array $digits, SymbolsInterface $symbols) - { - try - { - return implode('', array_map(array($symbols, 'getSymbol'), $digits)); - } - catch(\InvalidArgumentException $e) - { - return implode($this->separator, $digits); - } - } - } diff --git a/src/Formatter/StrictFormatter.php b/src/Formatter/StrictFormatter.php index a9e36b1..2d9f122 100644 --- a/src/Formatter/StrictFormatter.php +++ b/src/Formatter/StrictFormatter.php @@ -9,8 +9,15 @@ */ final class StrictFormatter implements FormatterInterface { - public function format(array $digits, SymbolsInterface $symbols) + private $symbols; + + public function __construct(SymbolsInterface $symbols) + { + $this->symbols = $symbols; + } + + public function format(array $digits, $signed) { - return implode('', array_map(array($symbols, 'getSymbol'), $digits)); + return ($signed ? '-' : '').implode('', array_map(array($this->symbols, 'getSymbol'), $digits)); } } diff --git a/src/FormatterInterface.php b/src/FormatterInterface.php index fdb2e07..2f7198d 100644 --- a/src/FormatterInterface.php +++ b/src/FormatterInterface.php @@ -9,10 +9,10 @@ interface FormatterInterface /** * Formats computed digits to a final result. * - * @param array $digits - * @param SymbolsInterface $symbols + * @param string[] $digits + * @param bool $signed * * @return mixed */ - public function format(array $digits, SymbolsInterface $symbols); + public function format(array $digits, $signed); } diff --git a/src/Numbase.php b/src/Numbase.php index aea89d4..d0fe765 100644 --- a/src/Numbase.php +++ b/src/Numbase.php @@ -1,100 +1,53 @@ */ final class Numbase { private $formatter; - private $symbols; + private $converter; - public function __construct(SymbolsInterface $symbols, FormatterInterface $formatter) + public function __construct(ConverterInterface $converter, FormatterInterface $formatter) { $this->formatter = $formatter; - $this->symbols = $symbols; + $this->converter = $converter; + } + + public static function createDefault(SymbolsInterface $symbols = null) + { + $symbols = $symbols ?: new Base62Symbols(); + + return new self(new GmpConverter($symbols), new StrictFormatter($symbols)); } /** * Converts number with given base to another base. Do not forget to set * proper symbols set. Result type depends on the formatter implementation. * - * @param int|string $source Number to convert - * @param int $sourceBase Source number base - * @param int $targetBase Target number base + * @param int|string $number Number to convert + * @param int $fromBase Source number base + * @param int $toBase Target number base * - * @return mixed - * - * @throws \InvalidArgumentException Invalid source base, target base or empty number + * @return mixed Depends on formatter */ - public function convert($source, $sourceBase, $targetBase) + public function convert($number, $fromBase, $toBase) { - if($sourceBase < 2 || $targetBase < 2) - { - $msg = 'Invalid source or target base, must be an integer greater than one!'; - throw new \InvalidArgumentException(sprintf($msg)); - } - - $source = (string)$source; - if(0 === mb_strlen($source)) + $signed = false; + $number = (string)$number; + if($number && '-' === $number[0]) { - $msg = 'How about a non-empty string?'; - throw new \InvalidArgumentException($msg); + $signed = true; + $number = substr($number, 1); } - $sign = ''; - if('-' === $source[0]) - { - $sign = '-'; - $source = substr($source, 1); - } - - $base10 = $this->convertToBase10($source, $sourceBase); - $digits = $this->computeBaseNDigits($base10, $targetBase); - - return $sign.$this->formatter->format($digits, $this->symbols); - } - - private function convertToBase10($source, $sourceBase) - { - $int = gmp_init(0, 10); - $length = mb_strlen($source) - 1; - - for($i = 0; $i <= $length; $i++) - { - $pow = gmp_pow($sourceBase, $length - $i); - $mul = gmp_mul($this->symbols->getValue($source[$i]), $pow); - $int = gmp_add($int, $mul); - } - - return $int; - } - - private function computeBaseNDigits($number, $targetBase) - { - $digits = array(); - $length = $this->computeBaseNLength($number, $targetBase); - - for($i = 0; $i < $length; $i++) - { - $pow = gmp_pow($targetBase, $length - $i - 1); - $div = gmp_div($number, $pow, GMP_ROUND_ZERO); - $number = gmp_sub($number, gmp_mul($div, $pow)); - $digits[] = $div; - } - - return array_map('gmp_strval', $digits); - } - - private function computeBaseNLength($number, $targetBase) - { - $digits = 0; - - while(gmp_cmp($number, gmp_pow($targetBase, $digits)) != -1) - { - $digits++; - } + $digits = $this->converter->convert($number, $fromBase, $toBase); - return $digits ?: 1; + return $this->formatter->format($digits, $signed); } } diff --git a/src/Symbols/Base10Symbols.php b/src/Symbols/Base10Symbols.php new file mode 100644 index 0000000..9488f1c --- /dev/null +++ b/src/Symbols/Base10Symbols.php @@ -0,0 +1,16 @@ + + */ +final class Base10Symbols extends AbstractSymbols implements SymbolsInterface + { + public function __construct() + { + $this->symbols = str_split('0123456789'); + $this->reverseSymbols = array_flip($this->symbols); + } + } diff --git a/src/Symbols/Base62Symbols.php b/src/Symbols/Base62Symbols.php index 5b7d8ca..60cbb25 100644 --- a/src/Symbols/Base62Symbols.php +++ b/src/Symbols/Base62Symbols.php @@ -10,11 +10,9 @@ final class Base62Symbols extends AbstractSymbols implements SymbolsInterface { public function __construct() { - $this->symbols = str_split('' - .'0123456789' + $this->symbols = str_split('0123456789' .'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - .'abcdefghijklmnopqrstuvwxyz' - .''); + .'abcdefghijklmnopqrstuvwxyz', 1); $this->reverseSymbols = array_flip($this->symbols); } } diff --git a/tests/ConverterTest.php b/tests/ConverterTest.php new file mode 100644 index 0000000..1800276 --- /dev/null +++ b/tests/ConverterTest.php @@ -0,0 +1,84 @@ + + */ +final class ConverterTest extends \PHPUnit_Framework_TestCase + { + public function testGmpConverter() + { + $digits = new GmpConverter(new Base62Symbols()); + + $this->assertSame(explode(':', '1:20:82:19:36:27:27:76:96:60:86:61:74:24:84:24:79:72:19:1'), + $digits->convert(gmp_strval(gmp_pow(10, 38), 10), 10, 99)); + $this->assertSame(str_split('100000000000', 1), $digits->convert('10000000000000000000000', 10, 100)); + } + + public function testGmpStrvalDigits() + { + $converter = new GmpStrvalConverter(new Base62Symbols()); + + $this->assertSame(explode(':', '2:17:59:32:31:60:4:18:0:7:60:7:54:25:34:11:18:35:1:58:47:14'), + $converter->convert(gmp_strval(gmp_pow(10, 38), 10), 10, 62)); + $this->assertSame(explode(':', '41:5:3:36:6:5:0:0:0:0:0:0:0:0:0:0:0'), + $converter->convert('10000000000000000000000', 10, 20)); + } + + public function testGmpStrvalExceptionOnInvalidSourceBase() + { + $converter = new GmpStrvalConverter(new Base62Symbols()); + $this->setExpectedException('InvalidArgumentException'); + $converter->convert('10', 1, 10); + } + + public function testGmpStrvalExceptionOnInvalidTargetBase() + { + $converter = new GmpStrvalConverter(new Base62Symbols()); + $this->setExpectedException('InvalidArgumentException'); + $converter->convert('10', 10, 63); + } + + public function testGmpStrvalExceptionOnEmptyNumber() + { + $converter = new GmpStrvalConverter(new Base62Symbols()); + $this->setExpectedException('InvalidArgumentException'); + $converter->convert('', 10, 20); + } + + public function testBaseConvertDigits() + { + $converter = new BaseConvertConverter(new Base62Symbols()); + + $this->assertSame(explode(':', '4:16:12:8:33:15:15:6:34:11:20:0:20:28:32:4:24:0:32:4:12:16:12:28:32'), + $converter->convert(gmp_strval(gmp_pow(10, 38), 10), 10, 36)); + $this->assertSame(str_split('100000000000', 1), $converter->convert('100000000000', 10, 10)); + $this->assertSame(explode(':', '3:18:2:10:0:0:0:0:0'), $converter->convert('100000000000', 10, 20)); + } + + public function testBaseConvertExceptionOnInvalidSourceBase() + { + $converter = new BaseConvertConverter(new Base62Symbols()); + $this->setExpectedException('InvalidArgumentException'); + $converter->convert('10', 40, 10); + } + + public function testBaseConvertExceptionOnInvalidTargetBase() + { + $converter = new BaseConvertConverter(new Base62Symbols()); + $this->setExpectedException('InvalidArgumentException'); + $converter->convert('10', 10, 40); + } + + public function testBaseConvertExceptionOnEmptyNumber() + { + $converter = new BaseConvertConverter(new Base62Symbols()); + $this->setExpectedException('InvalidArgumentException'); + $converter->convert('', 10, 20); + } + } diff --git a/tests/FormatterTest.php b/tests/FormatterTest.php index 6a40703..269cc69 100644 --- a/tests/FormatterTest.php +++ b/tests/FormatterTest.php @@ -1,8 +1,8 @@ assertEquals('12', $permissive->format(array(1, 2), $symbols)); - $this->assertEquals('100:200', $permissive->format(array(100, 200), $symbols)); + $this->assertEquals('12', $permissive->format(array(1, 2), false)); + $this->assertEquals('100:200', $permissive->format(array(100, 200), false)); - $this->assertEquals('12', $strict->format(array(1, 2), $symbols)); + $this->assertEquals('12', $strict->format(array(1, 2), false)); - $this->assertEquals(array(1, 2), $digits->format(array(1, 2), $symbols)); - $this->assertEquals(array(100, 200), $digits->format(array(100, 200), $symbols)); + $this->assertEquals(array(1, 2), $digits->format(array(1, 2), false)); + $this->assertSame(array(100, 200), $digits->format(array(100, 200), false)); } public function testExceptionInvalidSymbolStrictFormatter() { - $formatter = new StrictFormatter(); + $formatter = new StrictFormatter(new Base62Symbols()); $this->setExpectedException('InvalidArgumentException'); - $formatter->format(array(100), new Base62Symbols()); + $formatter->format(array(100), false); } } diff --git a/tests/NumbaseTest.php b/tests/NumbaseTest.php index 904d8c7..227770e 100644 --- a/tests/NumbaseTest.php +++ b/tests/NumbaseTest.php @@ -1,8 +1,8 @@ assertSame($result, $base62->convert($source, $from, $to)); - $this->assertSame($result, $string->convert($source, $from, $to)); - $this->assertSame($result, $array->convert($source, $from, $to)); + $this->assertSame($result, $numbase->convert($source, $from, $to)); if(false === strpos($result, ':')) { - $this->assertSame($source, $base62->convert($result, $to, $from)); - $this->assertSame($source, $string->convert($result, $to, $from)); - $this->assertSame($source, $array->convert($result, $to, $from)); + $this->assertSame($source, $numbase->convert($result, $to, $from)); } } public function provideNumbase() { - return array( + $symbols = '0123456789' + .'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + .'abcdefghijklmnopqrstuvwxyz'; + $numbases = array( + array(Numbase::createDefault()), // default base62 symbols + array(Numbase::createDefault(new StringSymbols($symbols))), + array(Numbase::createDefault(new ArraySymbols(str_split($symbols, 1)))), + ); + $numbers = array( array(gmp_strval(gmp_pow(10, 9), 10), 10, 1000000000, '10'), array(gmp_strval(gmp_pow(10, 1000), 10), 10, 100, '1'.str_pad('', 500, '0', STR_PAD_RIGHT)), - array(gmp_strval(gmp_pow(10, 38), 10), 10, 99, '1:20:82:19:36:27:27:76:96:60:86:61:74:24:84:24:79:72:19:1'), array('10000000000000000000000', 10, 100, '100000000000'), - array('654321', 10, 100, '65:43:21'), array('15', 10, 16, 'F'), array('zz', 62, 10, '3843'), array('3843', 10, 62, 'zz'), @@ -58,19 +49,31 @@ public function provideNumbase() array('65', 10, 2, '1000001'), array('1', 10, 10, '1'), array('0', 16, 2, '0'), + array('64000', 10, 32000, '20'), ); + + $result = array(); + foreach($numbers as $number) + { + foreach($numbases as $numbase) + { + $result[] = array_merge($numbase, $number); + } + } + + return $result; } /** * Want to test arbitrary number ranges? Tweak range() parameters in data - * provider and you're good to go. Defaults are 0..1 to not artificially + * provider and you're good to go. Defaults are -1..1 to not artificially * boost number of tests. * * @dataProvider provideLoop */ public function testLoop($num) { - $base62 = new Numbase(new Base62Symbols(), new PermissiveFormatter(':')); + $base62 = Numbase::createDefault(); $this->assertSame((string)$num, $base62->convert($base62->convert($num, 10, 62), 62, 10)); } @@ -81,23 +84,29 @@ public function provideLoop() }, range(-1, 1)); } + public function testOtherFormatters() + { + $array = new Numbase(new GmpConverter(new Base62Symbols()), new ArrayFormatter()); + $this->assertSame(array('-1', '0'), $array->convert(-10, 10, 10)); + } + public function testExceptionOnInvalidSourceBase() { - $numbase = new Numbase(new Base62Symbols(), new StrictFormatter()); + $numbase = Numbase::createDefault(); $this->setExpectedException('InvalidArgumentException'); $numbase->convert(10, 1, 16); } public function testExceptionOnInvalidTargetBase() { - $numbase = new Numbase(new Base62Symbols(), new StrictFormatter()); + $numbase = Numbase::createDefault(); $this->setExpectedException('InvalidArgumentException'); $numbase->convert(10, 10, -20); } public function testExceptionOnEmptyNumber() { - $numbase = new Numbase(new Base62Symbols(), new StrictFormatter()); + $numbase = Numbase::createDefault(); $this->setExpectedException('InvalidArgumentException'); $numbase->convert('', 10, 10); } diff --git a/tests/SymbolsTest.php b/tests/SymbolsTest.php index 28578cc..47e944c 100644 --- a/tests/SymbolsTest.php +++ b/tests/SymbolsTest.php @@ -1,7 +1,10 @@ @@ -20,6 +23,42 @@ public function testBase62Symbols() $this->assertFalse($symbols->hasValue(100)); } + public function testBase10Symbols() + { + $symbols = new Base10Symbols(); + + $this->assertSame(0, $symbols->getValue('0')); + $this->assertSame('0', $symbols->getSymbol(0)); + $this->assertTrue($symbols->hasSymbol('7')); + $this->assertFalse($symbols->hasSymbol('C')); + $this->assertTrue($symbols->hasValue(9)); + $this->assertFalse($symbols->hasValue(100)); + } + + public function testArraySymbols() + { + $symbols = new ArraySymbols(array(2 => '!', 4 => '@', 6 => '#')); + + $this->assertSame(2, $symbols->getValue('!')); + $this->assertSame('@', $symbols->getSymbol(4)); + $this->assertTrue($symbols->hasSymbol('#')); + $this->assertFalse($symbols->hasSymbol('V')); + $this->assertTrue($symbols->hasValue(4)); + $this->assertFalse($symbols->hasValue(5)); + } + + public function testStringSymbols() + { + $symbols = new StringSymbols('!@#$%^&*()'); + + $this->assertSame(2, $symbols->getValue('#')); + $this->assertSame('(', $symbols->getSymbol(8)); + $this->assertTrue($symbols->hasSymbol('#')); + $this->assertFalse($symbols->hasSymbol('V')); + $this->assertTrue($symbols->hasValue(4)); + $this->assertFalse($symbols->hasValue(40)); + } + public function testExceptionMissingSymbol() { $symbols = new Base62Symbols();