#![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 FixedPointAxis: AxisExtension {
const AXIS_ADDRESS: &'static str = "https://uor.foundation/axis/FixedPointAxis";
const MAX_OUTPUT_BYTES: usize = 8;
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>;
}
}
const WIDTH: usize = 8;
fn format_violation() -> ShapeViolation {
ShapeViolation {
shape_iri: "https://uor.foundation/axis/FixedPointAxis",
constraint_iri: "https://uor.foundation/axis/FixedPointAxis/iPlusFInRange",
property_iri: "https://uor.foundation/axis/qFormatTotalBits",
expected_range: "https://uor.foundation/axis/FixedPointAxis/I64Fit",
min_count: 1,
max_count: 64,
kind: uor_foundation::ViolationKind::ValueCheck,
}
}
fn decode(slice: &[u8]) -> i64 {
let mut buf = [0u8; 8];
buf.copy_from_slice(&slice[..8]);
i64::from_be_bytes(buf)
}
fn encode(value: i64) -> [u8; 8] {
value.to_be_bytes()
}
#[derive(Debug, Clone, Copy)]
pub struct FixedPointQNumeric<const INT_BITS: u32, const FRAC_BITS: u32>;
impl<const I: u32, const F: u32> Default for FixedPointQNumeric<I, F> {
fn default() -> Self {
Self
}
}
impl<const INT_BITS: u32, const FRAC_BITS: u32> FixedPointAxis
for FixedPointQNumeric<INT_BITS, FRAC_BITS>
{
const AXIS_ADDRESS: &'static str = "https://uor.foundation/axis/FixedPointAxis/Q";
const MAX_OUTPUT_BYTES: usize = WIDTH;
fn add(input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation> {
if INT_BITS + FRAC_BITS == 0 || INT_BITS + FRAC_BITS > 64 {
return Err(format_violation());
}
let (a, b) = split_pair(input, WIDTH)?;
check_output(out, WIDTH)?;
let result = decode(a).saturating_add(decode(b));
out[..WIDTH].copy_from_slice(&encode(result));
Ok(WIDTH)
}
fn sub(input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation> {
if INT_BITS + FRAC_BITS == 0 || INT_BITS + FRAC_BITS > 64 {
return Err(format_violation());
}
let (a, b) = split_pair(input, WIDTH)?;
check_output(out, WIDTH)?;
let result = decode(a).saturating_sub(decode(b));
out[..WIDTH].copy_from_slice(&encode(result));
Ok(WIDTH)
}
fn mul(input: &[u8], out: &mut [u8]) -> Result<usize, ShapeViolation> {
if INT_BITS + FRAC_BITS == 0 || INT_BITS + FRAC_BITS > 64 {
return Err(format_violation());
}
let (a, b) = split_pair(input, WIDTH)?;
check_output(out, WIDTH)?;
let product = i128::from(decode(a)) * i128::from(decode(b));
let rescaled = product >> FRAC_BITS;
let saturated: i64 = if rescaled > i128::from(i64::MAX) {
i64::MAX
} else if rescaled < i128::from(i64::MIN) {
i64::MIN
} else {
#[allow(clippy::cast_possible_truncation)]
{
rescaled as i64
}
};
out[..WIDTH].copy_from_slice(&encode(saturated));
Ok(WIDTH)
}
}
axis_extension_impl_for_fixed_point_axis!(
@generic FixedPointQNumeric<INT_BITS, FRAC_BITS>,
[const INT_BITS: u32, const FRAC_BITS: u32]
);
pub type FixedPointQ32_32Numeric = FixedPointQNumeric<32, 32>;
pub type FixedPointQ16_16Numeric = FixedPointQNumeric<16, 16>;
pub type FixedPointQ1_31Numeric = FixedPointQNumeric<1, 31>;
pub type FixedPointQ48_16Numeric = FixedPointQNumeric<48, 16>;
#[derive(Debug, Clone, Copy)]
pub struct FixedPointShape<const INT_BITS: u32, const FRAC_BITS: u32>;
impl<const I: u32, const F: u32> Default for FixedPointShape<I, F> {
fn default() -> Self {
Self
}
}
impl<const INT_BITS: u32, const FRAC_BITS: u32> ConstrainedTypeShape
for FixedPointShape<INT_BITS, FRAC_BITS>
{
const IRI: &'static str = "https://uor.foundation/type/ConstrainedType";
const SITE_COUNT: usize = WIDTH;
const CONSTRAINTS: &'static [ConstraintRef] = &[];
#[allow(clippy::cast_possible_truncation)]
const CYCLE_SIZE: u64 = 256u64.saturating_pow(WIDTH as u32);
}
impl<const INT_BITS: u32, const FRAC_BITS: u32> uor_foundation::pipeline::__sdk_seal::Sealed
for FixedPointShape<INT_BITS, FRAC_BITS>
{
}
impl<const INT_BITS: u32, const FRAC_BITS: u32> GroundedShape
for FixedPointShape<INT_BITS, FRAC_BITS>
{
}
impl<const INT_BITS: u32, const FRAC_BITS: u32> IntoBindingValue
for FixedPointShape<INT_BITS, FRAC_BITS>
{
const MAX_BYTES: usize = WIDTH;
fn into_binding_bytes(&self, _out: &mut [u8]) -> Result<usize, ShapeViolation> {
Ok(0)
}
}