#![allow(missing_docs)]
use uor_foundation::enforcement::{GroundedShape, ShapeViolation};
use uor_foundation::pipeline::{
AxisExtension, ConstrainedTypeShape, ConstraintRef, IntoBindingValue,
};
use uor_foundation_sdk::axis;
use crate::{check_output, split_pair};
axis! {
pub trait BigIntAxis: AxisExtension {
const AXIS_ADDRESS: &'static str = "https://uor.foundation/axis/BigIntAxis";
const MAX_OUTPUT_BYTES: usize = 32;
fn add(input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation>;
fn sub(input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation>;
fn mul(input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation>;
}
}
pub const MAX_BIG_INT_BYTES: usize = 64;
const ACC_CAP: usize = 2 * MAX_BIG_INT_BYTES;
fn width_violation() -> ShapeViolation {
ShapeViolation {
shape_iri: "https://uor.foundation/axis/BigIntAxis",
constraint_iri: "https://uor.foundation/axis/BigIntAxis/widthInRange",
property_iri: "https://uor.foundation/axis/operandByteWidth",
expected_range: "https://uor.foundation/axis/BigIntAxis/MaxBigIntBytes",
min_count: 1,
#[allow(clippy::cast_possible_truncation)]
max_count: MAX_BIG_INT_BYTES as u32,
kind: uor_foundation::ViolationKind::ValueCheck,
}
}
#[derive(Debug, Clone, Copy)]
pub struct BigIntModularNumeric<const BYTES: usize>;
impl<const BYTES: usize> Default for BigIntModularNumeric<BYTES> {
fn default() -> Self {
Self
}
}
impl<const BYTES: usize> BigIntAxis for BigIntModularNumeric<BYTES> {
const AXIS_ADDRESS: &'static str = "https://uor.foundation/axis/BigIntAxis/Modular";
const MAX_OUTPUT_BYTES: usize = BYTES;
fn add(input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation> {
if BYTES == 0 || BYTES > MAX_BIG_INT_BYTES {
return Err(width_violation());
}
let (a, b) = split_pair(input, BYTES)?;
check_output(out, BYTES)?;
let mut carry: u16 = 0;
for i in (0..BYTES).rev() {
let sum = u16::from(a[i]) + u16::from(b[i]) + carry;
#[allow(clippy::cast_possible_truncation)]
{
out[i] = (sum & 0xff) as u8;
}
carry = sum >> 8;
}
Ok(BYTES)
}
fn sub(input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation> {
if BYTES == 0 || BYTES > MAX_BIG_INT_BYTES {
return Err(width_violation());
}
let (a, b) = split_pair(input, BYTES)?;
check_output(out, BYTES)?;
let mut borrow: i16 = 0;
for i in (0..BYTES).rev() {
let diff = i16::from(a[i]) - i16::from(b[i]) - borrow;
if diff < 0 {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
{
out[i] = (diff + 256) as u8;
}
borrow = 1;
} else {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
{
out[i] = diff as u8;
}
borrow = 0;
}
}
Ok(BYTES)
}
fn mul(input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation> {
if BYTES == 0 || BYTES > MAX_BIG_INT_BYTES {
return Err(width_violation());
}
let (a, b) = split_pair(input, BYTES)?;
check_output(out, BYTES)?;
let mut acc = [0u32; ACC_CAP];
for i in (0..BYTES).rev() {
for j in (0..BYTES).rev() {
let prod = u32::from(a[i]) * u32::from(b[j]);
let pos = i + j + 1;
let sum = acc[pos] + (prod & 0xff);
acc[pos] = sum & 0xff;
let mut carry = (sum >> 8) + (prod >> 8);
let mut k = pos;
while carry > 0 && k > 0 {
k -= 1;
let next = acc[k] + carry;
acc[k] = next & 0xff;
carry = next >> 8;
}
}
}
for i in 0..BYTES {
#[allow(clippy::cast_possible_truncation)]
{
out[i] = (acc[i + BYTES] & 0xff) as u8;
}
}
Ok(BYTES)
}
}
axis_extension_impl_for_big_int_axis!(@generic BigIntModularNumeric<BYTES>, [const BYTES: usize]);
pub type BigInt256Numeric = BigIntModularNumeric<32>;
pub type BigInt512Numeric = BigIntModularNumeric<64>;
pub type BigInt128Numeric = BigIntModularNumeric<16>;
pub type BigInt64Numeric = BigIntModularNumeric<8>;
#[derive(Debug, Clone, Copy)]
pub struct BigIntShape<const BYTES: usize>;
impl<const BYTES: usize> Default for BigIntShape<BYTES> {
fn default() -> Self {
Self
}
}
impl<const BYTES: usize> ConstrainedTypeShape for BigIntShape<BYTES> {
const IRI: &'static str = "https://uor.foundation/type/ConstrainedType";
const SITE_COUNT: usize = BYTES;
const CONSTRAINTS: &'static [ConstraintRef] = &[];
#[allow(clippy::cast_possible_truncation)]
const CYCLE_SIZE: u64 = 256u64.saturating_pow(BYTES as u32);
}
impl<const BYTES: usize> uor_foundation::pipeline::__sdk_seal::Sealed for BigIntShape<BYTES> {}
impl<const BYTES: usize> GroundedShape for BigIntShape<BYTES> {}
impl<const BYTES: usize> IntoBindingValue for BigIntShape<BYTES> {
const MAX_BYTES: usize = BYTES;
fn into_binding_bytes(&self, _out: &mut [u8]) -> Result<usize, ShapeViolation> {
Ok(0)
}
}
impl<const BYTES: usize> uor_foundation::pipeline::PartitionProductFields for BigIntShape<BYTES> {
const FIELDS: &'static [(u32, u32)] = &[];
const FIELD_NAMES: &'static [&'static str] = &[];
}