use crate::stable_hash;
pub use inner::BigInt;
use num_bigint::BigInt as BI;
use num_traits::{FromPrimitive, One, Signed, ToPrimitive, Zero};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Sign {
Minus,
NoSign,
Plus,
}
impl From<num_bigint::Sign> for Sign {
#[inline]
fn from(value: num_bigint::Sign) -> Self {
match value {
num_bigint::Sign::Minus => Self::Minus,
num_bigint::Sign::NoSign => Self::NoSign,
num_bigint::Sign::Plus => Self::Plus,
}
}
}
impl From<Sign> for num_bigint::Sign {
#[inline]
fn from(value: Sign) -> Self {
match value {
Sign::Minus => Self::Minus,
Sign::NoSign => Self::NoSign,
Sign::Plus => Self::Plus,
}
}
}
pub(crate) mod inner {
use num_bigint::BigInt as BIO;
#[repr(transparent)]
#[derive(
Eq, PartialEq, PartialOrd, Ord, Clone, Hash, Default, serde::Serialize, serde::Deserialize,
)]
#[serde(transparent)]
pub struct BigInt(BIO);
impl BigInt {
pub fn new(inner: BIO) -> Self {
Self(inner)
}
#[inline(always)]
pub fn into_inner(self) -> BIO {
self.0
}
#[inline(always)]
pub fn as_inner(&self) -> &BIO {
&self.0
}
#[inline(always)]
pub(crate) fn as_mut_inner(&mut self) -> &mut BIO {
&mut self.0
}
}
}
impl BigInt {
pub fn abs(&self) -> Self {
self.as_inner().abs().into()
}
pub fn sign(&self) -> Sign {
self.as_inner().sign().into()
}
pub fn to_bytes_le(&self) -> (Sign, Vec<u8>) {
let (s, v) = self.as_inner().to_bytes_le();
(s.into(), v)
}
pub fn to_bytes_be(&self) -> (Sign, Vec<u8>) {
let (s, v) = self.as_inner().to_bytes_be();
(s.into(), v)
}
pub fn checked_add(&self, v: &Self) -> Option<Self> {
self.as_inner().checked_add(v.as_inner()).map(Self::new)
}
pub fn checked_sub(&self, v: &Self) -> Option<Self> {
self.as_inner().checked_sub(v.as_inner()).map(Self::new)
}
pub fn checked_mul(&self, v: &Self) -> Option<Self> {
self.as_inner().checked_mul(v.as_inner()).map(Self::new)
}
pub fn checked_div(&self, v: &Self) -> Option<Self> {
self.as_inner().checked_div(v.as_inner()).map(Self::new)
}
pub fn from_bytes_be(sign: impl Into<Sign>, bytes: &[u8]) -> Self {
Self::new(BI::from_bytes_be(sign.into().into(), bytes))
}
pub fn from_bytes_le(sign: impl Into<Sign>, bytes: &[u8]) -> Self {
Self::new(BI::from_bytes_le(sign.into().into(), bytes))
}
pub fn from_signed_bytes_be(bytes: &[u8]) -> Self {
Self::new(BI::from_signed_bytes_be(bytes))
}
pub fn from_signed_bytes_le(bytes: &[u8]) -> Self {
Self::new(BI::from_signed_bytes_le(bytes))
}
pub fn from_radix_be(sign: impl Into<Sign>, buf: &[u8], radix: u32) -> Option<Self> {
BI::from_radix_be(sign.into().into(), buf, radix).map(Self::new)
}
pub fn from_radix_le(sign: impl Into<Sign>, buf: &[u8], radix: u32) -> Option<Self> {
BI::from_radix_le(sign.into().into(), buf, radix).map(Self::new)
}
pub fn from_slice(sign: impl Into<Sign>, slice: &[u32]) -> Self {
Self::new(BI::from_slice(sign.into().into(), slice))
}
}
impl From<BI> for BigInt {
fn from(value: BI) -> Self {
Self::new(value)
}
}
impl From<BigInt> for bigdecimal::BigDecimal {
fn from(bi: BigInt) -> Self {
Self::from(&bi)
}
}
impl From<&BigInt> for bigdecimal::BigDecimal {
fn from(bi: &BigInt) -> Self {
Self::from(bi.as_inner().clone())
}
}
macro_rules! impl_from {
($ty:ty) => {
impl From<$ty> for BigInt {
fn from(v: $ty) -> Self {
Self::new(BI::from(v))
}
}
};
}
impl_from!(u8);
impl_from!(u16);
impl_from!(u32);
impl_from!(u64);
impl_from!(u128);
impl_from!(usize);
impl_from!(i8);
impl_from!(i16);
impl_from!(i32);
impl_from!(i64);
impl_from!(i128);
impl_from!(isize);
impl std::str::FromStr for BigInt {
type Err = <BI as std::str::FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
BI::from_str(s).map(Self::new)
}
}
impl core::fmt::Display for BigInt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_inner().fmt(f)
}
}
impl core::fmt::Debug for BigInt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<BI as core::fmt::Display>::fmt(self.as_inner(), f)
}
}
mod sql {
use super::*;
use bigdecimal::BigDecimal as BD;
use sqlx::Postgres;
impl sqlx::Type<Postgres> for BigInt {
fn type_info() -> <Postgres as sqlx::Database>::TypeInfo {
<BD as sqlx::Type<Postgres>>::type_info()
}
}
impl<'q> sqlx::Encode<'q, Postgres> for BigInt {
fn encode_by_ref(
&self,
buf: &mut <Postgres as sqlx::Database>::ArgumentBuffer<'q>,
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync + 'static>> {
let bd = bigdecimal::BigDecimal::from(self);
bd.encode(buf)
}
fn encode(
self,
buf: &mut <Postgres as sqlx::Database>::ArgumentBuffer<'q>,
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync + 'static>>
where
Self: Sized,
{
let bd = bigdecimal::BigDecimal::from(self);
bd.encode(buf)
}
}
impl<'r> sqlx::Decode<'r, Postgres> for BigInt {
fn decode(
value: <Postgres as sqlx::Database>::ValueRef<'r>,
) -> Result<Self, sqlx::error::BoxDynError> {
let bd = <BD as sqlx::Decode<Postgres>>::decode(value)?;
let (mut bi, exp) = bd.into_bigint_and_exponent();
if exp > 0 {
return Err(
format!("BigInt unable to convert from {bi} due to positive exponent {exp}").into(),
);
}
let mut exp = exp.unsigned_abs() as u32;
while exp >= 16 {
bi *= 10_u64.pow(16);
exp -= 16;
}
if exp > 0 {
bi *= 10_u64.pow(exp);
}
Ok(bi.into())
}
}
impl sqlx::postgres::PgHasArrayType for BigInt {
fn array_type_info() -> sqlx::postgres::PgTypeInfo {
<BD as sqlx::postgres::PgHasArrayType>::array_type_info()
}
}
}
impl<'x> std::ops::Add for &'x BigInt {
type Output = BigInt;
fn add(self, rhs: &'x BigInt) -> Self::Output {
BigInt::new(self.as_inner() + rhs.as_inner())
}
}
impl<'x> std::ops::Add<BigInt> for &'x BigInt {
type Output = BigInt;
fn add(self, rhs: BigInt) -> Self::Output {
BigInt::new(self.as_inner() + rhs.into_inner())
}
}
impl<T: Into<BigInt>> std::ops::Add<T> for BigInt {
type Output = BigInt;
fn add(self, rhs: T) -> Self::Output {
Self::new(self.into_inner() + rhs.into().into_inner())
}
}
impl<T: Into<BigInt>> std::ops::AddAssign<T> for BigInt {
fn add_assign(&mut self, rhs: T) {
*self.as_mut_inner() += rhs.into().into_inner();
}
}
impl<'x> std::ops::Sub for &'x BigInt {
type Output = BigInt;
fn sub(self, rhs: &'x BigInt) -> Self::Output {
BigInt::new(self.as_inner() - rhs.as_inner())
}
}
impl<'x> std::ops::Sub<BigInt> for &'x BigInt {
type Output = BigInt;
fn sub(self, rhs: BigInt) -> Self::Output {
BigInt::new(self.as_inner() - rhs.into_inner())
}
}
impl<T: Into<BigInt>> std::ops::Sub<T> for BigInt {
type Output = BigInt;
fn sub(self, rhs: T) -> Self::Output {
Self::new(self.into_inner() - rhs.into().into_inner())
}
}
impl<T: Into<BigInt>> std::ops::SubAssign<T> for BigInt {
fn sub_assign(&mut self, rhs: T) {
*self.as_mut_inner() -= rhs.into().into_inner();
}
}
impl<'x> std::ops::Mul for &'x BigInt {
type Output = BigInt;
fn mul(self, rhs: &'x BigInt) -> Self::Output {
BigInt::new(self.as_inner() * rhs.as_inner())
}
}
impl<'x> std::ops::Mul<BigInt> for &'x BigInt {
type Output = BigInt;
fn mul(self, rhs: BigInt) -> Self::Output {
BigInt::new(self.as_inner() * rhs.into_inner())
}
}
impl<T: Into<BigInt>> std::ops::Mul<T> for BigInt {
type Output = BigInt;
fn mul(self, rhs: T) -> Self::Output {
Self::new(self.into_inner() * rhs.into().into_inner())
}
}
impl<T: Into<BigInt>> std::ops::MulAssign<T> for BigInt {
fn mul_assign(&mut self, rhs: T) {
*self.as_mut_inner() *= rhs.into().into_inner();
}
}
impl<'x> std::ops::Div for &'x BigInt {
type Output = BigInt;
fn div(self, rhs: &'x BigInt) -> Self::Output {
BigInt::new(self.as_inner() / rhs.as_inner())
}
}
impl<'x> std::ops::Div<BigInt> for &'x BigInt {
type Output = BigInt;
fn div(self, rhs: BigInt) -> Self::Output {
BigInt::new(self.as_inner() / rhs.into_inner())
}
}
impl<T: Into<BigInt>> std::ops::Div<T> for BigInt {
type Output = BigInt;
fn div(self, rhs: T) -> Self::Output {
Self::new(self.into_inner() / rhs.into().into_inner())
}
}
impl<T: Into<BigInt>> std::ops::DivAssign<T> for BigInt {
fn div_assign(&mut self, rhs: T) {
Self::new(self.as_inner() / rhs.into().into_inner());
}
}
impl std::ops::Neg for BigInt {
type Output = BigInt;
fn neg(self) -> Self::Output {
Self::new(self.into_inner().neg())
}
}
impl One for BigInt {
fn one() -> Self {
Self::new(BI::one())
}
}
impl Zero for BigInt {
fn zero() -> Self {
Self::new(BI::zero())
}
fn is_zero(&self) -> bool {
self.as_inner().is_zero()
}
}
impl ToPrimitive for BigInt {
fn to_i64(&self) -> Option<i64> {
self.as_inner().to_i64()
}
fn to_u64(&self) -> Option<u64> {
self.as_inner().to_u64()
}
fn to_isize(&self) -> Option<isize> {
self.as_inner().to_isize()
}
fn to_i8(&self) -> Option<i8> {
self.as_inner().to_i8()
}
fn to_i16(&self) -> Option<i16> {
self.as_inner().to_i16()
}
fn to_i32(&self) -> Option<i32> {
self.as_inner().to_i32()
}
fn to_i128(&self) -> Option<i128> {
self.as_inner().to_i128()
}
fn to_usize(&self) -> Option<usize> {
self.as_inner().to_usize()
}
fn to_u8(&self) -> Option<u8> {
self.as_inner().to_u8()
}
fn to_u16(&self) -> Option<u16> {
self.as_inner().to_u16()
}
fn to_u32(&self) -> Option<u32> {
self.as_inner().to_u32()
}
fn to_u128(&self) -> Option<u128> {
self.as_inner().to_u128()
}
fn to_f32(&self) -> Option<f32> {
self.as_inner().to_f32()
}
fn to_f64(&self) -> Option<f64> {
self.as_inner().to_f64()
}
}
impl FromPrimitive for BigInt {
fn from_i64(n: i64) -> Option<Self> {
BI::from_i64(n).map(Self::new)
}
fn from_u64(n: u64) -> Option<Self> {
BI::from_u64(n).map(Self::new)
}
fn from_isize(n: isize) -> Option<Self> {
BI::from_isize(n).map(Self::new)
}
fn from_i8(n: i8) -> Option<Self> {
BI::from_i8(n).map(Self::new)
}
fn from_i16(n: i16) -> Option<Self> {
BI::from_i16(n).map(Self::new)
}
fn from_i32(n: i32) -> Option<Self> {
BI::from_i32(n).map(Self::new)
}
fn from_i128(n: i128) -> Option<Self> {
BI::from_i128(n).map(Self::new)
}
fn from_usize(n: usize) -> Option<Self> {
BI::from_usize(n).map(Self::new)
}
fn from_u8(n: u8) -> Option<Self> {
BI::from_u8(n).map(Self::new)
}
fn from_u16(n: u16) -> Option<Self> {
BI::from_u16(n).map(Self::new)
}
fn from_u32(n: u32) -> Option<Self> {
BI::from_u32(n).map(Self::new)
}
fn from_u128(n: u128) -> Option<Self> {
BI::from_u128(n).map(Self::new)
}
fn from_f32(n: f32) -> Option<Self> {
BI::from_f32(n).map(Self::new)
}
fn from_f64(n: f64) -> Option<Self> {
BI::from_f64(n).map(Self::new)
}
}
mod graphql {
use super::{BI, BigInt};
use async_graphql::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
#[Scalar(name = "BigInt")]
impl ScalarType for BigInt {
fn parse(value: Value) -> InputValueResult<Self> {
match &value {
Value::Number(n) => {
if n.is_f64() {
return Err(InputValueError::custom("Floating point values not allowed"));
}
if let Some(f) = n.as_i64() {
return Ok(BigInt::from(BI::from(f)));
}
Ok(BigInt::from(BI::from(n.as_u64().unwrap())))
}
Value::String(s) => s.parse().map_err(InputValueError::custom),
_ => Err(InputValueError::expected_type(value)),
}
}
fn to_value(&self) -> Value {
Value::String(self.to_string())
}
}
}
impl stable_hash::StableHash for BigInt {
fn stable_hash<H: stable_hash::StableHasher>(&self, field_address: H::Addr, state: &mut H) {
let (sign, bytes) = self.to_bytes_le();
stable_hash::utils::AsInt {
is_negative: sign == Sign::Minus,
little_endian: &bytes,
}
.stable_hash(field_address, state);
}
}
#[test]
fn test_integer_hash() {
use stable_hash::{StableHash, StableHasher, fast::FastStableHasher};
fn hash_bigint(b: BigInt) -> [u8; 32] {
let mut fsh = FastStableHasher::new();
b.stable_hash(0, &mut fsh);
fsh.to_bytes()
}
let normal_bytes = hash_bigint("-1".parse::<BigInt>().unwrap());
let from_i32 = hash_bigint(BigInt::from(-1_i32));
let two_bytes = hash_bigint(BigInt::from_signed_bytes_be(&[0xff, 0xff]));
let four_bytes = hash_bigint(BigInt::from_signed_bytes_be(&[0xff, 0xff, 0xff, 0xff]));
assert_eq!(normal_bytes, from_i32);
assert_eq!(normal_bytes, two_bytes);
assert_eq!(normal_bytes, four_bytes);
}