@@ -12,7 +12,7 @@ const floorDiv = (dividend, divisor) => {
12
12
}
13
13
14
14
/**
15
- * Converts a string from one base to other
15
+ * Converts a string from one base to other. Loses accuracy above the value of `Number.MAX_SAFE_INTEGER`.
16
16
* @param {string } stringInBaseOne String in input base
17
17
* @param {string } baseOneCharacters Character set for the input base
18
18
* @param {string } baseTwoCharacters Character set for the output base
@@ -43,14 +43,57 @@ const convertArbitraryBase = (stringInBaseOne, baseOneCharacterString, baseTwoCh
43
43
value += ( digitNumber * placeValue )
44
44
placeValue *= stringOneBase
45
45
}
46
- let stringInBaseTwo = ''
46
+ const outputChars = [ ]
47
47
const stringTwoBase = baseTwoCharacters . length
48
48
while ( value > 0 ) {
49
49
const [ divisionResult , remainder ] = floorDiv ( value , stringTwoBase )
50
- stringInBaseTwo = baseTwoCharacters [ remainder ] + stringInBaseTwo
50
+ outputChars . push ( baseTwoCharacters [ remainder ] )
51
51
value = divisionResult
52
52
}
53
- return stringInBaseTwo || baseTwoCharacters [ 0 ]
53
+ return outputChars . reverse ( ) . join ( '' ) || baseTwoCharacters [ 0 ]
54
54
}
55
55
56
- export { convertArbitraryBase }
56
+ /**
57
+ * Converts a arbitrary-length string from one base to other. Doesn't lose accuracy.
58
+ * @param {string } stringInBaseOne String in input base
59
+ * @param {string } baseOneCharacters Character set for the input base
60
+ * @param {string } baseTwoCharacters Character set for the output base
61
+ * @returns {string }
62
+ */
63
+ const convertArbitraryBaseBigIntVersion = ( stringInBaseOne , baseOneCharacterString , baseTwoCharacterString ) => {
64
+ if ( [ stringInBaseOne , baseOneCharacterString , baseTwoCharacterString ] . map ( arg => typeof arg ) . some ( type => type !== 'string' ) ) {
65
+ throw new TypeError ( 'Only string arguments are allowed' )
66
+ }
67
+
68
+ const baseOneCharacters = [ ...baseOneCharacterString ]
69
+ const baseTwoCharacters = [ ...baseTwoCharacterString ]
70
+
71
+ for ( const charactersInBase of [ baseOneCharacters , baseTwoCharacters ] ) {
72
+ if ( charactersInBase . length !== new Set ( charactersInBase ) . size ) {
73
+ throw new TypeError ( 'Duplicate characters in character set are not allowed' )
74
+ }
75
+ }
76
+ const reversedStringOneChars = [ ...stringInBaseOne ] . reverse ( )
77
+ const stringOneBase = BigInt ( baseOneCharacters . length )
78
+ let value = 0n
79
+ let placeValue = 1n
80
+ for ( const digit of reversedStringOneChars ) {
81
+ const digitNumber = BigInt ( baseOneCharacters . indexOf ( digit ) )
82
+ if ( digitNumber === - 1n ) {
83
+ throw new TypeError ( `Not a valid character: ${ digit } ` )
84
+ }
85
+ value += ( digitNumber * placeValue )
86
+ placeValue *= stringOneBase
87
+ }
88
+ const outputChars = [ ]
89
+ const stringTwoBase = BigInt ( baseTwoCharacters . length )
90
+ while ( value > 0n ) {
91
+ const divisionResult = value / stringTwoBase
92
+ const remainder = value % stringTwoBase
93
+ outputChars . push ( baseTwoCharacters [ remainder ] )
94
+ value = divisionResult
95
+ }
96
+ return outputChars . reverse ( ) . join ( '' ) || baseTwoCharacters [ 0 ]
97
+ }
98
+
99
+ export { convertArbitraryBase , convertArbitraryBaseBigIntVersion }
0 commit comments