dashu-base-0.4.1/.cargo_vcs_info.json0000644000000001420000000000100130470ustar { "git": { "sha1": "fbc7bb0af093d3aa3f4607f9b301a5c754f62daa" }, "path_in_vcs": "base" }dashu-base-0.4.1/CHANGELOG.md000064400000000000000000000031510072674642500135030ustar 00000000000000# Changelog ## 0.4.1 - Mark `AbsEq` as deprecated. - Re-implement functions `next_up` and `next_down`, and expose them through the `utils` module. ## 0.4.0 ### Add - Add `is_positive()` and `is_negative()` to the `Signed` trait. ### Change - `SquareRoot` and `CubicRoot` are moved to `dashu_base::math`. - `AbsCmp` is renamed to `AbsOrd`. - `FBig::square` and `Context::square` are renamed to `sqr`. ## 0.3.1 - Add trait `Inverse`. - Implement `AbsCmp` and `AbsEq` for primitive types. ## 0.3.0 ### Add - Add trait `AbsCmp` and `AbsEq` - Add trait `FloatEncoding` and implement it for `f32` and `f64` - Add trait `Signed` and implement it for all signed primitive types - Add conversion between `Sign` and `bool` - Implement `Abs` for `f32` and `f64` - Add types `error::{ConversionError, ParseError}` (originates from `dashu-int`) - Add trait `SquareRoot`, `SquareRootRem`, `CubicRoot`, `CubicRootRem` - Implement `EstimatedLog2` for `f32`, `f64` and signed integers ### Change - `trailing_zeros` has been removed from the `BitTest` trait - The definition of `BitTest::bit_len` has changed, and `BitTest` is now implemented for signed integers. ### Remove - `Root` and `RootRem` are removed (use `SquareRoot`, `SquareRootRem`, etc. instead) ## 0.2.1 - Implement `RootRem` for `u8`, `u16`, `u32` - Add trait `Root` and implement it for `u8`, `u16`, `u32`, `u64`, `u128` ## 0.2.0 - Add traits `Approximation`, `Sign` and `EstimatedLog2`. ## 0.1.1 - Fix the bug of the GCD algorithm. ## 0.1.0 (Initial release) - including several common trait definitions. dashu-base-0.4.1/Cargo.toml0000644000000023010000000000100110440ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.61" name = "dashu-base" version = "0.4.1" authors = ["Jacob Zhong "] description = "Common trait definitions and tools for the `dashu` libraries" homepage = "https://github.com/cmpute/dashu" documentation = "https://docs.rs/dashu-base" readme = "README.md" keywords = [ "mathematics", "numerics", ] categories = [ "mathematics", "no-std", ] license = "MIT OR Apache-2.0" repository = "https://github.com/cmpute/dashu" [package.metadata.docs.rs] all-features = true [[bench]] name = "benchmarks" harness = false [dev-dependencies.criterion] version = "0.5.1" features = ["html_reports"] [dev-dependencies.rand] version = "0.8.3" [features] default = ["std"] std = [] dashu-base-0.4.1/Cargo.toml.orig000064400000000000000000000013560072674642500145660ustar 00000000000000[package] name = "dashu-base" version = "0.4.1" authors = ["Jacob Zhong "] edition = "2021" description = "Common trait definitions and tools for the `dashu` libraries" keywords = ["mathematics", "numerics"] categories = ["mathematics", "no-std"] license = "MIT OR Apache-2.0" repository = "https://github.com/cmpute/dashu" homepage = "https://github.com/cmpute/dashu" documentation = "https://docs.rs/dashu-base" readme = "README.md" rust-version = "1.61" [package.metadata.docs.rs] all-features = true [features] default = ["std"] std = [] [dev-dependencies] rand = { version = "0.8.3" } criterion = { version = "0.5.1", features = ["html_reports"] } [[bench]] name = "benchmarks" harness = false dashu-base-0.4.1/README.md000064400000000000000000000003220072674642500131460ustar 00000000000000# dashu-base Common trait definitions for `dashu` crates. See [Docs.rs](https://docs.rs/dashu-base/latest/dashu_base/) for the full documentation. ## License See the [top-level readme](../README.md). dashu-base-0.4.1/benches/benchmarks.rs000064400000000000000000000055200072674642500157660ustar 00000000000000//! Benchmarks. use criterion::{ black_box, criterion_group, criterion_main, AxisScale, BenchmarkId, Criterion, PlotConfiguration, }; use dashu_base::{CubicRoot, ExtendedGcd, Gcd, SquareRoot}; use rand::prelude::*; const SEED: u64 = 1; macro_rules! uop_case { ($t:ty, $bits:literal, $method:ident, $rng:ident, $group:ident) => { let bits = $bits; let a: $t = $rng.gen_range(0..1 << $bits); $group.bench_with_input(BenchmarkId::from_parameter(bits), &bits, |bencher, _| { bencher.iter(|| black_box(a).$method()) }); }; } macro_rules! binop_case { ($t:ty, $bits:literal, $method:ident, $rng:ident, $group:ident) => { let bits = $bits; let a: $t = $rng.gen_range(0..1 << $bits); let b: $t = $rng.gen_range(0..1 << $bits); $group.bench_with_input(BenchmarkId::from_parameter(bits), &bits, |bencher, _| { bencher.iter(|| black_box(a).$method(black_box(b))) }); }; } fn bench_gcd(criterion: &mut Criterion) { let mut rng = StdRng::seed_from_u64(SEED); let mut group = criterion.benchmark_group("gcd"); group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); binop_case!(u16, 10, gcd, rng, group); binop_case!(u32, 20, gcd, rng, group); binop_case!(u64, 40, gcd, rng, group); binop_case!(u128, 80, gcd, rng, group); binop_case!(u128, 120, gcd, rng, group); group.finish(); let mut group = criterion.benchmark_group("gcd_ext"); group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); binop_case!(u16, 10, gcd_ext, rng, group); binop_case!(u32, 20, gcd_ext, rng, group); binop_case!(u64, 40, gcd_ext, rng, group); binop_case!(u128, 80, gcd_ext, rng, group); binop_case!(u128, 120, gcd_ext, rng, group); group.finish(); } fn bench_roots(criterion: &mut Criterion) { let mut rng = StdRng::seed_from_u64(SEED); let mut group = criterion.benchmark_group("sqrt"); group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); uop_case!(u16, 10, sqrt, rng, group); uop_case!(u32, 20, sqrt, rng, group); uop_case!(u64, 40, sqrt, rng, group); uop_case!(u128, 80, sqrt, rng, group); uop_case!(u128, 120, sqrt, rng, group); group.finish(); let mut group = criterion.benchmark_group("cbrt"); group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); uop_case!(u16, 10, cbrt, rng, group); uop_case!(u32, 20, cbrt, rng, group); uop_case!(u64, 40, cbrt, rng, group); uop_case!(u128, 80, cbrt, rng, group); uop_case!(u128, 120, cbrt, rng, group); group.finish(); } criterion_group!(benches, bench_gcd, bench_roots,); criterion_main!(benches); dashu-base-0.4.1/src/approx.rs000064400000000000000000000044420072674642500143440ustar 00000000000000//! Trait definitions for approximated values /// Represent an calculation result with a possible error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Approximation { /// The result is exact, contains the result value Exact(T), /// The result is inexact, contains the result value and error Inexact(T, E), } impl Approximation { /// Get the value of the calculation regardless of error #[inline] pub fn value(self) -> T { match self { Self::Exact(v) => v, Self::Inexact(v, _) => v, } } /// Get a reference to the calculation result #[inline] pub const fn value_ref(&self) -> &T { match self { Self::Exact(v) => v, Self::Inexact(v, _) => v, } } #[inline] pub fn unwrap(self) -> T { match self { Self::Exact(val) => val, Self::Inexact(_, _) => panic!("called `Approximation::unwrap()` on a `Inexact` value"), } } #[inline] pub fn error(self) -> Option { match self { Self::Exact(_) => None, Self::Inexact(_, e) => Some(e), } } #[inline] pub const fn error_ref(&self) -> Option<&E> { match self { Self::Exact(_) => None, Self::Inexact(_, e) => Some(e), } } #[inline] pub fn map(self, f: F) -> Approximation where F: FnOnce(T) -> U, { match self { Self::Exact(v) => Approximation::Exact(f(v)), Self::Inexact(v, e) => Approximation::Inexact(f(v), e), } } #[inline] pub fn and_then(self, f: F) -> Approximation where F: FnOnce(T) -> Approximation, { match self { Self::Exact(v) => match f(v) { Approximation::Exact(v2) => Approximation::Exact(v2), Approximation::Inexact(v2, e) => Approximation::Inexact(v2, e), }, Self::Inexact(v, e) => match f(v) { Approximation::Exact(v2) => Approximation::Inexact(v2, e), Approximation::Inexact(v2, e2) => Approximation::Inexact(v2, e2), }, } } } dashu-base-0.4.1/src/bit.rs000064400000000000000000000440300072674642500136060ustar 00000000000000//! Trait definitions for bitwise operations. //! //! Most traits are only implemented for unsigned integers yet. use core::num::FpCategory; use crate::{ Approximation::{self, *}, Sign::{self, *}, }; /// Bit query for integers /// /// # Examples /// /// ``` /// # use dashu_base::BitTest; /// // query a bit of the number /// assert_eq!(0b10010.bit(1), true); /// assert_eq!(0b10010.bit(3), false); /// assert_eq!(0b10010.bit(100), false); /// assert_eq!((-0b10010).bit(1), true); /// assert_eq!((-0b10010).bit(3), true); /// assert_eq!((-0b10010).bit(100), true); /// /// // query the bit length of the number /// assert_eq!(0.bit_len(), 0); /// assert_eq!(17.bit_len(), 5); /// assert_eq!((-17).bit_len(), 5); /// assert_eq!(0b101000000.bit_len(), 9); /// ``` pub trait BitTest { /// Effective bit length of the binary representation. /// /// For 0, the length is 0. /// /// For positive numbers it is: /// * number of digits in base 2 /// * the index of the top 1 bit plus one /// * the floored base-2 logarithm of the number plus one. /// /// For negative numbers it is: /// * number of digits in base 2 without the sign /// * the index of the top 0 bit plus one /// * the floored base-2 logarithm of the absolute value of the number plus one. fn bit_len(&self) -> usize; /// Returns true if the `n`-th bit is set in its two's complement binary representation, n starts from 0. fn bit(&self, n: usize) -> bool; } /// Functions related to the power of two. /// /// # Examples /// ``` /// use dashu_base::PowerOfTwo; /// /// let n = 5u32; /// assert!(!n.is_power_of_two()); /// assert_eq!(n.next_power_of_two(), 8); /// ``` pub trait PowerOfTwo { /// Test if self is a power of two (`2^k`) fn is_power_of_two(&self) -> bool; /// Get the smallest power of two greater than or equal to self. fn next_power_of_two(self) -> Self; } macro_rules! impl_bit_ops_for_uint { ($($T:ty)*) => {$( impl BitTest for $T { #[inline] fn bit_len(&self) -> usize { (<$T>::BITS - self.leading_zeros()) as usize } #[inline] fn bit(&self, position: usize) -> bool { if position >= <$T>::BITS as usize { return false; } else { self & (1 << position) > 0 } } } impl PowerOfTwo for $T { #[inline] fn is_power_of_two(&self) -> bool { <$T>::is_power_of_two(*self) } #[inline] fn next_power_of_two(self) -> $T { <$T>::next_power_of_two(self) } } )*} } impl_bit_ops_for_uint!(u8 u16 u32 u64 u128 usize); macro_rules! impl_bit_ops_for_int { ($($T:ty)*) => {$( impl BitTest for $T { #[inline] fn bit_len(&self) -> usize { self.unsigned_abs().bit_len() } #[inline] fn bit(&self, position: usize) -> bool { if position >= <$T>::BITS as usize { return self < &0; } else { self & (1 << position) > 0 } } } )*} } impl_bit_ops_for_int!(i8 i16 i32 i64 i128 isize); /// Support encoding and decoding of floats into (mantissa, exponent) parts. /// /// See the docs of each method for the details /// /// # Examples /// /// ``` /// # use dashu_base::{FloatEncoding, Approximation::*, Sign::*}; /// use core::num::FpCategory; /// /// assert_eq!(0f64.decode(), Ok((0, -1074))); // exponent will not be reduced /// assert_eq!(1f32.decode(), Ok((1 << 23, -23))); /// assert_eq!(f32::INFINITY.decode(), Err(FpCategory::Infinite)); /// /// assert_eq!(f64::encode(0, 1), Exact(0f64)); /// assert_eq!(f32::encode(1, 0), Exact(1f32)); /// assert_eq!(f32::encode(i32::MAX, 100), Inexact(f32::INFINITY, Positive)); /// ``` pub trait FloatEncoding { type Mantissa; type Exponent; /// Convert a float number `mantissa * 2^exponent` into `(mantissa, exponent)` parts faithfully. /// /// This method will not reduce the result (e.g. turn `2 * 2^-1` into `1 * 2^0`), and it /// will return [Err] when the float number is nan or infinite. fn decode(self) -> Result<(Self::Mantissa, Self::Exponent), FpCategory>; /// Convert `(mantissa, exponent)` to `mantissa * 2^exponent` faithfully. /// /// It won't generate `NaN` values. However if the actual value is out of the /// representation range, it might return an infinity or subnormal number. /// /// If any rounding happened during the conversion, it should follow the default /// behavior defined by IEEE 754 (round to nearest, ties to even) /// /// The returned approximation is exact if the input can be exactly representable by f32, /// otherwise the error field of the approximation contains the sign of `result - mantissa * 2^exp`. fn encode(mantissa: Self::Mantissa, exponent: Self::Exponent) -> Approximation where Self: Sized; } /// Round to even floating point adjustment, based on the bottom /// bit of mantissa and additional 2 bits (i.e. 3 bits in units of ULP/4). #[inline] fn round_to_even_adjustment(bits: u8) -> bool { bits >= 0b110 || bits == 0b011 } impl FloatEncoding for f32 { type Mantissa = i32; type Exponent = i16; #[inline] fn decode(self) -> Result<(i32, i16), FpCategory> { let bits: u32 = self.to_bits(); let sign_bit = bits >> 31; let mantissa_bits = bits & 0x7fffff; // deal with inf/nan values let mut exponent = ((bits >> 23) & 0xff) as i16; if exponent == 0xff { return if mantissa_bits != 0 { Err(FpCategory::Nan) } else { Err(FpCategory::Infinite) }; } // then parse values let mantissa = if exponent == 0 { // subnormal exponent = -126 - 23; mantissa_bits } else { // normal exponent -= 127 + 23; // bias + mantissa shift mantissa_bits | 0x800000 } as i32; let sign = Sign::from(sign_bit > 0); Ok((mantissa * sign, exponent)) } #[inline] fn encode(mantissa: i32, exponent: i16) -> Approximation { if mantissa == 0 { return Exact(0f32); } // clear sign let sign = (mantissa < 0) as u32; let mut mantissa = mantissa.unsigned_abs(); let zeros = mantissa.leading_zeros(); let top_bit = (u32::BITS - zeros) as i16 + exponent; if top_bit > 128 { // overflow return if sign == 0 { Inexact(f32::INFINITY, Sign::Positive) } else { Inexact(f32::NEG_INFINITY, Sign::Negative) }; } else if top_bit < -125 - 23 { // underflow return if sign == 0 { Inexact(0f32, Sign::Negative) } else { Inexact(-0f32, Sign::Positive) }; }; let bits; // bit representation let round_bits; // for rounding if top_bit <= -125 { // subnormal float // (this branch includes 1e-125, the smallest positive normal f32) // first remove the exponent let shift = exponent + 126 + 23; if shift >= 0 { round_bits = 0; // not rounding is required mantissa <<= shift as u32; } else { let shifted = mantissa << (30 + shift) as u32; round_bits = (shifted >> 28 & 0b110) as u8 | ((shifted & 0xfffffff) != 0) as u8; mantissa >>= (-shift) as u32; } // then compose the bit representation of f32 bits = (sign << 31) | mantissa; } else { // normal float // first normalize the mantissa (and remove the top bit) if mantissa == 1 { mantissa = 0; // shl will overflow } else { mantissa <<= zeros + 1; } // then calculate the exponent (bias is 127) let exponent = (exponent + 127 + u32::BITS as i16) as u32 - zeros - 1; // then compose the bit representation of f32 bits = (sign << 31) | (exponent << 23) | (mantissa >> 9); // get the low bit of mantissa and two extra bits, and adding round-to-even adjustment round_bits = ((mantissa >> 7) & 0b110) as u8 | ((mantissa & 0x7f) != 0) as u8; }; if round_bits & 0b11 == 0 { // If two extra bits are all zeros, then the float is exact Exact(f32::from_bits(bits)) } else { let sign = Sign::from(sign > 0); if round_to_even_adjustment(round_bits) { // If the mantissa overflows, this correctly increases the exponent and sets the mantissa to 0. // If the exponent overflows, we correctly get the representation of infinity. Inexact(f32::from_bits(bits + 1), Positive * sign) } else { Inexact(f32::from_bits(bits), Negative * sign) } } } } impl FloatEncoding for f64 { type Mantissa = i64; type Exponent = i16; #[inline] fn decode(self) -> Result<(i64, i16), FpCategory> { let bits: u64 = self.to_bits(); let sign_bit = bits >> 63; let mantissa_bits = bits & 0xfffffffffffff; // deal with inf/nan values let mut exponent = ((bits >> 52) & 0x7ff) as i16; if exponent == 0x7ff { return if mantissa_bits != 0 { Err(FpCategory::Nan) } else { Err(FpCategory::Infinite) }; } // then parse values let mantissa = if exponent == 0 { // subnormal exponent = -1022 - 52; mantissa_bits } else { // normal exponent -= 1023 + 52; // bias + mantissa shift mantissa_bits | 0x10000000000000 } as i64; if sign_bit == 0 { Ok((mantissa, exponent)) } else { Ok((-mantissa, exponent)) } } #[inline] fn encode(mantissa: i64, exponent: i16) -> Approximation { if mantissa == 0 { return Exact(0f64); } // clear sign let sign = (mantissa < 0) as u64; let mut mantissa = mantissa.unsigned_abs(); let zeros = mantissa.leading_zeros(); let top_bit = (u64::BITS - zeros) as i16 + exponent; if top_bit > 1024 { // overflow return if sign == 0 { Inexact(f64::INFINITY, Sign::Positive) } else { Inexact(f64::NEG_INFINITY, Sign::Negative) }; } else if top_bit < -1022 - 52 { // underflow return if sign == 0 { Inexact(0f64, Sign::Negative) } else { Inexact(-0f64, Sign::Positive) }; }; let bits; // bit representation let round_bits; // for rounding if top_bit <= -1022 { // subnormal float // (this branch includes 1e-1022, the smallest positive normal f32) // first remove the exponent let shift = exponent + 1022 + 52; if shift >= 0 { round_bits = 0; // not rounding is required mantissa <<= shift as u32; } else { let shifted = mantissa << (62 + shift) as u64; round_bits = (shifted >> 60 & 0b110) as u8 | ((shifted & 0xfffffffffffffff) != 0) as u8; mantissa >>= (-shift) as u32; } // then compose the bit representation of f64 bits = (sign << 63) | mantissa; } else { // normal float // first normalize the mantissa (and remove the top bit) if mantissa == 1 { mantissa = 0; // shl will overflow } else { mantissa <<= zeros + 1; } // then calculate the exponent (bias is 1023) let exponent = (exponent + 1023 + u64::BITS as i16) as u64 - zeros as u64 - 1; // then compose the bit representation of f64 bits = (sign << 63) | (exponent << 52) | (mantissa >> 12); // get the low bit of mantissa and two extra bits, and adding round-to-even adjustment round_bits = ((mantissa >> 10) & 0b110) as u8 | ((mantissa & 0x3ff) != 0) as u8; }; if round_bits & 0b11 == 0 { // If two extra bits are all zeros, then the float is exact Exact(f64::from_bits(bits)) } else { let sign = Sign::from(sign > 0); if round_to_even_adjustment(round_bits) { // If the mantissa overflows, this correctly increases the exponent and sets the mantissa to 0. // If the exponent overflows, we correctly get the representation of infinity. Inexact(f64::from_bits(bits + 1), Positive * sign) } else { Inexact(f64::from_bits(bits), Negative * sign) } } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_float_encoding() { // special values assert_eq!(f32::INFINITY.decode(), Err(FpCategory::Infinite)); assert_eq!(f32::NEG_INFINITY.decode(), Err(FpCategory::Infinite)); assert_eq!(f32::NAN.decode(), Err(FpCategory::Nan)); assert_eq!(f64::INFINITY.decode(), Err(FpCategory::Infinite)); assert_eq!(f64::NEG_INFINITY.decode(), Err(FpCategory::Infinite)); assert_eq!(f64::NAN.decode(), Err(FpCategory::Nan)); // round trip test let f32_cases = [ 0., -1., 1., f32::MIN, f32::MAX, f32::MIN_POSITIVE, -f32::MIN_POSITIVE, f32::EPSILON, f32::from_bits(0x1), // smallest f32 f32::from_bits(0x7ff), // some subnormal value f32::from_bits(0x7fffff), // largest subnormal number f32::from_bits(0x800000), // smallest normal number -123.4567, core::f32::consts::PI, ]; for f in f32_cases { let (man, exp) = f.decode().unwrap(); assert_eq!(f32::encode(man, exp), Exact(f)); } let f64_cases = [ 0., -1., 1., f64::MIN, f64::MAX, f64::MIN_POSITIVE, -f64::MIN_POSITIVE, f64::EPSILON, f64::from_bits(0x1), // smallest f64 f64::from_bits(0x7fffff), // largest subnormal number f64::from_bits(0xfffffffffffff), // some subnormal value f64::from_bits(0x10000000000000), // smallest normal number -123456.789012345, core::f64::consts::PI, ]; for f in f64_cases { let (man, exp) = f.decode().unwrap(); assert_eq!(f64::encode(man, exp), Exact(f)); } // test out of ranges assert_eq!(f32::encode(1, 128), Inexact(f32::INFINITY, Sign::Positive)); assert_eq!(f32::encode(-1, 128), Inexact(f32::NEG_INFINITY, Sign::Negative)); assert_eq!(f32::encode(1, -150), Inexact(0f32, Sign::Negative)); assert_eq!(f32::encode(-1, -150), Inexact(-0f32, Sign::Positive)); assert_eq!(f64::encode(1, 1024), Inexact(f64::INFINITY, Sign::Positive)); assert_eq!(f64::encode(-1, 1024), Inexact(f64::NEG_INFINITY, Sign::Negative)); assert_eq!(f64::encode(1, -1075), Inexact(0f64, Sign::Negative)); assert_eq!(f64::encode(-1, -1075), Inexact(-0f64, Sign::Positive)); // test rounding assert_eq!(f32::encode(3, -150), Inexact(f32::from_bits(0x00000002), Sign::Positive)); assert_eq!(f32::encode(-5, -150), Inexact(f32::from_bits(0x80000002), Sign::Positive)); assert_eq!(f32::encode(i32::MAX, 50), Inexact(f32::from_bits(0x68000000), Sign::Positive)); assert_eq!( f32::encode(i32::MAX, -150), Inexact(f32::from_bits(0x04000000), Sign::Positive) ); assert_eq!( f32::encode(i32::MAX, -160), Inexact(f32::from_bits(0x00100000), Sign::Positive) ); assert_eq!( f32::encode(i32::MAX, -170), Inexact(f32::from_bits(0x00000400), Sign::Positive) ); assert_eq!( f64::encode(3, -1075), Inexact(f64::from_bits(0x0000000000000002), Sign::Positive) ); assert_eq!( f64::encode(-5, -1075), Inexact(f64::from_bits(0x8000000000000002), Sign::Positive) ); assert_eq!( f64::encode(i64::MAX, 500), Inexact(f64::from_bits(0x6320000000000000), Sign::Positive) ); assert_eq!( f64::encode(i64::MAX, -1075), Inexact(f64::from_bits(0x00b0000000000000), Sign::Positive) ); assert_eq!( f64::encode(i64::MAX, -1095), Inexact(f64::from_bits(0x0000040000000000), Sign::Positive) ); assert_eq!(f64::encode(i64::MAX, -1115), Inexact(f64::from_bits(0x400000), Sign::Positive)); // other cases assert_eq!(f32::encode(1, 0), Exact(1f32)); assert_eq!(f64::encode(1, 0), Exact(1f64)); assert_eq!(f32::encode(0x1000000, -173), Exact(f32::from_bits(0x1))); assert_eq!(f64::encode(0x40000000000000, -1128), Exact(f64::from_bits(0x1))); } } dashu-base-0.4.1/src/error.rs000064400000000000000000000030520072674642500141600ustar 00000000000000//! Error types. use core::fmt::{self, Display, Formatter}; /// Number out of bounds. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ConversionError { /// The number is not in the representation range OutOfBounds, /// The conversion will cause a loss of precision LossOfPrecision, } impl Display for ConversionError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { ConversionError::OutOfBounds => f.write_str("number out of bounds"), ConversionError::LossOfPrecision => f.write_str("number can't be converted losslessly"), } } } #[cfg(feature = "std")] impl std::error::Error for ConversionError {} /// Error parsing a number. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ParseError { /// No digits in the string. NoDigits, /// Invalid digit for a given radix. InvalidDigit, /// The radix is not supported. UnsupportedRadix, /// The radices of different components of the number are different InconsistentRadix, } impl Display for ParseError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { ParseError::NoDigits => f.write_str("no digits"), ParseError::InvalidDigit => f.write_str("invalid digit"), ParseError::UnsupportedRadix => f.write_str("unsupported radix"), ParseError::InconsistentRadix => f.write_str("inconsistent radix"), } } } #[cfg(feature = "std")] impl std::error::Error for ParseError {} dashu-base-0.4.1/src/lib.rs000064400000000000000000000007360072674642500136030ustar 00000000000000//! This crate contains general trait definitions and some commonly used structs and enums. #![cfg_attr(not(feature = "std"), no_std)] pub mod approx; pub mod bit; pub mod error; pub mod math; pub mod ring; pub mod sign; /// Some useful utility functions that are also used internally in this crate. pub mod utils { pub use super::math::log::{next_down, next_up}; } pub use approx::*; pub use bit::*; pub use error::*; pub use math::*; pub use ring::*; pub use sign::*; dashu-base-0.4.1/src/math/inv.rs000064400000000000000000000010010072674642500145440ustar 00000000000000use super::Inverse; impl Inverse for f32 { type Output = f32; #[inline] fn inv(self) -> f32 { 1.0 / self } } impl Inverse for &f32 { type Output = f32; #[inline] fn inv(self) -> f32 { 1.0 / *self } } impl Inverse for f64 { type Output = f64; #[inline] fn inv(self) -> f64 { 1.0 / self } } impl Inverse for &f64 { type Output = f64; #[inline] fn inv(self) -> f64 { 1.0 / *self } } dashu-base-0.4.1/src/math/log.rs000064400000000000000000000326750072674642500145560ustar 00000000000000use super::EstimatedLog2; // 8bit fixed point estimation of log2(x), x from 0x80 to 0xff, rounding down. #[cfg(not(feature = "std"))] const LOG2_TAB: [u8; 128] = [ 0x00, 0x02, 0x05, 0x08, 0x0b, 0x0e, 0x10, 0x13, 0x16, 0x19, 0x1b, 0x1e, 0x21, 0x23, 0x26, 0x28, 0x2b, 0x2e, 0x30, 0x33, 0x35, 0x38, 0x3a, 0x3d, 0x3f, 0x41, 0x44, 0x46, 0x49, 0x4b, 0x4d, 0x50, 0x52, 0x54, 0x57, 0x59, 0x5b, 0x5d, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6d, 0x6f, 0x71, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d, 0x7f, 0x81, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8d, 0x8f, 0x91, 0x93, 0x95, 0x97, 0x99, 0x9b, 0x9d, 0x9f, 0xa1, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xad, 0xaf, 0xb1, 0xb3, 0xb5, 0xb6, 0xb8, 0xba, 0xbc, 0xbd, 0xbf, 0xc1, 0xc2, 0xc4, 0xc6, 0xc8, 0xc9, 0xcb, 0xcd, 0xce, 0xd0, 0xd1, 0xd3, 0xd5, 0xd6, 0xd8, 0xda, 0xdb, 0xdd, 0xde, 0xe0, 0xe1, 0xe3, 0xe5, 0xe6, 0xe8, 0xe9, 0xeb, 0xec, 0xee, 0xef, 0xf1, 0xf2, 0xf4, 0xf5, 0xf7, 0xf8, 0xfa, 0xfb, 0xfd, 0xfe, ]; /// A 8bit fixed point estimation of log2(n), the result /// is always less than the exact value and estimation error ≤ 2. #[cfg(not(feature = "std"))] const fn log2_fp8(n: u16) -> u16 { debug_assert!(n > 0xff); // if the input is small, it should be powered first let nbits = (u16::BITS - n.leading_zeros()) as u16; if n < 0x200 { // err = 0~2 in this range, use extra 1 bit to reduce error let lookup = LOG2_TAB[(n >> 1) as usize - 0x80]; let est = lookup as u16 + (7 + 1) * 256; est + (n < 354 && n & 1 > 0) as u16 } else if n < (0x4000 + 0x80) { // err = 0~3, use extra 2 bits to reduce error let shift = nbits - 8; let mask = n >> (shift - 2); let lookup = LOG2_TAB[(mask >> 2) as usize - 0x80]; let est = lookup as u16 + (7 + shift) * 256; // err could be 0 if mask & 3 < 3 est + (mask & 3 == 3) as u16 } else { // err = 0~3, use extra 7 bits to reduce error let shift = nbits - 8; let mask = n >> (shift - 7); let top_est = LOG2_TAB[(mask >> 7) as usize - 0x80]; let est = top_est as u16 + (7 + shift) * 256; // err could be 0 if mask & 127 < 80 est + (mask & 127 >= 80) as u16 } } /// A 8bit fixed point estimation of log2(n), the result /// is always greater than the exact value and estimation error ≤ 2. /// /// # Panics /// /// Panics if n is a power of two, in which case the log should /// be trivially handled. #[cfg(not(feature = "std"))] const fn ceil_log2_fp8(n: u16) -> u16 { debug_assert!(n > 0xff); // if the input is small, it should be powered first debug_assert!(!n.is_power_of_two()); let nbits = (u16::BITS - n.leading_zeros()) as u16; if n < 0x80 { // err = 0 in this range let shift = 8 - nbits; let top_est = LOG2_TAB[(n << shift) as usize - 0x80]; top_est as u16 + (7 - shift) * 256 + 1 } else if n < 0x200 { // err = 0 in 0x80 ~ 0x100, err = 0~2 in 0x100 ~ 0x200 let shift = nbits - 8; let top_est = LOG2_TAB[(n >> shift) as usize - 0x80]; let est = top_est as u16 + (7 + shift) * 256 + 1; if n > 0x100 && n & 1 == 1 { est + 2 } else { est } } else { // err = 0~3, use extra 2 bits to reduce error let shift = nbits - 8; let mask10 = n >> (shift - 2); let mask8 = mask10 >> 2; if mask8 == 255 { 0x100 + (7 + shift) * 256 } else { // find next item in LOG2_TAB let top_est = LOG2_TAB[mask8 as usize + 1 - 0x80]; let est = top_est as u16 + (7 + shift) * 256 + 1; est - (mask10 & 3 == 0) as u16 } } } /// Implementation of the nightly f32::next_up() /// /// This function will panic if the input is NaN or infinite. #[inline] pub fn next_up(f: f32) -> f32 { assert!(!f.is_nan() && !f.is_infinite()); const TINY_BITS: u32 = 0x1; // Smallest positive f32. const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff; let bits = f.to_bits(); let abs = bits & CLEAR_SIGN_MASK; let next_bits = if abs == 0 { TINY_BITS } else if bits == abs { bits + 1 } else { bits - 1 }; f32::from_bits(next_bits) } /// Implementation of the nightly f32::next_down() /// /// This function will panic if the input is NaN or infinite. #[inline] pub fn next_down(f: f32) -> f32 { assert!(!f.is_nan() && !f.is_infinite()); const NEG_TINY_BITS: u32 = 0x8000_0001; // Smallest negative f32. const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff; let bits = f.to_bits(); let abs = bits & CLEAR_SIGN_MASK; let next_bits = if abs == 0 { NEG_TINY_BITS } else if bits == abs { bits - 1 } else { bits + 1 }; f32::from_bits(next_bits) } #[cfg(not(feature = "std"))] impl EstimatedLog2 for u8 { #[inline] fn log2_bounds(&self) -> (f32, f32) { match *self { 0 => (f32::NEG_INFINITY, f32::NEG_INFINITY), 1 => (0., 0.), i if i.is_power_of_two() => { let log = self.trailing_zeros() as f32; (log, log) } 3 => (1.5849625, 1.5849626), i if i < 16 => { let pow = (i as u16).pow(4); let lb = log2_fp8(pow) as f32 / 256.0; let ub = ceil_log2_fp8(pow) as f32 / 256.0; (lb / 4., ub / 4.) } i => { let pow = (i as u16).pow(2); let lb = log2_fp8(pow) as f32 / 256.0; let ub = ceil_log2_fp8(pow) as f32 / 256.0; (lb / 2., ub / 2.) } } } } #[cfg(not(feature = "std"))] impl EstimatedLog2 for u16 { #[inline] fn log2_bounds(&self) -> (f32, f32) { if *self <= 0xff { return (*self as u8).log2_bounds(); } else if self.is_power_of_two() { let log = self.trailing_zeros() as f32; return (log, log); } let lb = log2_fp8(*self) as f32 / 256.0; let ub = ceil_log2_fp8(*self) as f32 / 256.0; (lb, ub) } } #[cfg(not(feature = "std"))] macro_rules! impl_log2_bounds_for_uint { ($($t:ty)*) => {$( impl EstimatedLog2 for $t { #[inline] fn log2_bounds(&self) -> (f32, f32) { if *self <= 0xff { return (*self as u8).log2_bounds(); } else if self.is_power_of_two() { let log = self.trailing_zeros() as f32; return (log, log); } let bits = <$t>::BITS - self.leading_zeros(); if bits <= u16::BITS { let lb = log2_fp8(*self as u16) as f32 / 256.0; let ub = ceil_log2_fp8(*self as u16) as f32 / 256.0; (lb, ub) } else { let shift = bits - u16::BITS; let hi = (*self >> shift) as u16; let lb = log2_fp8(hi) as f32 / 256.0; let ub = if hi == 1 << (u16::BITS - 1) { // specially handled because ceil_log2_fp8 disallow a power of 2 (u16::BITS as u16 - 1) * 256 + 1 } else { // in this case, the ceiling handled by the highest word // will cover the requirement for ceiling the low bits ceil_log2_fp8(hi) }; let ub = ub as f32 / 256.0; (next_down(lb + shift as f32), next_up(ub + shift as f32)) } } } )*}; } #[cfg(not(feature = "std"))] impl_log2_bounds_for_uint!(u32 u64 u128 usize); #[cfg(feature = "std")] macro_rules! impl_log2_bounds_for_uint { ($($t:ty)*) => {$( impl EstimatedLog2 for $t { fn log2_bounds(&self) -> (f32, f32) { if *self == 0 { return (f32::NEG_INFINITY, f32::NEG_INFINITY); } if self.is_power_of_two() { let log = self.trailing_zeros() as f32; (log, log) } else { let nbits = Self::BITS - self.leading_zeros(); if nbits <= 24 { // 24bit integer converted to f32 is lossless let log = (*self as f32).log2(); (next_down(log), next_up(log)) } else { let shifted = (self >> (nbits - 24)) as f32; let est_lb = shifted.log2(); let est_ub = (shifted + 1.).log2(); let shift = (nbits - 24) as f32; (next_down(est_lb + shift), next_up(est_ub + shift)) } } } #[inline] fn log2_est(&self) -> f32 { (*self as f32).log2() } } )*} } #[cfg(feature = "std")] impl_log2_bounds_for_uint!(u8 u16 u32 u64 u128 usize); macro_rules! impl_log2_bounds_for_int { ($($t:ty)*) => {$( impl EstimatedLog2 for $t { fn log2_bounds(&self) -> (f32, f32) { self.unsigned_abs().log2_bounds() } } )*}; } impl_log2_bounds_for_int!(i8 i16 i32 i64 i128 isize); #[cfg(not(feature = "std"))] macro_rules! impl_log2_bounds_for_float { ($($t:ty)*) => {$( impl EstimatedLog2 for $t { fn log2_bounds(&self) -> (f32, f32) { use crate::FloatEncoding; use core::num::FpCategory::*; if *self == 0. { (f32::NEG_INFINITY, f32::NEG_INFINITY) } else { match self.decode() { Ok((man, exp)) => { let (est_lb, est_ub) = man.log2_bounds(); (est_lb + exp as f32, est_ub + exp as f32) }, Err(Nan) => panic!("calling log2 on nans is forbidden!"), Err(Infinite) => (f32::INFINITY, f32::INFINITY), _ => unreachable!() } } } } )*}; } #[cfg(not(feature = "std"))] impl_log2_bounds_for_float!(f32 f64); #[cfg(feature = "std")] macro_rules! impl_log2_bounds_for_float { ($($t:ty)*) => {$( impl EstimatedLog2 for $t { #[inline] fn log2_bounds(&self) -> (f32, f32) { assert!(!self.is_nan()); if *self == 0. { (f32::NEG_INFINITY, f32::NEG_INFINITY) } else if self.is_infinite() { (f32::INFINITY, f32::INFINITY) } else { let log2 = self.abs().log2() as f32; (next_down(log2), next_up(log2)) } } #[inline] fn log2_est(&self) -> f32 { assert!(!self.is_nan()); if *self == 0. { f32::NEG_INFINITY } else if self.is_infinite() { f32::INFINITY } else { self.abs().log2() as f32 } } } )*}; } #[cfg(feature = "std")] impl_log2_bounds_for_float!(f32 f64); #[cfg(test)] mod tests { use super::*; #[test] #[cfg(not(feature = "std"))] fn test_log2_fp8() { assert_eq!(log2_fp8(1234), 2628); // err = 0 assert_eq!(log2_fp8(12345), 3478); // err = 1 assert_eq!(log2_fp8(0x100), 2048); // err = 0 assert_eq!(log2_fp8(0x101), 2049); // err = 0 assert_eq!(log2_fp8(0xff00), 4094); // err = 0 assert_eq!(log2_fp8(0xffff), 4095); // err = 0 assert_eq!(ceil_log2_fp8(1234), 2631); // err = 2 assert_eq!(ceil_log2_fp8(12345), 3480); // err = 0 assert_eq!(ceil_log2_fp8(0x101), 2051); // err = 1 assert_eq!(ceil_log2_fp8(0xff00), 4096); // err = 1 assert_eq!(ceil_log2_fp8(0xffff), 4096); // err = 0 } #[test] fn test_log2_bounds() { assert_eq!(0u8.log2_bounds(), (f32::NEG_INFINITY, f32::NEG_INFINITY)); assert_eq!(0i8.log2_bounds(), (f32::NEG_INFINITY, f32::NEG_INFINITY)); assert_eq!(0f32.log2_bounds(), (f32::NEG_INFINITY, f32::NEG_INFINITY)); // small tests for i in 1..1000u16 { let (lb, ub) = i.log2_bounds(); assert!(2f64.powf(lb as f64) <= i as f64); assert!(2f64.powf(ub as f64) >= i as f64); assert_eq!((-(i as i16)).log2_bounds(), (lb, ub)); let (lb, ub) = (i as f32).log2_bounds(); assert!(2f64.powf(lb as f64) <= i as f64); assert!(2f64.powf(ub as f64) >= i as f64); let (lb, ub) = (i as f64).log2_bounds(); assert!(2f64.powf(lb as f64) <= i as f64); assert!(2f64.powf(ub as f64) >= i as f64); } // large tests for i in (0x4000..0x400000u32).step_by(0x1001) { let (lb, ub) = i.log2_bounds(); assert!(2f64.powf(lb as f64) <= i as f64); assert!(2f64.powf(ub as f64) >= i as f64); } let (lb, ub) = 1e20f32.log2_bounds(); assert!(2f64.powf(lb as f64) <= 1e20); assert!(2f64.powf(ub as f64) >= 1e20); assert_eq!((-1e20f32).log2_bounds(), (lb, ub)); let (lb, ub) = 1e40f64.log2_bounds(); assert!(2f64.powf(lb as f64) <= 1e40); assert!(2f64.powf(ub as f64) >= 1e40); assert_eq!((-1e40f64).log2_bounds(), (lb, ub)); } } dashu-base-0.4.1/src/math/mod.rs000064400000000000000000000050550072674642500145440ustar 00000000000000//! Trait definitions for math operations /// Fast estimation of the binary logarithm of a number /// /// # Panics /// /// Panics if the number is 0 /// /// # Examples /// /// ``` /// use dashu_base::EstimatedLog2; /// /// let lb3 = 1.584962500721156f32; /// let (lb3_lb, lb3_ub) = 3u8.log2_bounds(); /// assert!(lb3_lb <= lb3 && lb3 <= lb3_ub); /// assert!((lb3 - lb3_lb) / lb3 < 1. / 256.); /// assert!((lb3_ub - lb3) / lb3 <= 1. / 256.); /// /// let lb3_est = 3u8.log2_est(); /// assert!((lb3 - lb3_est).abs() < 1e-3); /// ``` pub trait EstimatedLog2 { /// Estimate the bounds of the binary logarithm. /// /// The result is `(lower bound, upper bound)` such that `lower bound ≤ log2(self) ≤ upper bound`. /// The precision of the bounds must be at least 8 bits (relative error < 2^-8). /// /// With `std` disabled, the precision is about 13 bits. With `std` enabled, the precision /// can be full 24 bits. But the exact precision is not guaranteed and should not be not /// relied on. /// /// For negative values, the logarithm is calculated based on its absolute value. If the number /// is zero, then negative infinity will be returned. /// fn log2_bounds(&self) -> (f32, f32); /// Estimate the value of the binary logarithm. It's calculated as the /// average of [log2_bounds][EstimatedLog2::log2_bounds] by default. #[inline] fn log2_est(&self) -> f32 { let (lb, ub) = self.log2_bounds(); (lb + ub) / 2. } } /// Compute the multiplicative inverse (aka. reciprocal) of the number. /// /// # Examples /// /// ``` /// # use dashu_base::Inverse; /// assert_eq!(0.1234.inv(), 8.103727714748784); /// assert_eq!(f32::INFINITY.inv(), 0f32); /// ``` pub trait Inverse { type Output; fn inv(self) -> Self::Output; } /// Compute the square root of the number. /// /// The result should be rounded towards zero by default. /// /// # Examples /// /// ``` /// # use dashu_base::SquareRoot; /// assert_eq!(256u32.sqrt(), 16); /// assert_eq!(257u32.sqrt(), 16); /// ``` pub trait SquareRoot { type Output; fn sqrt(&self) -> Self::Output; } /// Compute the cubic root of the number. /// /// The result should be rounded towards zero by default. /// /// # Examples /// /// ``` /// # use dashu_base::CubicRoot; /// assert_eq!(216u32.cbrt(), 6); /// assert_eq!(217u32.cbrt(), 6); /// ``` pub trait CubicRoot { type Output; fn cbrt(&self) -> Self::Output; } mod inv; pub(crate) mod log; mod root; dashu-base-0.4.1/src/math/root.rs000064400000000000000000000035530072674642500147510ustar 00000000000000use super::{CubicRoot, SquareRoot}; use crate::{CubicRootRem, NormalizedRootRem, SquareRootRem}; // TODO(next): forward sqrt to f32/f64 if std is enabled and the input is small enough. // Implement after we have a benchmark. See https://github.com/Aatch/ramp/blob/master/src/int.rs#L579. impl SquareRoot for u8 { type Output = u8; #[inline] fn sqrt(&self) -> Self::Output { self.sqrt_rem().0 } } impl CubicRoot for u8 { type Output = u8; #[inline] fn cbrt(&self) -> Self::Output { self.cbrt_rem().0 } } macro_rules! impl_root_using_rootrem { ($t:ty, $half:ty) => { impl SquareRoot for $t { type Output = $half; #[inline] fn sqrt(&self) -> $half { if *self == 0 { return 0; } // normalize the input and call the normalized subroutine let shift = self.leading_zeros() & !1; // make sure shift is divisible by 2 let (root, _) = (self << shift).normalized_sqrt_rem(); root >> (shift / 2) } } impl CubicRoot for $t { type Output = $half; #[inline] fn cbrt(&self) -> $half { if *self == 0 { return 0; } // normalize the input and call the normalized subroutine let mut shift = self.leading_zeros(); shift -= shift % 3; // make sure shift is divisible by 3 let (root, _) = (self << shift).normalized_cbrt_rem(); root >> (shift / 3) } } }; } impl_root_using_rootrem!(u16, u8); impl_root_using_rootrem!(u32, u16); impl_root_using_rootrem!(u64, u32); impl_root_using_rootrem!(u128, u64); dashu-base-0.4.1/src/ring/div_rem.rs000064400000000000000000000045210072674642500154150ustar 00000000000000use super::{DivEuclid, DivRem, DivRemAssign, DivRemEuclid, RemEuclid}; macro_rules! impl_div_rem_ops_prim { ($($T:ty)*) => {$( impl DivRem for $T { type OutputDiv = $T; type OutputRem = $T; #[inline] fn div_rem(self, rhs: $T) -> ($T, $T) { (self / rhs, self % rhs) } } impl DivRemAssign for $T { type OutputRem = $T; #[inline] fn div_rem_assign(&mut self, rhs: $T) -> $T { let r = *self % rhs; *self /= rhs; r } } impl DivEuclid for $T { type Output = $T; #[inline] fn div_euclid(self, rhs: $T) -> $T { <$T>::div_euclid(self, rhs) } } impl RemEuclid for $T { type Output = $T; #[inline] fn rem_euclid(self, rhs: $T) -> $T { <$T>::rem_euclid(self, rhs) } } impl DivRemEuclid for $T { type OutputDiv = $T; type OutputRem = $T; #[inline] fn div_rem_euclid(self, rhs: $T) -> ($T, $T) { let (q, r) = (self / rhs, self % rhs); // depending on compiler to simplify the case for unsinged integers #[allow(unused_comparisons)] if r >= 0 { (q, r) } else if rhs >= 0{ (q - 1, r + rhs) } else { (q + 1, r - rhs) } } } )*} } impl_div_rem_ops_prim!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); #[cfg(test)] mod tests { use super::*; #[test] fn test_simple() { assert_eq!(7u32.div_rem(4), (1, 3)); assert_eq!(7u32.div_rem_euclid(4), (1, 3)); assert_eq!(7i32.div_rem(-4), (-1, 3)); assert_eq!(7i32.div_rem_euclid(-4), (-1, 3)); assert_eq!((-7i32).div_rem(4), (-1, -3)); assert_eq!((-7i32).div_rem_euclid(4), (-2, 1)); assert_eq!((-7i32).div_rem(-4), (1, -3)); assert_eq!((-7i32).div_rem_euclid(-4), (2, 1)); let mut n = 7u32; let r = n.div_rem_assign(4); assert!(n == 1 && r == 3); } } dashu-base-0.4.1/src/ring/gcd.rs000064400000000000000000000241330072674642500145260ustar 00000000000000use super::{ExtendedGcd, Gcd}; use core::mem::replace; trait UncheckedGcd { type Output; /// GCD with assumptions that (1) at least one of the input is not zero, (2) the /// two operands are relatively close, (3) the factor 2 is removed from the operands. /// For internal use only. fn unchecked_gcd(self, rhs: Rhs) -> Self::Output; } trait UncheckedExtendedGcd { type OutputGcd; type OutputCoeff; /// Extended GCD with assumptions that (1) at least one of the input is not zero, /// (2) the first oprand is larger than the second. For internal use only. fn unchecked_gcd_ext(self, rhs: Rhs) -> (Self::OutputGcd, Self::OutputCoeff, Self::OutputCoeff); } macro_rules! impl_unchecked_gcd_ops_prim { ($($U:ty | $I:ty;)*) => {$( impl UncheckedGcd for $U { type Output = $U; #[inline] fn unchecked_gcd(self, rhs: Self) -> Self::Output { debug_assert!(self | rhs > 0); debug_assert!(self & rhs & 1 > 0); let (mut a, mut b) = (self, rhs); // the binary GCD algorithm while a != b { if a > b { a -= b; a >>= a.trailing_zeros(); } else { b -= a; b >>= b.trailing_zeros(); } } a } } impl UncheckedExtendedGcd for $U { type OutputGcd = $U; type OutputCoeff = $I; #[inline] fn unchecked_gcd_ext(self, rhs: $U) -> ($U, $I, $I) { debug_assert!(self | rhs > 0); debug_assert!(self >= rhs); // keep r = self * s + rhs * t let (mut last_r, mut r) = (self, rhs); let (mut last_s, mut s) = (1, 0); let (mut last_t, mut t) = (0, 1); loop { let quo = last_r / r; let new_r = last_r - quo * r; if new_r == 0 { return (r, s, t) } last_r = replace(&mut r, new_r); let new_s = last_s - quo as $I * s; last_s = replace(&mut s, new_s); let new_t = last_t - quo as $I * t; last_t = replace(&mut t, new_t); } } } )*}; ($($U:ty | $I:ty => $HU:ty | $HI:ty;)*) => {$( // treat the integers as two parts impl UncheckedGcd for $U { type Output = $U; fn unchecked_gcd(self, rhs: Self) -> Self::Output { debug_assert!(self | rhs > 0); debug_assert!(self & rhs & 1 > 0); let (mut a, mut b) = (self, rhs); // the binary GCD algorithm while a != b { if (a | b) >> <$HU>::BITS == 0 { // forward to single width int return (a as $HU).unchecked_gcd(b as $HU) as $U; } if a > b { a -= b; a >>= a.trailing_zeros(); } else { b -= a; b >>= b.trailing_zeros(); } } a } } impl UncheckedExtendedGcd for $U { type OutputGcd = $U; type OutputCoeff = $I; fn unchecked_gcd_ext(self, rhs: $U) -> ($U, $I, $I) { debug_assert!(self | rhs > 0); debug_assert!(self >= rhs); // keep r = self * s + rhs * t let (mut last_r, mut r) = (self, rhs); let (mut last_s, mut s) = (1, 0); let (mut last_t, mut t) = (0, 1); // normal euclidean algorithm on double width integers while r >> <$HU>::BITS > 0 { let quo = last_r / r; let new_r = last_r - quo * r; if new_r == 0 { return (r, s, t); } last_r = replace(&mut r, new_r); let new_s = last_s - quo as $I * s; last_s = replace(&mut s, new_s); let new_t = last_t - quo as $I * t; last_t = replace(&mut t, new_t); } // reduce double by single let r = r as $HU; let quo = last_r / r as $U; let new_r = (last_r - quo * r as $U) as $HU; if new_r == 0 { return (r as $U, s, t); } let new_s = last_s - quo as $I * s; let new_t = last_t - quo as $I * t; // forward to single width int let (g, cx, cy) = r.unchecked_gcd_ext(new_r); let (cx, cy) = (cx as $I, cy as $I); (g as $U, &cx * s + &cy * new_s, cx * t + cy * new_t) } } )*} } impl_unchecked_gcd_ops_prim!(u8 | i8; u16 | i16; usize | isize;); #[cfg(target_pointer_width = "16")] impl_unchecked_gcd_ops_prim!(u32 | i32 => u16 | i16; u64 | i64 => u32 | i32; u128 | i128 => u64 | i64;); #[cfg(target_pointer_width = "32")] impl_unchecked_gcd_ops_prim!(u32 | i32;); #[cfg(target_pointer_width = "32")] impl_unchecked_gcd_ops_prim!(u64 | i64 => u32 | i32; u128 | i128 => u64 | u64;); #[cfg(target_pointer_width = "64")] impl_unchecked_gcd_ops_prim!(u32 | i32; u64 | i64;); #[cfg(target_pointer_width = "64")] impl_unchecked_gcd_ops_prim!(u128 | i128 => u64 | i64;); macro_rules! impl_gcd_ops_prim { ($($U:ty | $I:ty;)*) => {$( impl Gcd for $U { type Output = $U; #[inline] fn gcd(self, rhs: Self) -> Self::Output { let (mut a, mut b) = (self, rhs); if a == 0 || b == 0 { if a == 0 && b == 0 { panic_gcd_0_0(); } return a | b; } // find common factors of 2 let shift = (a | b).trailing_zeros(); a >>= a.trailing_zeros(); b >>= b.trailing_zeros(); // reduce by division if the difference between operands is large let (za, zb) = (a.leading_zeros(), b.leading_zeros()); const GCD_BIT_DIFF_THRESHOLD: u32 = 3; if za > zb.wrapping_add(GCD_BIT_DIFF_THRESHOLD) { let r = b % a; if r == 0 { return a << shift; } else { b = r >> r.trailing_zeros(); } } else if zb > za.wrapping_add(4) { let r = a % b; if r == 0 { return b << shift; } else { a = r >> r.trailing_zeros(); } } // forward to the gcd algorithm a.unchecked_gcd(b) << shift } } impl ExtendedGcd for $U { type OutputGcd = $U; type OutputCoeff = $I; #[inline] fn gcd_ext(self, rhs: $U) -> ($U, $I, $I) { let (mut a, mut b) = (self, rhs); // check if zero inputs match (a == 0, b == 0) { (true, true) => panic_gcd_0_0(), (true, false) => return (b, 0, 1), (false, true) => return (a, 1, 0), _ => {} } // find common factors of 2 let shift = (a | b).trailing_zeros(); a >>= shift; b >>= shift; // make sure a is larger than b if a >= b { if b == 1 { // this shortcut eliminates the overflow when a = <$T>::MAX and b = 1 (1 << shift, 0, 1) } else { // forward to the gcd algorithm let (g, ca, cb) = a.unchecked_gcd_ext(b); (g << shift, ca, cb) } } else { if a == 1 { (1 << shift, 1, 0) } else { let (g, cb, ca) = b.unchecked_gcd_ext(a); (g << shift, ca, cb) } } } } )*} } impl_gcd_ops_prim!(u8 | i8; u16 | i16; u32 | i32; u64 | i64; u128 | i128; usize | isize;); fn panic_gcd_0_0() -> ! { panic!("the greatest common divisor is not defined between zeros!") } #[cfg(test)] mod tests { use super::*; #[test] fn test_simple() { assert_eq!(12u8.gcd(18), 6); assert_eq!(16u16.gcd(2032), 16); assert_eq!(0x40000000u32.gcd(0xcfd41b91), 1); assert_eq!( 0x80000000000000000000000000000000u128.gcd(0x6f32f1ef8b18a2bc3cea59789c79d441), 1 ); assert_eq!( 79901280795560547607793891992771245827u128.gcd(27442821378946980402542540754159585749), 1 ); let result = 12u8.gcd_ext(18); assert_eq!(result, (6, -1, 1)); let result = 16u16.gcd_ext(2032); assert_eq!(result, (16, 1, 0)); let result = 0x40000000u32.gcd_ext(0xcfd41b91); assert_eq!(result, (1, -569926925, 175506801)); let result = 0x80000000000000000000000000000000u128.gcd_ext(0x6f32f1ef8b18a2bc3cea59789c79d441); assert_eq!( result, ( 1, 59127885930508821681098646892310825630, -68061485417298041807799738471800882239 ) ); } } dashu-base-0.4.1/src/ring/mod.rs000064400000000000000000000066510072674642500145550ustar 00000000000000//! Trait definitions for operations related to rings (integer/polynomial/etc.) /// Compute quotient and remainder at the same time. /// /// # Examples /// ``` /// use dashu_base::DivRem; /// assert_eq!(23.div_rem(10), (2, 3)); /// ``` pub trait DivRem { type OutputDiv; type OutputRem; fn div_rem(self, rhs: Rhs) -> (Self::OutputDiv, Self::OutputRem); } /// Compute quotient inplace and return remainder at the same time. /// /// # Examples /// ``` /// use dashu_base::DivRemAssign; /// let mut n = 23; /// let r = n.div_rem_assign(10); /// assert!(n == 2 && r == 3); /// ``` pub trait DivRemAssign { type OutputRem; fn div_rem_assign(&mut self, rhs: Rhs) -> Self::OutputRem; } /// Compute Euclidean quotient. /// /// # Examples /// ``` /// use dashu_base::DivEuclid; /// assert_eq!((-23).div_euclid(10), -3); /// ``` pub trait DivEuclid { type Output; fn div_euclid(self, rhs: Rhs) -> Self::Output; } /// Compute Euclidean remainder. /// /// # Examples /// ``` /// use dashu_base::RemEuclid; /// assert_eq!((-23).rem_euclid(10), 7); /// ``` pub trait RemEuclid { type Output; fn rem_euclid(self, rhs: Rhs) -> Self::Output; } /// Compute Euclidean quotient and remainder at the same time. /// /// # Examples /// ``` /// use dashu_base::DivRemEuclid; /// assert_eq!((-23).div_rem_euclid(10), (-3, 7)); /// ``` pub trait DivRemEuclid { type OutputDiv; type OutputRem; fn div_rem_euclid(self, rhs: Rhs) -> (Self::OutputDiv, Self::OutputRem); } /// Compute the greatest common divisor. /// /// For negative integers, the common divisor is still kept positive. /// /// # Examples /// ``` /// use dashu_base::Gcd; /// assert_eq!(12u8.gcd(10u8), 2); /// ``` /// /// # Panics /// /// Panics if both operands are zeros pub trait Gcd { type Output; /// Compute the greatest common divisor between the two operands. /// /// Panics if both operands are zeros fn gcd(self, rhs: Rhs) -> Self::Output; } /// Compute the greatest common divisor between self and the other operand, and return /// both the common divisor `g` and the Bézout coefficients respectively. /// /// For negative integers, the common divisor is still kept positive. /// /// # Examples /// ``` /// use dashu_base::{Gcd, ExtendedGcd}; /// let (g, cx, cy) = 12u8.gcd_ext(10u8); /// assert_eq!(g, 12u8.gcd(10u8)); /// assert_eq!(g as i8, 12 * cx + 10 * cy); /// ``` /// /// # Panics /// /// Panics if both operands are zeros pub trait ExtendedGcd { type OutputGcd; type OutputCoeff; /// Calculate the greatest common divisor between the two operands, returns /// the common divisor `g` and the Bézout coefficients respectively. /// /// Panics if both operands are zeros fn gcd_ext(self, rhs: Rhs) -> (Self::OutputGcd, Self::OutputCoeff, Self::OutputCoeff); } /// Computer the floored square root of the number and return the remainder at the same time. pub trait SquareRootRem { type Output; fn sqrt_rem(&self) -> (Self::Output, Self); } /// Computer the floored cubic root of the number and return the remainder at the same time. pub trait CubicRootRem { type Output; fn cbrt_rem(&self) -> (Self::Output, Self); } mod div_rem; mod gcd; mod root; pub(crate) use root::NormalizedRootRem; dashu-base-0.4.1/src/ring/root.rs000064400000000000000000000473600072674642500147630ustar 00000000000000use super::{CubicRootRem, SquareRootRem}; use crate::DivRem; pub(crate) trait NormalizedRootRem: Sized { type OutputRoot; /// Square root with the normalized input such that highest or second /// highest bit are set. For internal use only. fn normalized_sqrt_rem(self) -> (Self::OutputRoot, Self); /// Cubic root with the normalized input such that at least one of the /// highest three bits are set. For internal use only. fn normalized_cbrt_rem(self) -> (Self::OutputRoot, Self); } // Estimations of normalized 1/sqrt(x) with 9 bits precision. Specifically // (rsqrt_tab[i] + 0x100) / 0x200 ≈ (sqrt(32) / sqrt(32 + i)) const RSQRT_TAB: [u8; 96] = [ 0xfc, 0xf4, 0xed, 0xe6, 0xdf, 0xd9, 0xd3, 0xcd, 0xc7, 0xc2, 0xbc, 0xb7, 0xb2, 0xad, 0xa9, 0xa4, 0xa0, 0x9c, 0x98, 0x94, 0x90, 0x8c, 0x88, 0x85, 0x81, 0x7e, 0x7b, 0x77, 0x74, 0x71, 0x6e, 0x6b, 0x69, 0x66, 0x63, 0x61, 0x5e, 0x5b, 0x59, 0x57, 0x54, 0x52, 0x50, 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37, 0x36, 0x34, 0x32, 0x30, 0x2f, 0x2d, 0x2c, 0x2a, 0x28, 0x27, 0x25, 0x24, 0x22, 0x21, 0x1f, 0x1e, 0x1d, 0x1b, 0x1a, 0x19, 0x17, 0x16, 0x15, 0x14, 0x12, 0x11, 0x10, 0x0f, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, ]; // Estimations of normalized 1/cbrt(x) with 9 bits precision. Specifically // (rcbrt_tab[i] + 0x100) / 0x200 ≈ (cbrt(8) / cbrt(8 + i)) const RCBRT_TAB: [u8; 56] = [ 0xf6, 0xe4, 0xd4, 0xc6, 0xb9, 0xae, 0xa4, 0x9b, 0x92, 0x8a, 0x83, 0x7c, 0x76, 0x70, 0x6b, 0x66, 0x61, 0x5c, 0x57, 0x53, 0x4f, 0x4b, 0x48, 0x44, 0x41, 0x3e, 0x3b, 0x38, 0x35, 0x32, 0x2f, 0x2d, 0x2a, 0x28, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x10, 0x0e, 0x0c, 0x0b, 0x09, 0x08, 0x06, 0x05, 0x03, 0x02, 0x01, ]; /// Fix the estimation error of `sqrt(n)`, `s` is the (mutable) estimation variable, /// This procedure requires s <= `sqrt(n)`, returns the error `n - s^2`. macro_rules! fix_sqrt_error { ($t:ty, $n:ident, $s:ident) => {{ let mut e = $n - ($s as $t).pow(2); let mut elim = 2 * $s as $t + 1; while e >= elim { $s += 1; e -= elim; elim += 2; } e }}; } /// Fix the estimation error of `cbrt(n)`, `c` is the (mutable) estimation variable, /// This procedure requires c <= `cbrt(n)`, returns the error `n - c^3`. macro_rules! fix_cbrt_error { ($t:ty, $n:ident, $c:ident) => {{ let cc = ($c as $t).pow(2); let mut e = $n - cc * ($c as $t); let mut elim = 3 * (cc + $c as $t) + 1; while e >= elim { $c += 1; e -= elim; elim += 6 * ($c as $t); } e }}; } impl NormalizedRootRem for u16 { type OutputRoot = u8; fn normalized_sqrt_rem(self) -> (u8, u16) { debug_assert!(self.leading_zeros() <= 1); // retrieved r ≈ √32 / √(n >> 9) * 0x200 = 1 / √(n >> 14) * 2^9 = 2^16 / √n. let r = 0x100 | RSQRT_TAB[(self >> 9) as usize - 32] as u32; // 9 bits let s = (r * self as u32) >> 16; let mut s = (s - 1) as u8; // to make sure s is an underestimate // then fix the estimation error let e = fix_sqrt_error!(u16, self, s); (s, e) } fn normalized_cbrt_rem(self) -> (u8, u16) { debug_assert!(self.leading_zeros() <= 2); // retrieved r ≈ ∛8 / ∛(n >> 9) * 0x200 = 1 / ∛(n >> 12) * 2^9 = 2^13 / ∛n. let adjust = self.leading_zeros() == 0; let r = 0x100 | RCBRT_TAB[(self >> (9 + (3 * adjust as u8))) as usize - 8] as u32; // 9 bits let r2 = (r * r) >> (2 + 2 * adjust as u8); let c = (r2 * self as u32) >> 24; let mut c = (c - 1) as u8; // to make sure c is an underestimate // step6: fix the estimation error, at most 2 steps are needed // if we use more bits to estimate the initial guess, less steps can be required let e = fix_cbrt_error!(u16, self, c); (c, e) } } /// Get the high part of widening mul on two u16 integers #[inline] fn wmul16_hi(a: u16, b: u16) -> u16 { (((a as u32) * (b as u32)) >> 16) as u16 } impl NormalizedRootRem for u32 { type OutputRoot = u16; fn normalized_sqrt_rem(self) -> (u16, u32) { // Use newton's method on 1/sqrt(n) // x_{i+1} = x_i * (3 - n*x_i^2) / 2 debug_assert!(self.leading_zeros() <= 1); // step1: lookup initial estimation of normalized 1/√n. The lookup table uses the highest 7 bits, // since the input is normalized, the lookup index must be larger than 2**(7-2) = 32. // then the retrieved r ≈ √32 / √(n >> 25) * 0x200 = 1 / √(n >> 30) / 2^9 = 2^24 / √n. let n16 = (self >> 16) as u16; let r = 0x100 | RSQRT_TAB[(n16 >> 9) as usize - 32] as u32; // 9 bits // step2: first Newton iteration (without dividing by 2) // r will be an estimation of 2^(24+6) / √n with 16 bits effective precision let r = ((3 * r as u16) << 5) - (wmul32_hi(self, r * r * r) >> 11) as u16; // 15 bits // step3: √n = x * 1/√n let r = r << 1; // normalize to 16 bits, now r estimates 2^31 / √n let mut s = wmul16_hi(r, n16).saturating_mul(2); // overflowing can happen s -= 4; // to make sure s is an underestimate // step4: second Newton iteration on √n let e = self - (s as u32) * (s as u32); s += wmul16_hi((e >> 16) as u16, r); // step5: fix the estimation error, at most 2 steps are needed // if we use more bits to estimate the initial guess, less steps can be required let e = fix_sqrt_error!(u32, self, s); (s, e) } fn normalized_cbrt_rem(self) -> (u16, u32) { // Use newton's method on 1/cbrt(n) // x_{i+1} = x_i * (4 - n*x_i^3) / 3 debug_assert!(self.leading_zeros() <= 2); // step1: lookup initial estimation of 1/∛x. The lookup table uses the highest 6 bits up to 30rd. // if the input is 32/31 bit, then shift it to 29/28 bit. // retrieved r ≈ ∛8 / ∛(n >> 24) * 0x200 = 1 / ∛(n >> 27) * 2^9 = 2^18 / ∛n. let adjust = self.leading_zeros() < 2; let n16 = (self >> (16 + 3 * adjust as u8)) as u16; let r = 0x100 | RCBRT_TAB[(n16 >> 8) as usize - 8] as u32; // 9 bits // step2: first Newton iteration // required shift = 18 * 3 - 11 - 16 * 2 - * 2 = 11 // afterwards, r ≈ 2^(18+11-4) / ∛n let r3 = (r * r * r) >> 11; let t = (4 << 11) - wmul16_hi(n16, r3 as u16); // 13 bits let mut r = ((r * t as u32 / 3) >> 4) as u16; // 16 bits r >>= adjust as u8; // recover the adjustment if needed // step5: ∛x = x * (1/∛x)^2 let r = r - 10; // to make sure c is an underestimate let mut c = wmul16_hi(r, wmul16_hi(r, (self >> 16) as u16)) >> 2; // step6: fix the estimation error, at most 2 steps are needed // if we use more bits to estimate the initial guess, less steps can be required let e = fix_cbrt_error!(u32, self, c); (c, e) } } /// Get the high part of widening mul on two u32 integers #[inline] fn wmul32_hi(a: u32, b: u32) -> u32 { (((a as u64) * (b as u64)) >> 32) as u32 } impl NormalizedRootRem for u64 { type OutputRoot = u32; fn normalized_sqrt_rem(self) -> (u32, u64) { // Use newton's method on 1/sqrt(n) // x_{i+1} = x_i * (3 - n*x_i^2) / 2 debug_assert!(self.leading_zeros() <= 1); // step1: lookup initial estimation of normalized 1/√n. The lookup table uses the highest 7 bits, // since the input is normalized, the lookup index must be larger than 2**(7-2) = 32. // then the retrieved r ≈ √32 / √(n >> 57) * 0x200 = 1 / √(n >> 62) / 2^9 = 2^40 / √n. let n32 = (self >> 32) as u32; let r = 0x100 | RSQRT_TAB[(n32 >> 25) as usize - 32] as u32; // 9 bits // step2: first Newton iteration (without dividing by 2) // afterwards, r ≈ 2^(40+22) / √n with 16 bits effective precision let r = ((3 * r) << 21) - wmul32_hi(n32, (r * r * r) << 5); // 31 bits // step3: second Newton iteration (without dividing by 2) // afterwards, r ≈ 2^(40+19) / √n with 32 bits effective precision let t = (3 << 28) - wmul32_hi(r, wmul32_hi(r, n32)); // 29 bits let r = wmul32_hi(r, t); // 28 bits // step4: √n = x * 1/√n let r = r << 4; // normalize to 32 bits, now r estimates 2^63 / √n let mut s = wmul32_hi(r, n32) << 1; s -= 10; // to make sure s is an underestimate // step5: third Newton iteration on √n let e = self - (s as u64) * (s as u64); s += wmul32_hi((e >> 32) as u32, r); // step6: fix the estimation error, at most 2 steps are needed // if we use more bits to estimate the initial guess, less steps can be required let e = fix_sqrt_error!(u64, self, s); (s, e) } fn normalized_cbrt_rem(self) -> (u32, u64) { // Use newton's method on 1/cbrt(n) // x_{i+1} = x_i * (4 - n*x_i^3) / 3 debug_assert!(self.leading_zeros() <= 2); // step1: lookup initial estimation of 1/∛x. The lookup table uses the highest 6 bits up to 63rd. // if the input has 64 bits, then shift it to 61 bits. // retrieved r ≈ ∛8 / ∛(n >> 57) * 0x200 = 1 / ∛(n >> 60) * 2^9 = 2^29 / ∛n. let adjust = self.leading_zeros() == 0; let n32 = (self >> (32 + 3 * adjust as u8)) as u32; let r = 0x100 | RCBRT_TAB[(n32 >> 25) as usize - 8] as u32; // 9 bits // step2: first Newton iteration // required shift = 29 * 3 - 32 * 2 = 23 // afterwards, r ≈ 2^(29+23) / ∛n = 2^52 / ∛n let t = (4 << 23) - wmul32_hi(n32, r * r * r); let r = r * (t / 3); // 32 bits // step3: second Newton iteration // required shift = 52 * 3 - 32 * 4 = 28 // afterwards, r ≈ 2^(52+28-32) / ∛n = 2^48 / ∛n let t = (4 << 28) - wmul32_hi(r, wmul32_hi(r, wmul32_hi(r, n32))); let mut r = wmul32_hi(r, t) / 3; // 28 bits r >>= adjust as u8; // recover the adjustment if needed // step4: ∛x = x * (1/∛x)^2 = x * (2^48/∛x)^2 / 2^(32*3) let r = r - 1; // to make sure c is an underestimate let mut c = wmul32_hi(r, wmul32_hi(r, (self >> 32) as u32)); // step5: fix the estimation error, at most 3 steps are needed // if we use more bits to estimate the initial guess, less steps can be required let e = fix_cbrt_error!(u64, self, c); (c, e) } } impl NormalizedRootRem for u128 { type OutputRoot = u64; fn normalized_sqrt_rem(self) -> (u64, u128) { debug_assert!(self.leading_zeros() <= 1); // use the "Karatsuba Square Root" algorithm // (see the implementation in dashu_int, or https://hal.inria.fr/inria-00072854/en/) // step1: calculate sqrt on high parts let (a, b) = (self >> u64::BITS, self & u64::MAX as u128); let (a, b) = (a as u64, b as u64); let (s1, r1) = a.normalized_sqrt_rem(); // step2: estimate the result with low parts // note that r1 <= 2*s1 < 2^(KBITS + 1) // here r0 = (r1*B + b) / 2 const KBITS: u32 = u64::BITS / 2; let r0 = r1 << (KBITS - 1) | b >> (KBITS + 1); let (mut q, mut u) = r0.div_rem(s1 as u64); if q >> KBITS > 0 { // if q >= B (then q = B), reduce the overestimate q -= 1; u += s1 as u64; } let mut s = (s1 as u64) << KBITS | q; let r = (u << (KBITS + 1)) | (b & ((1 << (KBITS + 1)) - 1)); let q2 = q * q; let mut c = (u >> (KBITS - 1)) as i8 - (r < q2) as i8; let mut r = r.wrapping_sub(q2); // step3: fix the estimation error if necessary if c < 0 { let (new_r, c1) = r.overflowing_add(s); s -= 1; let (new_r, c2) = new_r.overflowing_add(s); r = new_r; c += c1 as i8 + c2 as i8; } (s, (c as u128) << u64::BITS | r as u128) } fn normalized_cbrt_rem(self) -> (u64, u128) { debug_assert!(self.leading_zeros() <= 2); /* * the following algorithm is similar to the "Karatsuba Square Root" above: * assume n = a*B^3 + b2*B^2 + b1*B + b0, B=2^k, a has roughly 3k bits * 1. calculate cbrt on high part: * c1, r1 = cbrt_rem(a) * 2. estimate the root with low part * q, u = div_rem(r1*B + b2, 3*c1^2) * c = c1*B + q * r = u*B^2 + b1*B + b0 - 3*c1*q^2*B - q^3 * * 3. if a5 is normalized, then only few adjustments are needed * while r < 0 { * r += 3*c^2 - 3*c + 1 * c -= 1 * } */ // step1: calculate cbrt on high 62 bits let (c1, r1) = if self.leading_zeros() > 0 { // actually on high 65 bits let a = (self >> 63) as u64; let (mut c, _) = a.normalized_cbrt_rem(); c >>= 1; (c, (a >> 3) - (c as u64).pow(3)) } else { let a = (self >> 66) as u64; a.normalized_cbrt_rem() }; // step2: estimate the root with low part const KBITS: u32 = 22; let r0 = ((r1 as u128) << KBITS) | (self >> (2 * KBITS) & ((1 << KBITS) - 1)); let (q, u) = r0.div_rem(3 * (c1 as u128).pow(2)); let mut c = ((c1 as u64) << KBITS) + (q as u64); // here q might be larger than B // r = u*B^2 + b1*B + b0 - 3*c1*q^2*B - q^3 let t1 = (u << (2 * KBITS)) | (self & ((1 << (2 * KBITS)) - 1)); let t2 = (((3 * (c1 as u128)) << KBITS) + q) * q.pow(2); let mut r = t1 as i128 - t2 as i128; // step3: adjustment, finishes in at most 4 steps while r < 0 { r += 3 * (c as i128 - 1) * c as i128 + 1; c -= 1; } (c, r as u128) } } // The implementation for u8 is very naive, because it's rarely used impl SquareRootRem for u8 { type Output = u8; #[inline] fn sqrt_rem(&self) -> (u8, u8) { // brute-force search, because there are only 16 possibilites. let mut s = 0; let e = fix_sqrt_error!(u8, self, s); (s, e) } } impl CubicRootRem for u8 { type Output = u8; #[inline] fn cbrt_rem(&self) -> (u8, u8) { // brute-force search, because there are only 7 possibilites. let mut c = 0; let e = fix_cbrt_error!(u8, self, c); (c, e) } } macro_rules! impl_rootrem_using_normalized { ($t:ty, $half:ty) => { impl SquareRootRem for $t { type Output = $half; fn sqrt_rem(&self) -> ($half, $t) { if *self == 0 { return (0, 0); } // normalize the input and call the normalized subroutine let shift = self.leading_zeros() & !1; // make sure shift is divisible by 2 let (mut root, mut rem) = (self << shift).normalized_sqrt_rem(); if shift != 0 { root >>= shift / 2; rem = self - (root as $t).pow(2); } (root, rem) } } impl CubicRootRem for $t { type Output = $half; fn cbrt_rem(&self) -> ($half, $t) { if *self == 0 { return (0, 0); } // normalize the input and call the normalized subroutine let mut shift = self.leading_zeros(); shift -= shift % 3; // make sure shift is divisible by 3 let (mut root, mut rem) = (self << shift).normalized_cbrt_rem(); if shift != 0 { root >>= shift / 3; rem = self - (root as $t).pow(3); } (root, rem) } } }; } impl_rootrem_using_normalized!(u16, u8); impl_rootrem_using_normalized!(u32, u16); impl_rootrem_using_normalized!(u64, u32); impl_rootrem_using_normalized!(u128, u64); #[cfg(test)] mod tests { use super::*; use crate::math::{CubicRoot, SquareRoot}; use rand::random; #[test] fn test_sqrt() { assert_eq!(2u8.sqrt_rem(), (1, 1)); assert_eq!(2u16.sqrt_rem(), (1, 1)); assert_eq!(2u32.sqrt_rem(), (1, 1)); assert_eq!(2u64.sqrt_rem(), (1, 1)); assert_eq!(2u128.sqrt_rem(), (1, 1)); assert_eq!(u8::MAX.sqrt_rem(), (15, 30)); assert_eq!(u16::MAX.sqrt_rem(), (u8::MAX, (u8::MAX as u16) * 2)); assert_eq!(u32::MAX.sqrt_rem(), (u16::MAX, (u16::MAX as u32) * 2)); assert_eq!(u64::MAX.sqrt_rem(), (u32::MAX, (u32::MAX as u64) * 2)); assert_eq!(u128::MAX.sqrt_rem(), (u64::MAX, (u64::MAX as u128) * 2)); assert_eq!((u8::MAX / 2).sqrt_rem(), (11, 6)); assert_eq!((u16::MAX / 2).sqrt_rem(), (181, 6)); assert_eq!((u32::MAX / 2).sqrt_rem(), (46340, 88047)); assert_eq!((u64::MAX / 2).sqrt_rem(), (3037000499, 5928526806)); assert_eq!((u128::MAX / 2).sqrt_rem(), (13043817825332782212, 9119501915260492783)); // some cases from previous bugs assert_eq!(65533u32.sqrt_rem(), (255, 508)); macro_rules! random_case { ($T:ty) => { let n: $T = random(); let (root, rem) = n.sqrt_rem(); assert_eq!(root, n.sqrt()); assert!(rem <= (root as $T) * 2, "sqrt({}) remainder too large", n); assert_eq!(n, (root as $T).pow(2) + rem, "sqrt({}) != {}, {}", n, root, rem); }; } const N: u32 = 10000; for _ in 0..N { random_case!(u8); random_case!(u16); random_case!(u32); random_case!(u64); random_case!(u128); } } #[test] fn test_cbrt() { assert_eq!(2u8.cbrt_rem(), (1, 1)); assert_eq!(2u16.cbrt_rem(), (1, 1)); assert_eq!(2u32.cbrt_rem(), (1, 1)); assert_eq!(2u64.cbrt_rem(), (1, 1)); assert_eq!(2u128.cbrt_rem(), (1, 1)); assert_eq!((u8::MAX / 2).cbrt_rem(), (5, 2)); assert_eq!((u16::MAX / 2).cbrt_rem(), (31, 2976)); assert_eq!((u32::MAX / 2).cbrt_rem(), (1290, 794647)); assert_eq!((u64::MAX / 2).cbrt_rem(), (2097151, 13194133241856)); assert_eq!((u128::MAX / 2).cbrt_rem(), (5541191377756, 58550521324026917344808511)); assert_eq!((u8::MAX / 4).cbrt_rem(), (3, 36)); assert_eq!((u16::MAX / 4).cbrt_rem(), (25, 758)); assert_eq!((u32::MAX / 4).cbrt_rem(), (1023, 3142656)); assert_eq!((u64::MAX / 4).cbrt_rem(), (1664510, 5364995536903)); assert_eq!((u128::MAX / 4).cbrt_rem(), (4398046511103, 58028439341489006246363136)); macro_rules! random_case { ($T:ty) => { let n: $T = random(); let (root, rem) = n.cbrt_rem(); assert_eq!(root, n.cbrt()); let root = root as $T; assert!(rem <= 3 * (root * root + root), "cbrt({}) remainder too large", n); assert_eq!(n, root.pow(3) + rem, "cbrt({}) != {}, {}", n, root, rem); }; } const N: u32 = 10000; for _ in 0..N { random_case!(u16); random_case!(u32); random_case!(u64); random_case!(u128); } } } dashu-base-0.4.1/src/sign.rs000064400000000000000000000206550072674642500137770ustar 00000000000000//! Trait definitions for sign related operations. use core::{ cmp::Ordering, ops::{Mul, MulAssign, Neg}, }; /// Absolute value. /// /// # Examples /// ``` /// use dashu_base::Abs; /// assert_eq!((-5).abs(), 5); /// ``` pub trait Abs { type Output; fn abs(self) -> Self::Output; } /// Unsigned absolute value. /// /// # Examples /// ``` /// # use dashu_base::UnsignedAbs; /// assert_eq!((-5i8).unsigned_abs(), 5u8); /// ``` pub trait UnsignedAbs { type Output; fn unsigned_abs(self) -> Self::Output; } /// Check whether the magnitude of this number is equal the magnitude of the other number /// /// # Examples /// /// ``` /// # use dashu_base::AbsEq; /// assert!(5.abs_eq(&-5)); /// assert!(12.3.abs_eq(&-12.3)); /// ``` #[deprecated(since = "0.5.0", note = "AbsEq will be moved in AbsOrd in v0.5")] // TODO(v0.5): deprecate pub trait AbsEq { fn abs_eq(&self, rhs: &Rhs) -> bool; } /// Compare the magnitude of this number to the magnitude of the other number /// /// Note that this function will panic if either of the numbers is NaN. /// /// # Examples /// /// ``` /// # use dashu_base::AbsOrd; /// assert!(5.abs_cmp(&-6).is_le()); /// assert!(12.3.abs_cmp(&-12.3).is_eq()); /// ``` pub trait AbsOrd { fn abs_cmp(&self, rhs: &Rhs) -> Ordering; } /// This trait marks the number is signed. /// /// Notice that the negative zeros (of [f32] and [f64]) are still considered /// to have a positive sign. /// /// # Examples /// /// ``` /// # use dashu_base::{Signed, Sign}; /// assert_eq!((-2).sign(), Sign::Negative); /// assert_eq!((-2.4).sign(), Sign::Negative); /// assert_eq!((0.).sign(), Sign::Positive); /// /// assert!(2.is_positive()); /// assert!((-2.4).is_negative()); /// assert!((0.).is_positive()); /// ``` pub trait Signed { fn sign(&self) -> Sign; #[inline] fn is_positive(&self) -> bool { self.sign() == Sign::Positive } #[inline] fn is_negative(&self) -> bool { self.sign() == Sign::Negative } } macro_rules! impl_abs_ops_prim { ($($signed:ty;)*) => {$( // this branch is only for float impl Abs for $signed { type Output = $signed; #[inline] fn abs(self) -> Self::Output { if self.is_nan() || self >= 0. { self } else { -self } } } )*}; ($($signed:ty => $unsigned:ty;)*) => {$( impl Abs for $signed { type Output = $signed; #[inline] fn abs(self) -> Self::Output { <$signed>::abs(self) } } impl UnsignedAbs for $signed { type Output = $unsigned; #[inline] fn unsigned_abs(self) -> Self::Output { <$signed>::unsigned_abs(self) } } )*} } impl_abs_ops_prim!(i8 => u8; i16 => u16; i32 => u32; i64 => u64; i128 => u128; isize => usize;); impl_abs_ops_prim!(f32; f64;); /// An enum representing the sign of a number /// /// A sign can be converted to or from a boolean value, assuming `true` is [Negative]. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum Sign { Positive, Negative, } use Sign::*; impl From for Sign { /// Convert boolean value to [Sign], returns [Negative] for `true` #[inline] fn from(v: bool) -> Self { match v { true => Self::Negative, false => Self::Positive, } } } impl From for bool { /// Convert [Sign] to boolean value, returns `true` for [Negative] #[inline] fn from(v: Sign) -> Self { match v { Sign::Negative => true, Sign::Positive => false, } } } impl Neg for Sign { type Output = Sign; #[inline] fn neg(self) -> Sign { match self { Positive => Negative, Negative => Positive, } } } impl Mul for Sign { type Output = Sign; #[inline] fn mul(self, rhs: Sign) -> Sign { match (self, rhs) { (Positive, Positive) => Positive, (Positive, Negative) => Negative, (Negative, Positive) => Negative, (Negative, Negative) => Positive, } } } impl Mul for Sign { type Output = Ordering; #[inline] fn mul(self, rhs: Ordering) -> Self::Output { match self { Positive => rhs, Negative => rhs.reverse(), } } } impl Mul for Ordering { type Output = Ordering; #[inline] fn mul(self, rhs: Sign) -> Self::Output { match rhs { Positive => self, Negative => self.reverse(), } } } impl MulAssign for Sign { #[inline] fn mul_assign(&mut self, rhs: Sign) { *self = *self * rhs; } } impl PartialOrd for Sign { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Sign { #[inline] fn cmp(&self, other: &Self) -> Ordering { match (self, other) { (Positive, Negative) => Ordering::Greater, (Negative, Positive) => Ordering::Less, _ => Ordering::Equal, } } } macro_rules! impl_sign_ops_for_primitives { ($($t:ty)*) => {$( impl Mul<$t> for Sign { type Output = $t; #[inline] fn mul(self, rhs: $t) -> Self::Output { match self { Positive => rhs, Negative => -rhs } } } impl Mul for $t { type Output = $t; #[inline] fn mul(self, rhs: Sign) -> Self::Output { match rhs { Positive => self, Negative => -self } } } )*}; } impl_sign_ops_for_primitives!(i8 i16 i32 i64 i128 isize f32 f64); macro_rules! impl_signed_for_int { ($($t:ty)*) => {$( impl Signed for $t { #[inline] fn sign(&self) -> Sign { Sign::from(*self < 0) } } #[allow(deprecated)] impl AbsEq for $t { #[inline] fn abs_eq(&self, rhs: &Self) -> bool { self.abs() == rhs.abs() } } impl AbsOrd for $t { #[inline] fn abs_cmp(&self, rhs: &Self) -> Ordering { self.abs().cmp(&rhs.abs()) } } )*}; } impl_signed_for_int!(i8 i16 i32 i64 i128 isize); macro_rules! impl_signed_for_float { ($t:ty, $shift:literal) => { impl Signed for $t { #[inline] fn sign(&self) -> Sign { if self.is_nan() { panic!("nan doesn't have a sign") } else if *self == -0. { return Sign::Positive; } Sign::from(self.to_bits() >> $shift > 0) } } #[allow(deprecated)] impl AbsEq for $t { #[inline] fn abs_eq(&self, rhs: &Self) -> bool { self.abs() == rhs.abs() } } impl AbsOrd for $t { #[inline] fn abs_cmp(&self, rhs: &Self) -> Ordering { self.abs() .partial_cmp(&rhs.abs()) .expect("abs_cmp is not allowed on NaNs!") } } }; } impl_signed_for_float!(f32, 31); impl_signed_for_float!(f64, 63); #[cfg(test)] mod tests { use super::*; #[test] fn test_signed() { assert_eq!(0i32.sign(), Sign::Positive); assert_eq!(1i32.sign(), Sign::Positive); assert_eq!((-1i32).sign(), Sign::Negative); assert_eq!(0f32.sign(), Sign::Positive); assert_eq!((-0f32).sign(), Sign::Positive); assert_eq!(1f32.sign(), Sign::Positive); assert_eq!((-1f32).sign(), Sign::Negative); } #[test] #[should_panic] fn test_signed_nan() { let _ = f32::NAN.sign(); } #[test] #[should_panic] fn test_abs_cmp_nan() { let _ = f32::NAN.abs_cmp(&f32::NAN); } }