use alloc::format;
use alloc::string::{String, ToString};
use core::fmt::{
self, Debug, Display, Formatter, LowerExp, LowerHex, Octal, UpperExp, UpperHex, Write,
};
use core::hash::{Hash, Hasher};
use core::ops::{
Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
};
use core::str::FromStr;
use amplify::num::apfloat::{ieee, Float, Status, StatusAnd};
use amplify::num::{i1024, i256, i512, u1024, u256, u512};
use half::bf16;
pub trait NumberLayout: Copy {
#[inline]
fn bits(self) -> u16 { self.bytes() * 8 }
fn bytes(self) -> u16;
fn is_signed(self) -> bool;
#[inline]
fn is_fixed_width(self) -> bool { true }
fn sign_bit(self) -> u16;
fn sign_byte(self) -> u16;
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)]
#[display(inner)]
pub enum Layout {
Integer(IntLayout),
Float(FloatLayout),
}
impl Layout {
#[inline]
pub fn signed(bytes: u16) -> Layout { Layout::Integer(IntLayout::signed(bytes)) }
#[inline]
pub fn unsigned(bytes: u16) -> Layout { Layout::Integer(IntLayout::unsigned(bytes)) }
#[inline]
pub fn float(layout: FloatLayout) -> Layout { Layout::Float(layout) }
#[inline]
pub fn is_unsigned_int(self) -> bool {
matches!(self, Layout::Integer(IntLayout { signed: false, .. }))
}
#[inline]
pub fn is_signed_int(self) -> bool {
matches!(self, Layout::Integer(IntLayout { signed: true, .. }))
}
#[inline]
pub fn is_integer(self) -> bool { matches!(self, Layout::Integer(_)) }
#[inline]
pub fn is_float(self) -> bool { matches!(self, Layout::Float(_)) }
#[inline]
pub fn into_signed(mut self) -> Layout {
if let Layout::Integer(il) = &mut self {
*il = il.into_signed()
}
self
}
#[inline]
pub fn into_unsigned(mut self) -> Layout {
if let Layout::Integer(il) = &mut self {
*il = il.into_unsigned()
}
self
}
#[inline]
pub fn using_sign(mut self, other: Layout) -> Layout {
if let (Layout::Integer(il), Layout::Integer(il2)) = (&mut self, other) {
*il = il2.using_sign(il2)
}
self
}
}
impl NumberLayout for Layout {
#[inline]
fn bytes(self) -> u16 {
match self {
Layout::Integer(il) => il.bytes(),
Layout::Float(fl) => fl.bytes(),
}
}
#[inline]
fn is_signed(self) -> bool { matches!(self, Layout::Integer(IntLayout { signed: true, .. })) }
#[inline]
fn sign_bit(self) -> u16 {
match self {
Layout::Integer(il) => il.sign_bit(),
Layout::Float(fl) => fl.sign_bit(),
}
}
#[inline]
fn sign_byte(self) -> u16 {
match self {
Layout::Integer(il) => il.sign_byte(),
Layout::Float(fl) => fl.sign_byte(),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct IntLayout {
pub signed: bool,
pub bytes: u16,
}
impl Display for IntLayout {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_char(if self.signed { 'i' } else { 'u' })?;
write!(f, "{}", self.bits())
}
}
impl IntLayout {
#[inline]
pub fn signed(bytes: u16) -> IntLayout { Self { signed: true, bytes } }
#[inline]
pub fn unsigned(bytes: u16) -> IntLayout { Self { signed: false, bytes } }
#[inline]
pub fn into_signed(mut self) -> IntLayout {
self.signed = true;
self
}
#[inline]
pub fn into_unsigned(mut self) -> IntLayout {
self.signed = false;
self
}
#[inline]
pub fn using_sign(mut self, other: IntLayout) -> IntLayout {
self.signed = other.signed;
self
}
pub fn fits_usize(self, value: usize) -> bool {
(self.bytes() <= 8 && value > u64::MAX as usize)
|| (self.bytes() <= 4 && value > u32::MAX as usize)
|| (self.bytes() <= 2 && value > u16::MAX as usize)
|| (self.bytes() <= 1 && value > u8::MAX as usize)
}
}
impl NumberLayout for IntLayout {
#[inline]
fn bytes(self) -> u16 { self.bytes }
#[inline]
fn is_signed(self) -> bool { self.signed }
#[inline]
fn sign_bit(self) -> u16 { self.bits() - 1 }
#[inline]
fn sign_byte(self) -> u16 { self.bytes() - 1 }
}
impl From<IntLayout> for Layout {
#[inline]
fn from(layout: IntLayout) -> Self { Layout::Integer(layout) }
}
impl From<&IntLayout> for Layout {
#[inline]
fn from(layout: &IntLayout) -> Self { Layout::Integer(*layout) }
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)]
pub enum FloatLayout {
#[display("bfloat16")]
BFloat16 = 2,
#[display("ieee:binary16")]
IeeeHalf = 3,
#[display("ieee:binary32")]
IeeeSingle = 4,
#[display("ieee:binary64")]
IeeeDouble = 5,
#[display("x87:binary80")]
X87DoubleExt = 6,
#[display("ieee:binary128")]
IeeeQuad = 7,
#[display("ieee:binary256")]
IeeeOct = 8,
#[display("tapered:binary512")]
FloatTapered = 9,
}
impl NumberLayout for FloatLayout {
fn bytes(self) -> u16 {
match self {
FloatLayout::BFloat16 => 2,
FloatLayout::IeeeHalf => 2,
FloatLayout::IeeeSingle => 4,
FloatLayout::IeeeDouble => 8,
FloatLayout::X87DoubleExt => 10,
FloatLayout::IeeeQuad => 16,
FloatLayout::IeeeOct => 32,
FloatLayout::FloatTapered => 64,
}
}
#[inline]
fn is_signed(self) -> bool { true }
#[inline]
fn sign_bit(self) -> u16 { self.bits() - 1 }
#[inline]
fn sign_byte(self) -> u16 { self.bytes() - 1 }
}
impl FloatLayout {
pub fn with(value: u8) -> Option<Self> {
Some(match value {
x if x == FloatLayout::BFloat16 as u8 => FloatLayout::BFloat16,
x if x == FloatLayout::IeeeHalf as u8 => FloatLayout::IeeeHalf,
x if x == FloatLayout::IeeeSingle as u8 => FloatLayout::IeeeSingle,
x if x == FloatLayout::IeeeDouble as u8 => FloatLayout::IeeeDouble,
x if x == FloatLayout::IeeeQuad as u8 => FloatLayout::IeeeQuad,
x if x == FloatLayout::IeeeOct as u8 => FloatLayout::IeeeOct,
x if x == FloatLayout::X87DoubleExt as u8 => FloatLayout::X87DoubleExt,
x if x == FloatLayout::FloatTapered as u8 => FloatLayout::FloatTapered,
_ => return None,
})
}
#[inline]
pub fn is_float(self) -> bool { self as u8 > 1 }
#[inline]
pub fn is_tapered(self) -> bool { self == FloatLayout::FloatTapered }
#[inline]
pub fn significand_pos(self) -> Option<Range<u16>> {
match self {
FloatLayout::BFloat16 => Some(0..7),
FloatLayout::IeeeHalf => Some(0..10),
FloatLayout::IeeeSingle => Some(0..23),
FloatLayout::IeeeDouble => Some(0..52),
FloatLayout::X87DoubleExt => Some(0..64),
FloatLayout::IeeeQuad => Some(0..112),
FloatLayout::IeeeOct => Some(0..236),
FloatLayout::FloatTapered => None,
}
}
#[inline]
pub fn exponent_pos(self) -> Option<Range<u16>> {
match self {
FloatLayout::BFloat16 => Some(7..15),
FloatLayout::IeeeHalf => Some(10..15),
FloatLayout::IeeeSingle => Some(23..31),
FloatLayout::IeeeDouble => Some(52..63),
FloatLayout::X87DoubleExt => Some(64..79),
FloatLayout::IeeeQuad => Some(112..127),
FloatLayout::IeeeOct => Some(236..255),
FloatLayout::FloatTapered => None,
}
}
}
impl From<FloatLayout> for Layout {
#[inline]
fn from(layout: FloatLayout) -> Self { Layout::Float(layout) }
}
impl From<&FloatLayout> for Layout {
#[inline]
fn from(layout: &FloatLayout) -> Self { Layout::Float(*layout) }
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default, From)]
pub struct MaybeNumber(Option<Number>);
impl MaybeNumber {
#[inline]
pub fn none() -> MaybeNumber { MaybeNumber(None) }
#[inline]
pub fn some(val: Number) -> MaybeNumber { MaybeNumber(Some(val)) }
#[inline]
pub fn reshape(&mut self, to: Layout) -> bool {
match self.0 {
None => true,
Some(ref mut val) => val.reshape(to),
}
}
}
impl From<Number> for MaybeNumber {
fn from(val: Number) -> Self { MaybeNumber(Some(val)) }
}
impl From<&Number> for MaybeNumber {
fn from(val: &Number) -> Self { MaybeNumber(Some(*val)) }
}
impl From<&Option<Number>> for MaybeNumber {
fn from(val: &Option<Number>) -> Self { MaybeNumber(*val) }
}
impl From<Option<&Number>> for MaybeNumber {
fn from(val: Option<&Number>) -> Self { MaybeNumber(val.copied()) }
}
impl From<MaybeNumber> for Option<Number> {
fn from(val: MaybeNumber) -> Self { val.0 }
}
impl Deref for MaybeNumber {
type Target = Option<Number>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl Display for MaybeNumber {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.0 {
None => f.write_str("~"),
Some(ref val) => Display::fmt(val, f),
}
}
}
impl FromStr for MaybeNumber {
type Err = LiteralParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(if s.contains(&['p'][..]) {
ieee::Quad::from_str(s)?.into()
} else {
Number::from_str(s)?.into()
})
}
}
#[derive(Copy, Clone)]
pub struct Number {
bytes: [u8; 1024],
layout: Layout,
}
impl Hash for Number {
fn hash<H: Hasher>(&self, state: &mut H) {
let clean = self.to_clean();
clean.layout.hash(state);
state.write(&clean.bytes);
}
}
impl Default for Number {
fn default() -> Number {
Number { layout: Layout::Integer(IntLayout::unsigned(1)), bytes: [0u8; 1024] }
}
}
impl AsRef<[u8]> for Number {
fn as_ref(&self) -> &[u8] { &self[..] }
}
impl AsMut<[u8]> for Number {
fn as_mut(&mut self) -> &mut [u8] { &mut self[..] }
}
impl Index<u16> for Number {
type Output = u8;
fn index(&self, index: u16) -> &Self::Output {
assert!(index < self.len());
&self.bytes[index as usize]
}
}
impl IndexMut<u16> for Number {
fn index_mut(&mut self, index: u16) -> &mut Self::Output {
assert!(index < self.len());
&mut self.bytes[index as usize]
}
}
impl Index<RangeFull> for Number {
type Output = [u8];
fn index(&self, _: RangeFull) -> &Self::Output { &self.bytes[..self.len() as usize] }
}
impl IndexMut<RangeFull> for Number {
fn index_mut(&mut self, _: RangeFull) -> &mut Self::Output {
let len = self.len() as usize;
&mut self.bytes[..len]
}
}
impl Index<Range<u16>> for Number {
type Output = [u8];
fn index(&self, index: Range<u16>) -> &Self::Output {
assert!(index.start < self.len() && index.end <= self.len());
&self.bytes[index.start as usize..index.end as usize]
}
}
impl IndexMut<Range<u16>> for Number {
fn index_mut(&mut self, index: Range<u16>) -> &mut Self::Output {
assert!(index.start < self.len() && index.end <= self.len());
&mut self.bytes[index.start as usize..index.end as usize]
}
}
impl Index<RangeInclusive<u16>> for Number {
type Output = [u8];
fn index(&self, index: RangeInclusive<u16>) -> &Self::Output {
assert!(*index.start() < self.len() && *index.end() < self.len());
&self.bytes[*index.start() as usize..*index.end() as usize]
}
}
impl IndexMut<RangeInclusive<u16>> for Number {
fn index_mut(&mut self, index: RangeInclusive<u16>) -> &mut Self::Output {
&mut self.bytes[*index.start() as usize..*index.end() as usize]
}
}
impl Index<RangeFrom<u16>> for Number {
type Output = [u8];
fn index(&self, index: RangeFrom<u16>) -> &Self::Output {
assert!(index.start < self.len());
&self.bytes[index.start as usize..self.len() as usize]
}
}
impl IndexMut<RangeFrom<u16>> for Number {
fn index_mut(&mut self, index: RangeFrom<u16>) -> &mut Self::Output {
assert!(index.start < self.len());
let len = self.len() as usize;
&mut self.bytes[index.start as usize..len]
}
}
impl Index<RangeTo<u16>> for Number {
type Output = [u8];
fn index(&self, index: RangeTo<u16>) -> &Self::Output {
assert!(index.end <= self.len());
&self.bytes[..index.end as usize]
}
}
impl IndexMut<RangeTo<u16>> for Number {
fn index_mut(&mut self, index: RangeTo<u16>) -> &mut Self::Output {
assert!(index.end <= self.len());
&mut self.bytes[..index.end as usize]
}
}
impl Index<RangeToInclusive<u16>> for Number {
type Output = [u8];
fn index(&self, index: RangeToInclusive<u16>) -> &Self::Output {
assert!(index.end < self.len());
&self.bytes[..=index.end as usize]
}
}
impl IndexMut<RangeToInclusive<u16>> for Number {
fn index_mut(&mut self, index: RangeToInclusive<u16>) -> &mut Self::Output {
assert!(index.end < self.len());
&mut self.bytes[..=index.end as usize]
}
}
impl Number {
#[inline]
pub fn zero(layout: Layout) -> Number { Number { layout, bytes: [0u8; 1024] } }
#[inline]
pub fn masked_bit(bit_no: u16, layout: Layout) -> Number {
let mut zero = Number { layout, bytes: [0u8; 1024] };
zero.bytes[(bit_no / 8) as usize] = 1 << (bit_no % 8);
zero
}
pub fn with(slice: impl AsRef<[u8]>, layout: impl Into<Layout>) -> Option<Number> {
let layout = layout.into();
let slice = slice.as_ref();
if slice.len() != layout.bytes() as usize {
return None;
}
let mut me = Number::from_slice(slice);
me.layout = layout;
Some(me)
}
pub fn from_slice(slice: impl AsRef<[u8]>) -> Number {
let len = slice.as_ref().len();
let mut bytes = [0u8; 1024];
bytes[0..len].copy_from_slice(slice.as_ref());
Number { layout: Layout::unsigned(len as u16), bytes }
}
#[cfg(feature = "std")]
pub fn from_hex(s: &str) -> Result<Number, amplify::hex::Error> {
use amplify::hex::FromHex;
let s = s.trim_start_matches("0x");
let len = s.len() / 2;
if len > 1024 {
return Err(amplify::hex::Error::InvalidLength(1024, len));
}
let mut bytes = [0u8; 1024];
let hex = Vec::<u8>::from_hex(s)?;
bytes[0..len].copy_from_slice(&hex);
Ok(Number { layout: Layout::unsigned(hex.len() as u16), bytes })
}
#[cfg(feature = "std")]
pub fn to_hex(self) -> String {
let mut ret = String::with_capacity(2usize * self.len() as usize + 2);
write!(ret, "0x").expect("writing to string");
for ch in &self.bytes {
write!(ret, "{:02x}", ch).expect("writing to string");
}
ret
}
#[inline]
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> u16 { self.layout.bytes() }
#[inline]
pub fn layout(&self) -> Layout { self.layout }
#[inline]
pub fn count_zeros(&self) -> u16 { self.len() - self.count_ones() }
pub fn count_ones(&self) -> u16 {
let mut count = 0u16;
for byte in &self[..] {
count += byte.count_ones() as u16;
}
count
}
pub fn min_bit_len(&self) -> u16 {
if self.layout.is_float() {
return self.layout.bits();
}
if self.len() == 0 {
return 0;
}
let empty_bytes = self[..]
.iter()
.rev()
.take_while(|&&v| if self.is_negative() { v == 0xff } else { v == 0 })
.count() as u16;
let index = if self.len() > empty_bytes { self.len() - empty_bytes - 1 } else { 0 };
let head_bits = match self.is_negative() {
true => 8 - self[index].leading_ones(),
false => 8 - self[index].leading_zeros(),
};
index * 8 + head_bits as u16 + self.layout.is_signed() as u16
}
pub fn is_positive(self) -> bool {
if self.layout.is_unsigned_int() {
return true;
}
if self.is_zero() {
return false;
}
self[self.layout.sign_byte()] & 0x80 == 0
}
pub fn is_negative(self) -> bool { !self.is_zero() && !self.is_positive() }
pub fn is_zero(self) -> bool {
let mut clean = self.to_clean();
if self.layout.is_float() {
clean = clean.without_sign().expect("should not fail when it is float");
}
clean.bytes == [0; 1024]
}
pub fn is_nan(self) -> bool {
match self.layout {
Layout::Integer(_) => false,
Layout::Float(FloatLayout::BFloat16) => bf16::from(self).is_nan(),
Layout::Float(FloatLayout::IeeeHalf) => ieee::Half::from(self).is_nan(),
Layout::Float(FloatLayout::IeeeSingle) => ieee::Single::from(self).is_nan(),
Layout::Float(FloatLayout::IeeeDouble) => ieee::Double::from(self).is_nan(),
Layout::Float(FloatLayout::IeeeQuad) => ieee::Quad::from(self).is_nan(),
Layout::Float(FloatLayout::IeeeOct) => ieee::Oct::from(self).is_nan(),
Layout::Float(FloatLayout::X87DoubleExt) => {
ieee::X87DoubleExtended::from(self).is_nan()
}
Layout::Float(FloatLayout::FloatTapered) => todo!("(#5) tapered float NaN detection"),
}
}
pub fn is_max(self) -> bool {
match self.layout {
Layout::Integer(int_layout) => {
let mut mask = u1024::from(0u8);
for _ in 0..int_layout.bytes - int_layout.is_signed() as u16 {
mask <<= 1;
mask |= 1u8;
}
self.to_clean() == mask.into()
}
_ => false,
}
}
#[inline]
pub fn clean(&mut self) {
let len = self.len() as usize;
self.bytes[len..].fill(0);
}
#[inline]
pub fn to_clean(mut self) -> Self {
self.clean();
self
}
#[inline]
pub fn into_signed(mut self) -> Number {
if let Layout::Integer(il) = &mut self.layout {
*il = il.into_signed()
}
self
}
#[inline]
pub fn into_unsigned(mut self) -> Number {
if let Layout::Integer(il) = &mut self.layout {
*il = il.into_unsigned()
}
self
}
pub fn reshape(&mut self, to: Layout) -> bool {
match (self.layout, to) {
(from, to) if from == to => true,
(
Layout::Integer(IntLayout { signed: true, bytes: b_from }),
Layout::Integer(IntLayout { signed: true, bytes: b_to }),
) if !self.is_positive() && b_from < b_to => {
self.layout = to;
for i in b_from..b_to {
self[i] = 255u8;
}
self.clean();
true
}
(Layout::Integer(IntLayout { .. }), Layout::Integer(IntLayout { bytes: len2, .. })) => {
let bit_len = self.min_bit_len();
self.layout = to;
self.clean();
bit_len <= len2 * 8
}
(Layout::Float(l1), Layout::Float(l2)) => {
let value = match l1 {
FloatLayout::BFloat16 => bf16::from(*self).to_string(),
FloatLayout::IeeeHalf => ieee::Half::from(*self).to_string(),
FloatLayout::IeeeSingle => ieee::Single::from(*self).to_string(),
FloatLayout::IeeeDouble => ieee::Double::from(*self).to_string(),
FloatLayout::X87DoubleExt => ieee::X87DoubleExtended::from(*self).to_string(),
FloatLayout::IeeeQuad => ieee::Quad::from(*self).to_string(),
FloatLayout::IeeeOct => {
unimplemented!("IEEE octal precision layout conversion")
}
FloatLayout::FloatTapered => unimplemented!("tapered float layout conversion"),
};
*self = match l2 {
FloatLayout::BFloat16 => bf16::from_str(&value)
.ok()
.and_then(|v| MaybeNumber::from(v).0)
.expect("float layout conversion"),
FloatLayout::IeeeHalf => ieee::Half::from_str(&value)
.ok()
.and_then(|v| MaybeNumber::from(v).0)
.expect("float layout conversion"),
FloatLayout::IeeeSingle => ieee::Single::from_str(&value)
.ok()
.and_then(|v| MaybeNumber::from(v).0)
.expect("float layout conversion"),
FloatLayout::IeeeDouble => ieee::Double::from_str(&value)
.ok()
.and_then(|v| MaybeNumber::from(v).0)
.expect("float layout conversion"),
FloatLayout::X87DoubleExt => ieee::X87DoubleExtended::from_str(&value)
.ok()
.and_then(|v| MaybeNumber::from(v).0)
.expect("float layout conversion"),
FloatLayout::IeeeQuad => ieee::Quad::from_str(&value)
.ok()
.and_then(|v| MaybeNumber::from(v).0)
.expect("float layout conversion"),
FloatLayout::IeeeOct => {
unimplemented!("IEEE octal precision layout conversion")
}
FloatLayout::FloatTapered => unimplemented!("tapered float layout conversion"),
};
false
}
(Layout::Float(fl), Layout::Integer(_)) => {
let val = match fl {
FloatLayout::BFloat16 => todo!("BFloat16 to integer conversion"),
FloatLayout::IeeeHalf => ieee::Half::from(*self).to_i256(256),
FloatLayout::IeeeSingle => ieee::Single::from(*self).to_i256(256),
FloatLayout::IeeeDouble => ieee::Double::from(*self).to_i256(256),
FloatLayout::X87DoubleExt => ieee::X87DoubleExtended::from(*self).to_i256(256),
FloatLayout::IeeeQuad => ieee::Quad::from(*self).to_i256(256),
FloatLayout::IeeeOct => ieee::Oct::from(*self).to_i256(256),
FloatLayout::FloatTapered => unimplemented!("tapered float layout conversion"),
};
*self = Number::from(val.value);
self.reshape(to);
val.status == Status::OK
}
(from, to) => todo!("Number layout reshape from {} to {}", from, to),
}
}
pub fn reshaped(mut self, to: Layout, wrap: bool) -> Option<Number> {
self.reshape(to).then(|| self).or(if wrap { Some(self) } else { None })
}
#[doc(hidden)]
#[inline]
pub(super) fn to_u1024_bytes(self) -> u1024 { self.to_clean().into() }
#[doc(hidden)]
#[inline]
pub(super) fn to_i1024_bytes(self) -> i1024 { self.to_clean().into() }
}
#[derive(Clone, Eq, PartialEq, Debug, Display, From)]
#[cfg_attr(feature = "std", derive(Error))]
#[display(inner)]
#[non_exhaustive]
pub enum LiteralParseError {
#[from]
Int(core::num::ParseIntError),
#[from]
#[display(Debug)]
Float(amplify::num::apfloat::ParseError),
#[display("unknown token `{0}` while parsing AluVM assembly literal")]
UnknownLiteral(String),
}
impl FromStr for Number {
type Err = LiteralParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(if let Some(s) = s.strip_prefix("0x") {
u128::from_str_radix(s, 16)?.into()
} else if let Some(s) = s.strip_prefix("0o") {
u128::from_str_radix(s, 8)?.into()
} else if let Some(s) = s.strip_prefix("0b") {
u128::from_str_radix(s, 2)?.into()
} else if s.starts_with('-') {
i128::from_str(s)?.into()
} else {
u128::from_str(s)?.into()
})
}
}
impl Debug for Number {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let len = self.layout.bytes() as usize;
f.debug_struct("Number")
.field("layout", &self.layout)
.field("bytes", {
#[cfg(feature = "std")]
{
use amplify::hex::ToHex;
&self.bytes[..len].to_hex()
}
#[cfg(not(feature = "std"))]
{
&format!("{:#04X?}", &self.bytes[0..len])
}
})
.finish()
}
}
impl Display for Number {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.layout {
Layout::Integer(IntLayout { signed: false, .. }) if self.min_bit_len() <= 12 => {
write!(f, "{}", u16::from(self))
}
Layout::Integer(IntLayout { signed: false, .. }) if self.min_bit_len() < 16 * 8 => {
write!(f, "0x{:X}", self)
}
Layout::Integer(IntLayout { signed: true, bytes }) if bytes <= 16 => {
Display::fmt(&i128::from(self), f)
}
Layout::Integer(IntLayout { signed: false, bytes }) if bytes <= 16 => {
Display::fmt(&u128::from(self), f)
}
Layout::Integer(IntLayout { signed: false, bytes }) if bytes <= 32 => {
Display::fmt(&u256::from(self), f)
}
Layout::Integer(IntLayout { signed: false, .. }) if self.min_bit_len() < 512 => {
Display::fmt(&u512::from(self), f)
}
Layout::Integer(IntLayout { .. }) => Display::fmt(&u1024::from(self), f),
Layout::Float(FloatLayout::BFloat16) => Display::fmt(&half::bf16::from(self), f),
Layout::Float(FloatLayout::IeeeHalf) => Display::fmt(&ieee::Half::from(self), f),
Layout::Float(FloatLayout::IeeeSingle) => Display::fmt(&ieee::Single::from(self), f),
Layout::Float(FloatLayout::IeeeDouble) => Display::fmt(&ieee::Double::from(self), f),
Layout::Float(FloatLayout::IeeeQuad) => Display::fmt(&ieee::Quad::from(self), f),
Layout::Float(FloatLayout::X87DoubleExt) => {
Display::fmt(&ieee::X87DoubleExtended::from(self), f)
}
_ => {
f.write_str("<not supported float layout for display>")
}
}
}
}
impl LowerHex for Number {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
#[cfg(feature = "std")]
use amplify::hex::ToHex;
match self.layout {
Layout::Integer(IntLayout { signed: true, bytes }) if bytes <= 16 => {
LowerHex::fmt(&i128::from(self), f)
}
Layout::Integer(IntLayout { signed: false, bytes }) if bytes <= 16 => {
LowerHex::fmt(&u128::from(self), f)
}
Layout::Integer(IntLayout { signed: false, bytes }) if bytes < 32 => {
#[cfg(feature = "std")]
{
f.write_str(u256::from(self).to_be_bytes().to_hex().trim_start_matches('0'))
}
#[cfg(not(feature = "std"))]
{
f.write_str("<hex display requires std library>")
}
}
Layout::Integer(IntLayout { signed: false, bytes }) if bytes < 32 => {
#[cfg(feature = "std")]
{
f.write_str(u512::from(self).to_be_bytes().to_hex().trim_start_matches('0'))
}
#[cfg(not(feature = "std"))]
{
f.write_str("<hex display requires std library>")
}
}
Layout::Integer(IntLayout { .. }) => {
#[cfg(feature = "std")]
{
f.write_str(u1024::from(self).to_be_bytes().to_hex().trim_start_matches('0'))
}
#[cfg(not(feature = "std"))]
{
f.write_str("<hex display requires std library>")
}
}
_ => {
f.write_str("<not supported float layout for display>")
}
}
}
}
impl UpperHex for Number {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let s = if f.alternate() { format!("{:#x}", self) } else { format!("{:x}", self) };
f.write_str(&s.to_uppercase())
}
}
impl Octal for Number {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.layout {
Layout::Integer(IntLayout { signed: true, bytes }) if bytes <= 16 => {
Octal::fmt(&i128::from(self), f)
}
Layout::Integer(IntLayout { signed: false, bytes }) if bytes <= 16 => {
Octal::fmt(&u128::from(self), f)
}
_ => {
f.write_str("<not supported float layout for display>")
}
}
}
}
impl LowerExp for Number {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.layout {
Layout::Integer(_) => Display::fmt(self, f),
_ => {
f.write_str("<not supported float layout for display>")
}
}
}
}
impl UpperExp for Number {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let s = if f.alternate() { format!("{:#e}", self) } else { format!("{:e}", self) };
f.write_str(&s.to_uppercase())
}
}
macro_rules! impl_number_bytes_conv {
($len:literal) => {
impl From<Number> for [u8; $len] {
fn from(val: Number) -> Self {
let len = (val.min_bit_len() + 7) as usize / 8;
assert!(
len <= $len,
"attempt to convert number into a byte array with incorrect length",
);
let mut bytes = [0u8; $len];
bytes[..len].copy_from_slice(&val.bytes[..len]);
bytes
}
}
impl From<[u8; $len]> for Number {
fn from(val: [u8; $len]) -> Number {
let mut bytes = [0u8; 1024];
bytes[0..$len].copy_from_slice(&val[..]);
Number { layout: Layout::unsigned($len), bytes }
}
}
impl From<[u8; $len]> for MaybeNumber {
fn from(val: [u8; $len]) -> MaybeNumber { MaybeNumber::from(Number::from(val)) }
}
impl From<Option<[u8; $len]>> for MaybeNumber {
fn from(val: Option<[u8; $len]>) -> MaybeNumber {
MaybeNumber::from(val.map(Number::from))
}
}
impl From<&Option<[u8; $len]>> for MaybeNumber {
fn from(val: &Option<[u8; $len]>) -> MaybeNumber {
MaybeNumber::from(val.map(Number::from))
}
}
};
}
macro_rules! impl_number_int_conv {
($ty:ident, $len:literal, $signed:expr) => {
impl From<Number> for $ty {
fn from(val: Number) -> Self {
assert!(
val.min_bit_len() <= $len * 8,
"attempt to convert Number into type with lower bit dimension"
);
if $signed {
let mut ret = match val[val.layout.sign_byte()] & 0x80 {
0 => [0u8; $len],
_ => [255u8; $len],
};
for i in 0..val.layout.bytes() {
ret[i as usize] = val[i];
}
$ty::from_le_bytes(ret)
} else {
$ty::from_le_bytes(<[u8; $len]>::from(val))
}
}
}
impl From<&$ty> for Number {
fn from(val: &$ty) -> Self {
let mut bytes = [0u8; 1024];
let le = val.to_le_bytes();
bytes[0..le.len()].copy_from_slice(&le[..]);
if $signed {
Number { layout: Layout::signed(le.len() as u16), bytes }
} else {
Number { layout: Layout::unsigned(le.len() as u16), bytes }
}
}
}
impl From<&Number> for $ty {
fn from(val: &Number) -> Self { $ty::from(*val) }
}
impl From<$ty> for Number {
fn from(val: $ty) -> Self { Number::from(&val) }
}
impl From<$ty> for MaybeNumber {
fn from(val: $ty) -> Self { MaybeNumber::some(Number::from(val)) }
}
impl From<&$ty> for MaybeNumber {
fn from(val: &$ty) -> Self { MaybeNumber::some(Number::from(*val)) }
}
impl From<Option<$ty>> for MaybeNumber {
fn from(val: Option<$ty>) -> Self { MaybeNumber::from(val.map(Number::from)) }
}
impl From<Option<&$ty>> for MaybeNumber {
fn from(val: Option<&$ty>) -> Self { MaybeNumber::from(val.copied().map(Number::from)) }
}
impl From<&Option<$ty>> for MaybeNumber {
fn from(val: &Option<$ty>) -> Self { MaybeNumber::from((*val).map(Number::from)) }
}
};
}
macro_rules! impl_number_float_conv {
($ty:ident, $tys:ident, $len:literal, $layout:ident) => {
impl From<Number> for $ty {
fn from(val: Number) -> Self {
assert!(
val.min_bit_len() <= $len * 8,
"attempt to convert Number into type with lower bit dimension"
);
$ty::from_bits(val.into())
}
}
impl From<&Number> for $ty {
fn from(val: &Number) -> Self { $ty::from(*val) }
}
impl From<$ty> for MaybeNumber {
fn from(mut val: $ty) -> Self {
if val.is_nan() {
return MaybeNumber::none();
}
if val == -$ty::ZERO {
val = $ty::ZERO
}
let mut bytes = [0u8; 1024];
let le = val.to_bits().to_le_bytes();
bytes[0..le.len()].copy_from_slice(&le[..]);
MaybeNumber::some(Number { layout: Layout::float(FloatLayout::$layout), bytes })
}
}
};
}
impl<T: ::core::convert::Into<MaybeNumber>> From<StatusAnd<T>> for MaybeNumber {
fn from(init: StatusAnd<T>) -> Self {
match init.status {
Status::OK | Status::INEXACT => init.value.into(),
_ => MaybeNumber::none(),
}
}
}
impl_number_bytes_conv!(1);
impl_number_bytes_conv!(2);
impl_number_bytes_conv!(4);
impl_number_bytes_conv!(8);
impl_number_bytes_conv!(16);
impl_number_bytes_conv!(20);
impl_number_bytes_conv!(32);
impl_number_bytes_conv!(64);
impl_number_bytes_conv!(128);
impl_number_bytes_conv!(256);
impl_number_bytes_conv!(512);
impl_number_bytes_conv!(1024);
impl_number_int_conv!(u8, 1, false);
impl_number_int_conv!(u16, 2, false);
impl_number_int_conv!(u32, 4, false);
impl_number_int_conv!(u64, 8, false);
impl_number_int_conv!(u128, 16, false);
impl_number_int_conv!(u256, 32, false);
impl_number_int_conv!(u512, 64, false);
impl_number_int_conv!(u1024, 128, false);
mod _float_impl {
use amplify::num::apfloat::ieee::*;
use amplify::num::apfloat::Float;
use half::bf16;
use super::*;
impl_number_float_conv!(bf16, bf16, 2, BFloat16);
impl_number_float_conv!(Half, HalfS, 2, IeeeHalf);
impl_number_float_conv!(Single, SingleS, 4, IeeeSingle);
impl_number_float_conv!(Double, DoubleS, 8, IeeeDouble);
impl_number_float_conv!(X87DoubleExtended, X87DoubleExtendedS, 10, X87DoubleExt);
impl_number_float_conv!(Quad, QuadS, 16, IeeeQuad);
impl_number_float_conv!(Oct, OctS, 32, IeeeOct);
}
impl_number_int_conv!(i8, 1, true);
impl_number_int_conv!(i16, 2, true);
impl_number_int_conv!(i32, 4, true);
impl_number_int_conv!(i64, 8, true);
impl_number_int_conv!(i128, 16, true);
impl_number_int_conv!(i256, 32, true);
impl_number_int_conv!(i512, 64, true);
impl_number_int_conv!(i1024, 128, true);
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, From)]
pub struct Step(#[from] i8);
impl Step {
pub fn with(val: i8) -> Self { Self(val) }
pub fn as_i8(self) -> i8 { self.0 }
}
impl From<Step> for Number {
#[inline]
fn from(step: Step) -> Self { Number::from(step.0) }
}
impl Display for Step {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let val = self.0;
if f.alternate() {
match val {
1 => f.write_str("inc"),
-1 => f.write_str("dec"),
x if x < 0 => f.write_str("sub"),
x if x >= 0 => f.write_str("add"),
_ => unreachable!(),
}
} else if val.abs() > 1 {
Display::fmt(&if val >= 0 { val } else { -val }, f)?;
f.write_char(',')
} else {
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bytes_conv_test() {
assert_eq!([255u8], <[u8; 1]>::from(Number::from(255u8)));
}
#[test]
fn asserting_layouts_kinds() {
let signed_integer_layout = Layout::Integer(IntLayout::signed(33));
assert!(signed_integer_layout.is_signed_int());
assert!(!signed_integer_layout.is_unsigned_int());
assert!(!signed_integer_layout.is_float());
let unsigned_integer_layout = Layout::Integer(IntLayout::unsigned(33));
assert!(unsigned_integer_layout.is_unsigned_int());
assert!(!unsigned_integer_layout.is_signed_int());
assert!(!unsigned_integer_layout.is_float());
let float_layout = Layout::Float(FloatLayout::BFloat16);
assert!(float_layout.is_float());
assert!(!float_layout.is_signed_int());
assert!(!float_layout.is_unsigned_int());
}
#[test]
fn returning_bytes() {
let signed_integer_layout = Layout::Integer(IntLayout::signed(3));
assert_eq!(signed_integer_layout.bytes(), 3);
let unsigned_integer_layout = Layout::Integer(IntLayout::unsigned(3));
assert_eq!(unsigned_integer_layout.bytes(), 3);
let float_layout = Layout::Float(FloatLayout::BFloat16);
assert_eq!(float_layout.bytes(), 2);
}
#[test]
fn is_zero_test() {
let num = Number::from(0);
assert_eq!(true, num.is_zero());
let num = Number::from(1);
assert_eq!(false, num.is_zero());
}
#[test]
fn is_unsigned_int_test() {
let num = Number::from(0u8);
assert_eq!(true, num.layout.is_unsigned_int());
let num = Number::from(0i8);
assert_eq!(false, num.layout.is_unsigned_int());
let num = Number::from(1u16);
assert_eq!(true, num.layout.is_unsigned_int());
let num = Number::from(1i16);
assert_eq!(false, num.layout.is_unsigned_int());
let num = Number::from(-1);
assert_eq!(false, num.layout.is_unsigned_int());
}
#[test]
fn is_positive_test() {
let num = Number::from(1);
assert_eq!(true, num.is_positive());
let num = Number::from(0);
assert_eq!(false, num.is_positive());
let num = Number::from(-1);
assert_eq!(false, num.is_positive());
let num = Number::from(127);
assert_eq!(true, num.is_positive());
}
#[test]
fn reshape_test() {
let mut x =
Number::with(&[1u8], Layout::Integer(IntLayout { signed: false, bytes: 1 })).unwrap();
let y = Number::with(&[1u8, 0u8], Layout::Integer(IntLayout { signed: false, bytes: 2 }))
.unwrap();
assert_eq!(true, x.reshape(Layout::Integer(IntLayout { signed: false, bytes: 2 })));
assert_eq!(x, y);
}
#[test]
fn reshape_with_same_layout_test() {
let mut x =
Number::with(&[1u8], Layout::Integer(IntLayout { signed: false, bytes: 1 })).unwrap();
let y =
Number::with(&[1u8], Layout::Integer(IntLayout { signed: false, bytes: 1 })).unwrap();
assert_eq!(true, x.reshape(Layout::Integer(IntLayout { signed: false, bytes: 1 })));
assert_eq!(x, y);
}
#[test]
fn reshape_negative_value_test() {
let mut x = Number::from(-24i8);
let y = Number::from(-24i16);
let z = Number::from(-24i128);
assert_eq!(x.layout, Layout::Integer(IntLayout { signed: true, bytes: 1 }));
assert_eq!(true, x.reshape(Layout::Integer(IntLayout { signed: true, bytes: 2 })));
assert_eq!(x, y);
assert_eq!(true, x.reshape(Layout::Integer(IntLayout { signed: true, bytes: 16 })));
assert_eq!(x, z);
}
#[test]
fn take_sign_test() {
let x = Number::from(-1i8);
let y = Number::from(255u8);
let z = MaybeNumber::from(ieee::Single::SMALLEST).unwrap();
assert_eq!(x.into_unsigned(), y);
assert_eq!(x.into_unsigned().into_signed(), x);
assert_eq!(z.into_unsigned(), z);
assert_eq!(z.into_signed(), z);
}
}