118 lines
2.7 KiB
TypeScript
118 lines
2.7 KiB
TypeScript
import bigInt, { BigInteger } from 'big-integer'
|
|
|
|
import { randomBytes } from './buffer-utils'
|
|
|
|
/**
|
|
* Convert a big integer to a buffer
|
|
*
|
|
* @param value Value to convert
|
|
* @param length Length of the resulting buffer (by default it's computed automatically)
|
|
* @param le Whether to use little-endian encoding
|
|
*/
|
|
export function bigIntToBuffer(
|
|
value: BigInteger,
|
|
length = 0,
|
|
le = false,
|
|
): Buffer {
|
|
const array = value.toArray(256).value
|
|
|
|
if (length !== 0 && array.length > length) {
|
|
throw new Error('Value out of bounds')
|
|
}
|
|
|
|
if (length !== 0) {
|
|
// padding
|
|
while (array.length !== length) array.unshift(0)
|
|
}
|
|
|
|
if (le) array.reverse()
|
|
|
|
const buffer = Buffer.alloc(length || array.length)
|
|
buffer.set(array, 0)
|
|
|
|
return buffer
|
|
}
|
|
|
|
/**
|
|
* Convert a buffer to a big integer
|
|
*
|
|
* @param buffer Buffer to convert
|
|
* @param offset Offset to start reading from
|
|
* @param length Length to read
|
|
* @param le Whether to use little-endian encoding
|
|
*/
|
|
export function bufferToBigInt(
|
|
buffer: Buffer,
|
|
offset = 0,
|
|
length = buffer.length,
|
|
le = false,
|
|
): BigInteger {
|
|
const arr = [...buffer.slice(offset, offset + length)]
|
|
|
|
if (le) arr.reverse()
|
|
|
|
return bigInt.fromArray(arr, 256)
|
|
}
|
|
|
|
/**
|
|
* Generate a random big integer of the given size (in bytes)
|
|
* @param size Size in bytes
|
|
*/
|
|
export function randomBigInt(size: number): BigInteger {
|
|
return bufferToBigInt(randomBytes(size))
|
|
}
|
|
|
|
/**
|
|
* Generate a random big integer of the given size (in bits)
|
|
* @param bits
|
|
*/
|
|
export function randomBigIntBits(bits: number): BigInteger {
|
|
let num = randomBigInt(Math.ceil(bits / 8))
|
|
|
|
const bitLength = num.bitLength()
|
|
|
|
if (bitLength.gt(bits)) {
|
|
const toTrim = bigInt.randBetween(bitLength.minus(bits), 8)
|
|
num = num.shiftRight(toTrim)
|
|
}
|
|
|
|
return num
|
|
}
|
|
|
|
/**
|
|
* Generate a random big integer in the range [min, max)
|
|
*
|
|
* @param max Maximum value (exclusive)
|
|
* @param min Minimum value (inclusive)
|
|
*/
|
|
export function randomBigIntInRange(
|
|
max: BigInteger,
|
|
min = bigInt.one,
|
|
): BigInteger {
|
|
const interval = max.minus(min)
|
|
if (interval.isNegative()) throw new Error('expected min < max')
|
|
|
|
const byteSize = interval.bitLength().divide(8).toJSNumber()
|
|
|
|
let result = randomBigInt(byteSize)
|
|
while (result.gt(interval)) result = result.minus(interval)
|
|
|
|
return min.plus(result)
|
|
}
|
|
|
|
/**
|
|
* Compute the multiplicity of 2 in the prime factorization of n
|
|
* @param n
|
|
*/
|
|
export function twoMultiplicity(n: BigInteger): BigInteger {
|
|
if (n === bigInt.zero) return bigInt.zero
|
|
|
|
let m = bigInt.zero
|
|
let pow = bigInt.one
|
|
|
|
while (true) {
|
|
if (!n.and(pow).isZero()) return m
|
|
m = m.plus(bigInt.one)
|
|
pow = pow.shiftLeft(1)
|
|
}
|
|
}
|