UNPKG

@rniv/base-n

Version:
180 lines (164 loc) 5.41 kB
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 } }