use num_bigint::BigInt;
use num_traits::{pow, Signed};
use once_cell::sync::Lazy;
use std::io::Error;
use std::io::ErrorKind;
pub static BIG_ZERO: Lazy<BigInt> = Lazy::new(|| BigInt::from(0));
pub static BIG_ONE: Lazy<BigInt> = Lazy::new(|| BigInt::from(1));
pub static BIG_TWO: Lazy<BigInt> = Lazy::new(|| BigInt::from(2));
pub fn bigint_to_bytes(v_: &BigInt, signed: bool) -> Result<Vec<u8>, Error> {
let v = v_.clone();
if v == *BIG_ZERO {
return Ok(vec![]);
}
if !signed && v.is_negative() {
return Err(Error::new(
ErrorKind::InvalidInput,
"OverflowError: can't convert negative int to unsigned".to_string(),
));
}
let mut byte_count = 1;
let mut dec = 0;
let div = if signed {
BIG_ONE.clone()
} else {
BIG_ZERO.clone()
};
let b_pow = BigInt::from(1_u64 << 32); if v.is_negative() {
let mut right_hand = (-(v.clone()) + BIG_ONE.clone()) * (div + BIG_ONE.clone());
while pow(b_pow.clone(), (byte_count - 1) / 4 + 1) < right_hand {
byte_count += 4;
}
right_hand = -(v.clone()) * BIG_TWO.clone();
while pow(BIG_TWO.clone(), 8 * byte_count) < right_hand {
byte_count += 1;
}
} else {
let mut right_hand = (v.clone() + BIG_ONE.clone()) * (div.clone() + BIG_ONE.clone());
while pow(b_pow.clone(), (byte_count - 1) / 4 + 1) < right_hand {
byte_count += 4;
}
right_hand = (v.clone() + BIG_ONE.clone()) * (div + BIG_ONE.clone());
while pow(BigInt::from(2_u32), 8 * byte_count) < right_hand {
byte_count += 1;
}
}
let extra_byte = usize::from(
signed
&& v > *BIG_ZERO
&& ((v.clone() >> ((byte_count - 1) * 8)) & BigInt::from(0x80_u32)) > *BIG_ZERO,
);
let total_bytes = byte_count + extra_byte;
let mut dv = Vec::<u8>::with_capacity(total_bytes);
let byte4_remain = byte_count % 4;
let byte4_length = (byte_count - byte4_remain) / 4;
dv.resize(total_bytes, 0);
let (_sign, u32_digits) = v.to_u32_digits();
for (i, n) in u32_digits.iter().take(byte4_length).enumerate() {
let word_idx = byte4_length - i - 1;
let num = *n as u64;
let pointer = extra_byte + byte4_remain + word_idx * 4;
let setval = if v.is_negative() {
(1_u64 << 32) - num - dec as u64
} else {
num
};
dec = 1;
set_u32(&mut dv, pointer, setval as u32);
}
let last_bytes = u32_digits[u32_digits.len() - 1];
let transform = |idx| {
if v.is_negative() {
(((1 << (8 * byte4_remain)) - last_bytes - dec) >> (8 * idx)) as u8
} else {
(last_bytes >> (8 * idx)) as u8
}
};
for i in 0..byte4_remain {
set_u8(&mut dv, extra_byte + i, transform(byte4_remain - i - 1));
}
Ok(dv)
}
pub fn set_u8(vec: &mut [u8], n: usize, v: u8) {
vec[n] = v;
}
pub fn set_u32(vec: &mut [u8], n: usize, v: u32) {
vec[n] = ((v >> 24) & 0xff) as u8;
vec[n + 1] = ((v >> 16) & 0xff) as u8;
vec[n + 2] = ((v >> 8) & 0xff) as u8;
vec[n + 3] = (v & 0xff) as u8;
}