@rniv/base-n
Version:
## Installation
180 lines (164 loc) • 5.41 kB
text/typescript
let range = (n: number) => {
return [...Array(n).keys()]
}
export class BaseN {
private _regex = /^[a-z0-9]+$/i
private _base_regex: RegExp
private _symbols_string: string
private _base: number
private _symbols: Array<string>
private _numbers: Array<number>
private _symbol_number = {}
private _number_symbol = {}
constructor(symbols: Array<string> | string) {
if (typeof symbols === 'string') {
symbols = symbols.split('')
}
this._symbols_string = symbols.join('')
if (!this.is_valid_symbols()) throw new Error('Invalid symbols')
this._base = symbols.length
this._symbols = symbols
this._base_regex = new RegExp(`^[${this._symbols_string}]+$`)
this._numbers = range(this._base)
this._init()
}
private _init(): void {
for (let idx = 0; idx < this._base; idx++) {
let num = this._numbers[idx]
let symbol = this._symbols[idx]
this._symbol_number[symbol] = num
this._number_symbol[num] = symbol
}
}
private is_valid_symbols(): boolean {
let regex_test = this._regex.test(this._symbols_string)
if (!regex_test) return regex_test
let duplicate_test = !this.has_duplicate()
return duplicate_test
}
private has_duplicate(): boolean {
let charset = {}
for (let char of this._symbols_string) {
if (charset[char] === 1) return true
charset[char] = 1
}
return false
}
encode(n: number): string {
n = parseInt(n + '')
let result = ''
while (n > 0) {
let rem = n % this._base
result = this._number_symbol[rem] + result
n = parseInt((n / this._base) + '')
}
if (result.length < 1) {
result = this._number_symbol[0]
}
return result
}
decode(s: string): number {
if (!this._base_regex.test(s)) throw new Error('Invalid symbols exist')
s = s.trim()
let result = 0
let pow = s.length - 1
for (let symbol of s) {
result += Math.pow(this._base, pow) * this._symbol_number[symbol]
pow -= 1
}
return result
}
add(n1: string, n2: string) {
if (!this._base_regex.test(n1) || !this._base_regex.test(n2)) throw new Error('Invalid symbols exist')
let n1_array = Array.from(n1)
let n2_array = Array.from(n2)
let result = ''
let sum = ''
let carry = ''
while (n1_array.length > 0 || n2_array.length > 0) {
[sum, carry] = this._add(n1_array.pop(), n2_array.pop(), carry)
result = sum + result
}
if (this._symbol_number[carry] !== 0) result = carry + result
result = this.remove_left_zeros(result)
return result
}
private _add(n1: string, n2: string, carry: string): Array<string> {
let n1_value = this._symbol_number[n1] || 0
let n2_value = this._symbol_number[n2] || 0
let carry_value = this._symbol_number[carry] || 0
let sum = n1_value + n2_value + carry_value
carry_value = parseInt((sum / this._base) + '')
let result = sum % this._base
return [this._number_symbol[result], this._number_symbol[carry_value]]
}
subtract(s1: string, s2: string, abs = true): string {
if (!this._base_regex.test(s1) || !this._base_regex.test(s2)) throw new Error('Invalid symbols exist')
let compare_value = this.compare(s1, s2)
if (compare_value === -1) {
[s1, s2] = [s2, s1]
}
let result = ''
let borrow = ''
let s1_array = Array.from(s1)
let s2_array = Array.from(s2)
let value = ''
while (s1_array.length > 0 || s2_array.length > 0) {
[value, borrow] = this._subtract(s1_array.pop(), s2_array.pop(), borrow)
if (s1.length > 1 && s1_array.length === 0 && this._symbol_number[value] === 0)
continue
result = value + result
}
if (!abs) {
let prefix = compare_value === -1 ? '-' : ''
return prefix + result
}
result = this.remove_left_zeros(result)
return result
}
private _subtract(s1: string, s2: string, borrow: string): Array<string> {
let s1_value = this._symbol_number[s1] || 0
let s2_value = this._symbol_number[s2] || 0
let borrow_value = this._symbol_number[borrow] || 0
let value = s1_value - s2_value - borrow_value
if (value < 0) {
borrow_value = 1
value += this._base
}
let result = this._number_symbol[value]
borrow = this._number_symbol[borrow_value]
return [result, borrow]
}
compare(s1: string, s2: string): number {
if (!this._base_regex.test(s1) || !this._base_regex.test(s2)) throw new Error('Invalid symbols exist')
s1 = this.remove_left_zeros(s1)
s2 = this.remove_left_zeros(s2)
if (s1.length > s2.length) return 1
else if (s1.length < s2.length) return -1
let max_len = s1.length
for (let idx = 0; idx < max_len; idx++) {
let s1_char = s1[idx]
let s2_char = s2[idx]
let s1_char_value = this._symbol_number[s1_char] || 0
let s2_char_value = this._symbol_number[s2_char] || 0
if (s1_char_value === s2_char_value) {
continue
} else if (s1_char_value < s2_char_value) {
return -1
} else {
return 1
}
}
return 0
}
private remove_left_zeros(s: string): string {
let zero_char = this._number_symbol[0]
let idx = 0
for (; idx < s.length && s[idx] === zero_char; idx++);
let result = s.slice(idx)
if(result.length < 1){
result = this._number_symbol[0]
}
return result
}
}