Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
100.00% |
1 / 1 |
|
100.00% |
2 / 2 |
CRAP | |
100.00% |
33 / 33 |
Converter | |
100.00% |
1 / 1 |
|
100.00% |
2 / 2 |
12 | |
100.00% |
33 / 33 |
createAlphabet | |
100.00% |
1 / 1 |
4 | |
100.00% |
11 / 11 |
|||
convert | |
100.00% |
1 / 1 |
8 | |
100.00% |
22 / 22 |
1 | <?php |
2 | |
3 | namespace TonyBogdanov\Alphabase; |
4 | |
5 | use TonyBogdanov\Alphabase\Exception\InvalidAlphabetException; |
6 | use TonyBogdanov\Alphabase\Exception\InvalidInputException; |
7 | |
8 | /** |
9 | * Class Converter |
10 | * |
11 | * @package TonyBogdanov\Alphabase |
12 | */ |
13 | class Converter { |
14 | |
15 | /** @var array[] */ |
16 | protected static array $alphabets = []; |
17 | |
18 | /** |
19 | * @param string $symbols |
20 | * @return array |
21 | * @throws InvalidAlphabetException |
22 | */ |
23 | protected static function createAlphabet( string $symbols ): array { |
24 | |
25 | if ( ! isset( static::$alphabets[ $symbols ] ) ) { |
26 | |
27 | $alphabet = str_split( $symbols ); |
28 | $unique = array_unique( $alphabet ); |
29 | |
30 | if ( count( $unique ) !== count( $alphabet ) ) { |
31 | throw new InvalidAlphabetException( 'The alphabet must contain only unique characters.' ); |
32 | } |
33 | |
34 | if ( 2 > count( $unique ) ) { |
35 | throw new InvalidAlphabetException( sprintf( |
36 | 'The alphabet must contain at least 2 characters, got: %d.', |
37 | count( $unique ), |
38 | ) ); |
39 | } |
40 | |
41 | static::$alphabets[ $symbols ] = [ $unique, array_flip( $unique ) ]; |
42 | |
43 | } |
44 | |
45 | return static::$alphabets[ $symbols ]; |
46 | |
47 | } |
48 | |
49 | /** |
50 | * @param string $input |
51 | * @param string $inputAlphabet |
52 | * @param string $outputAlphabet |
53 | * @return string |
54 | * @throws InvalidAlphabetException |
55 | * @throws InvalidInputException |
56 | */ |
57 | public static function convert( string $input, string $inputAlphabet, string $outputAlphabet ): string { |
58 | |
59 | if ( '' === $input ) { |
60 | return ''; |
61 | } |
62 | |
63 | [ $is, $isf ] = static::createAlphabet( $inputAlphabet ); |
64 | [ $os ] = static::createAlphabet( $outputAlphabet ); |
65 | |
66 | $invalid = array_diff( array_unique( str_split( $input ) ), $is ); |
67 | if ( 0 < count( $invalid ) ) { |
68 | throw new InvalidInputException( sprintf( |
69 | 'The input string contains characters outside of the input alphabet: %s.', |
70 | json_encode( array_values( $invalid ) ), |
71 | ) ); |
72 | } |
73 | |
74 | $ib = count( $is ); |
75 | $ob = count( $os ); |
76 | |
77 | $carry = preg_match( '/^' . preg_quote( $is[0], '/' ) . '+/', $input, $match ) ? strlen( $match[0] ) : 0; |
78 | if ( $carry === strlen( $input ) ) { |
79 | return str_repeat( $os[0], $carry ); |
80 | } |
81 | |
82 | $value = '0'; |
83 | foreach ( array_reverse( str_split( substr( $input, $carry ) ) ) as $index => $symbol ) { |
84 | $value = bcadd( $value, bcmul( $isf[ $symbol ], bcpow( $ib, $index ) ) ); |
85 | } |
86 | |
87 | $result = []; |
88 | while ( -1 === bccomp( 0, $value ) ) { |
89 | array_unshift( $result, $os[ (int) bcmod( $value, $ob ) ] ); |
90 | $value = bcdiv( $value, $ob ); |
91 | } |
92 | |
93 | return ( 0 < $carry ? str_repeat( $os[0], $carry ) : '' ) . implode( '', $result ); |
94 | |
95 | } |
96 | |
97 | } |