-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
169 lines (148 loc) · 3.97 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*!
* any-to-any
*
* Copyright(c) 2019-2022 Imed Jaberi
* MIT Licensed
*/
import 'colors'
/**
* TODO: remove experimentalNextConvert when we fix the problem when convert large hexa number to decimal.
*
* INFO:
* convert('aa309be366a5cdd63103b657465f6f9617fbaee', 16, 10)
* should equal '60725777912152566396085878260777866453342730990'
* but return '60725777912152552488440220822060464062280464800'.
*/
import { experimentalConvert } from './experimental-convert.js'
/**
* Utility to extract the unit to convert it.
*
* @api public
*/
function getUnitToConvertIt(number: string, fromDecimal?: boolean) {
return +number >= 0 && +number <= 9
? +number
: !fromDecimal
? number.charCodeAt(0) - 55
: String.fromCharCode(+number + 55)
}
/**
* Convert from base 10 to any base.
*
* @api public
*/
function decimalToAnyBase(iNumber: number, oBase: number): string {
let result = ''
while (iNumber > 0) {
result =
String(getUnitToConvertIt(String((iNumber % oBase).toFixed()), true)) +
result
iNumber = Math.floor(iNumber / oBase)
}
return result
}
/**
* Convert from any base to base 10.
*
* @api public
*/
function anyBaseToDecimal(iNumber: string, iBase: number): number {
// we don't need to make any convertion when we pass iBase with 10 value
if (iBase === 10) return +iNumber
return iNumber
.split('')
.reduce(
(prevResult, currentItem, index) =>
prevResult +
+getUnitToConvertIt(currentItem) *
Math.pow(iBase, iNumber.length - 1 - index),
0
)
}
/**
* Utility to check passed number is on the range or not.
*
* @api private
*/
function isInRange(iNumber: string, iBase: number) {
return (+iNumber - 0) * (+iNumber - (iBase - 1)) <= 0
}
/**
* Utility to invoke the convert process.
*
* @api private
*/
function convertEngine(iNumber: string, iBase: number, oBase: number): string {
// https://github.com/SeanCannon/aybabtu#32-bit-limitation
if (iNumber.length > 32 && iBase < 10)
return (iNumber.match(/.{1,32}/g) as string[])
.map((chunk) => convertEngine(chunk, iBase, oBase))
.join('')
return iBase === oBase
? iNumber
: decimalToAnyBase(anyBaseToDecimal(iNumber, iBase), oBase)
}
/**
* Convert from any base to any base.
*
* @api public
*/
function convert(
iNumber: string | number,
iBase: number,
oBase: number
): string {
// force iNumber to be uppercase string
iNumber = `${iNumber}`.toUpperCase()
// init current sign
let sign = ''
// remove the 1st char if exist the '-' sign
// and update the current sign
if (iNumber.charAt(0) === '-') {
sign = '-'
iNumber = iNumber.slice(1)
}
// validator.
// check if exist some special charts.
if (iNumber.match(/[0-9A-Z]/g)?.length !== iNumber.length)
throw new Error(
'The input number should be not have special charts or empty.'
)
// check the input base.
if (iBase < 2 || iBase > 36)
throw new Error('The input base should be between 2 et 36.')
// check the outbut base.
if (oBase < 2 || oBase > 36)
throw new Error('The output base should be between 2 et 36.')
// TODO: work around this in the near future.
if (iBase > 10) {
console.warn(
`
Please, be careful and ensure that you pass valid input number which respect the range of the passed bases.
The module isn't checking the range for input bases greater than 10. soony, will do!
`.red
)
} else {
const normalizediNumber = iNumber
.split('')
.filter((number) => isInRange(number, iBase))
.join('')
if (iNumber !== normalizediNumber)
throw new Error(
`The input number '${iNumber}' isn't on the range [0, ${iBase - 1}].`
)
}
// early return when we pass 0 (0 always equal 0) - with out sign -
if (iNumber === '0') return iNumber
return sign + convertEngine(iNumber, iBase, oBase)
}
/**
* Expose.
*/
export {
anyBaseToDecimal,
decimalToAnyBase,
convert,
experimentalConvert,
convert as default,
}