From a876ba38f4a42932d2e76be6e1c4eec3ed27480f Mon Sep 17 00:00:00 2001 From: Mohammad Rezaei Date: Thu, 15 Nov 2018 15:15:57 -0500 Subject: [PATCH 1/7] Implement Power10 trait --- benches/power10.rs | 539 ++++++++++++++++++++++++++++++++ src/lib.rs | 7 + src/power10.rs | 757 +++++++++++++++++++++++++++++++++++++++++++++ tests/power10.rs | 215 +++++++++++++ 4 files changed, 1518 insertions(+) create mode 100644 benches/power10.rs create mode 100644 src/power10.rs create mode 100644 tests/power10.rs diff --git a/benches/power10.rs b/benches/power10.rs new file mode 100644 index 0000000..7c89bb9 --- /dev/null +++ b/benches/power10.rs @@ -0,0 +1,539 @@ +#![feature(test)] + +extern crate num_integer; +extern crate num_traits; +extern crate test; + +use num_integer::floor_log10; +use num_integer::is_power_of_ten; +use num_integer::Power10; +use num_traits::One; +use num_traits::PrimInt; +use num_traits::Zero; +use test::{black_box, Bencher}; + +#[bench] +fn benchl10_u32_only_powers_of_ten(b: &mut Bencher) { + let v = powers_10_vec::(); + bench_log10_slice(b, &v, floor_log10); +} + +#[bench] +fn benchl10_u32_up_to_10000(b: &mut Bencher) { + let v = first_10000_vec::(); + bench_log10_slice(b, &v, floor_log10); +} + +#[bench] +fn benchl10_u64_only_powers_of_ten(b: &mut Bencher) { + let v = powers_10_vec::(); + bench_log10_slice(b, &v, floor_log10); +} + +#[bench] +fn benchl10_u64_up_to_10000(b: &mut Bencher) { + let v = first_10000_vec::(); + bench_log10_slice(b, &v, floor_log10); +} + +#[bench] +fn benchp10_u32_only_powers_of_ten(b: &mut Bencher) { + let v = powers_10_vec::(); + bench_pow10_slice(b, &v, is_power_of_ten); +} + +#[bench] +fn benchp10_u32_up_to_10000(b: &mut Bencher) { + let v = first_10000_vec::(); + bench_pow10_slice(b, &v, is_power_of_ten); +} + +#[bench] +fn benchp10_u64_only_powers_of_ten(b: &mut Bencher) { + let v = powers_10_vec::(); + bench_pow10_slice(b, &v, is_power_of_ten); +} + +#[bench] +fn benchp10_u64_up_to_10000(b: &mut Bencher) { + let v = first_10000_vec::(); + bench_pow10_slice(b, &v, is_power_of_ten); +} + +#[bench] +fn benchp10_u64_10000_random(b: &mut Bencher) { + let v = random_10000_vec_u64(); + bench_pow10_slice(b, &v, is_power_of_ten); +} + +#[bench] +fn benchp10_u64_only_powers_of_ten_simple(b: &mut Bencher) { + let v = powers_10_vec::(); + bench_pow10_slice(b, &v, is_pow_10_simple_u64); +} + +#[bench] +fn benchp10_u64_up_to_10000_simple(b: &mut Bencher) { + let v = first_10000_vec::(); + bench_pow10_slice(b, &v, is_pow_10_simple_u64); +} + +#[bench] +fn benchp10_u64_10000_random_simple(b: &mut Bencher) { + let v = random_10000_vec_u64(); + bench_pow10_slice(b, &v, is_pow_10_simple_u64); +} + +#[bench] +fn benchp10_u64_only_powers_of_ten_rev_search(b: &mut Bencher) { + let v = powers_10_vec::(); + bench_pow10_slice(b, &v, is_pow_10_rev_search_u64); +} + +#[bench] +fn benchp10_u64_up_to_10000_rev_search(b: &mut Bencher) { + let v = first_10000_vec::(); + bench_pow10_slice(b, &v, is_pow_10_rev_search_u64); +} + +#[bench] +fn benchp10_u64_10000_random_rev_search(b: &mut Bencher) { + let v = random_10000_vec_u64(); + bench_pow10_slice(b, &v, is_pow_10_rev_search_u64); +} + +#[bench] +fn benchp10_u64_only_powers_of_ten_lz(b: &mut Bencher) { + let v = powers_10_vec::(); + bench_pow10_slice(b, &v, is_pow_10_lz_u64); +} + +#[bench] +fn benchp10_u64_up_to_10000_lz(b: &mut Bencher) { + let v = first_10000_vec::(); + bench_pow10_slice(b, &v, is_pow_10_lz_u64); +} + +#[bench] +fn benchp10_u64_10000_random_lz(b: &mut Bencher) { + let v = random_10000_vec_u64(); + bench_pow10_slice(b, &v, is_pow_10_lz_u64); +} + +static POWER10_LZ_U64: [u64; 64] = [ + 10000000000000000000, + 0, + 0, + 0, + 1000000000000000000, + 0, + 0, + 100000000000000000, + 0, + 0, + 10000000000000000, + 0, + 0, + 0, + 1000000000000000, + 0, + 0, + 100000000000000, + 0, + 0, + 10000000000000, + 0, + 0, + 0, + 1000000000000, + 0, + 0, + 100000000000, + 0, + 0, + 10000000000, + 0, + 0, + 0, + 1000000000, + 0, + 0, + 100000000, + 0, + 0, + 10000000, + 0, + 0, + 0, + 1000000, + 0, + 0, + 100000, + 0, + 0, + 10000, + 0, + 0, + 0, + 1000, + 0, + 0, + 100, + 0, + 0, + 10, + 0, + 0, + 1, +]; + +#[inline] +fn is_pow_10_lz_u64(v: u64) -> bool { + v == POWER10_LZ_U64[(v.leading_zeros() & 63) as usize] +} + +#[inline] +fn is_pow_10_simple_u64(v: u64) -> bool { + v == 1 + || v == 10 + || v == 100 + || v == 1_000 + || v == 10_000 + || v == 100_000 + || v == 1_000_000 + || v == 10_000_000 + || v == 100_000_000 + || v == 1_000_000_000 + || v == 10_000_000_000 + || v == 100_000_000_000 + || v == 1_000_000_000_000 + || v == 10_000_000_000_000 + || v == 100_000_000_000_000 + || v == 1_000_000_000_000_000 + || v == 10_000_000_000_000_000 + || v == 100_000_000_000_000_000 + || v == 1_000_000_000_000_000_000u64 + || v == 10_000_000_000_000_000_000 +} + +#[inline] +fn is_pow_10_rev_search_u64(v: u64) -> bool { + if v >= 10_000_000_000_000_000_000 { + return v == 10_000_000_000_000_000_000; + } + if v >= 1_000_000_000_000_000_000 { + return v == 1_000_000_000_000_000_000; + } + if v >= 100_000_000_000_000_000 { + return v == 100_000_000_000_000_000; + } + if v >= 10_000_000_000_000_000 { + return v == 10_000_000_000_000_000; + } + if v >= 1_000_000_000_000_000 { + return v == 1_000_000_000_000_000; + } + if v >= 100_000_000_000_000 { + return v == 100_000_000_000_000; + } + if v >= 10_000_000_000_000 { + return v == 10_000_000_000_000; + } + if v >= 1_000_000_000_000 { + return v == 1_000_000_000_000; + } + if v >= 100_000_000_000 { + return v == 100_000_000_000; + } + if v >= 10_000_000_000 { + return v == 10_000_000_000; + } + if v >= 1_000_000_000 { + return v == 1_000_000_000; + } + if v >= 100_000_000 { + return v == 100_000_000; + } + if v >= 10_000_000 { + return v == 10_000_000; + } + if v >= 1_000_000 { + return v == 1_000_000; + } + if v >= 100_000 { + return v == 100_000; + } + if v >= 10_000 { + return v == 10_000; + } + if v >= 1_000 { + return v == 1_000; + } + if v >= 100 { + return v == 100; + } + if v >= 10 { + return v == 10; + } + if v >= 1 { + return v == 1; + } + false +} + +fn random_10000_vec_u64() -> Vec { + let mut v = Vec::new(); + let mut x: u64 = 0; + let mut rng = SplitMix { + v: 0xCAFE_1234_FEED_5678, + }; + while x < 10000 { + v.push(rng.next_u64()); + x += 1; + } + v +} + +#[bench] +fn benchp10_u16_only_powers_of_ten(b: &mut Bencher) { + let v = powers_10_vec::(); + bench_pow10_slice(b, &v, is_power_of_ten); +} + +#[bench] +fn benchp10_u16_up_to_10000(b: &mut Bencher) { + let v = first_10000_vec::(); + bench_pow10_slice(b, &v, is_power_of_ten); +} + +#[bench] +fn benchp10_u16_10000_random(b: &mut Bencher) { + let v = random_10000_vec_u16(); + bench_pow10_slice(b, &v, is_power_of_ten); +} + +#[bench] +fn benchp10_u16_only_powers_of_ten_simple(b: &mut Bencher) { + let v = powers_10_vec::(); + bench_pow10_slice(b, &v, is_pow_10_simple_u16); +} + +#[bench] +fn benchp10_u16_up_to_10000_simple(b: &mut Bencher) { + let v = first_10000_vec::(); + bench_pow10_slice(b, &v, is_pow_10_simple_u16); +} + +#[bench] +fn benchp10_u16_10000_random_simple(b: &mut Bencher) { + let v = random_10000_vec_u16(); + bench_pow10_slice(b, &v, is_pow_10_simple_u16); +} + +#[bench] +fn benchp10_u16_only_powers_of_ten_rev_search(b: &mut Bencher) { + let v = powers_10_vec::(); + bench_pow10_slice(b, &v, is_pow_10_rev_search_u16); +} + +#[bench] +fn benchp10_u16_up_to_10000_rev_search(b: &mut Bencher) { + let v = first_10000_vec::(); + bench_pow10_slice(b, &v, is_pow_10_rev_search_u16); +} + +#[bench] +fn benchp10_u16_10000_random_rev_search(b: &mut Bencher) { + let v = random_10000_vec_u16(); + bench_pow10_slice(b, &v, is_pow_10_rev_search_u16); +} + +#[inline] +fn is_pow_10_simple_u16(v: u16) -> bool { + v == 1 || v == 10 || v == 100 || v == 1_000 || v == 10_000 +} + +#[inline] +fn is_pow_10_rev_search_u16(v: u16) -> bool { + if v >= 10_000 { + return v == 10_000; + } + if v >= 1_000 { + return v == 1_000; + } + if v >= 100 { + return v == 100; + } + if v >= 10 { + return v == 10; + } + if v >= 1 { + return v == 1; + } + false +} + +fn random_10000_vec_u16() -> Vec { + let mut v = Vec::new(); + let mut x: u16 = 0; + let mut rng = SplitMix { + v: 0xCAFE_1234_FEED_5678, + }; + while x < 10000 { + v.push(rng.next_u16()); + x += 1; + } + v +} + +#[bench] +fn benchp10_u8_only_powers_of_ten(b: &mut Bencher) { + let v = powers_10_vec::(); + bench_pow10_slice(b, &v, is_power_of_ten); +} + +#[bench] +fn benchp10_u8_10000_random(b: &mut Bencher) { + let v = random_10000_vec_u8(); + bench_pow10_slice(b, &v, is_power_of_ten); +} + +#[bench] +fn benchp10_u8_only_powers_of_ten_simple(b: &mut Bencher) { + let v = powers_10_vec::(); + bench_pow10_slice(b, &v, is_pow_10_simple_u8); +} + +#[bench] +fn benchp10_u8_10000_random_simple(b: &mut Bencher) { + let v = random_10000_vec_u8(); + bench_pow10_slice(b, &v, is_pow_10_simple_u8); +} + +#[bench] +fn benchp10_u8_only_powers_of_ten_hash(b: &mut Bencher) { + let v = powers_10_vec::(); + bench_pow10_slice(b, &v, is_pow_10_hash_u8); +} + +#[bench] +fn benchp10_u8_10000_random_hash(b: &mut Bencher) { + let v = random_10000_vec_u8(); + bench_pow10_slice(b, &v, is_pow_10_hash_u8); +} + +#[inline] +fn is_pow_10_simple_u8(v: u8) -> bool { + v == 1 || v == 10 || v == 100 +} + +static POWER10_HASH_U8: [u32; 8] = [1, 1, 10, 0, 100, 0, 0, 0]; + +#[inline] +fn is_pow_10_hash_u8(v: u8) -> bool { + let u = v as u32; + u == POWER10_HASH_U8[(u & 7) as usize] +} + +fn random_10000_vec_u8() -> Vec { + let mut v = Vec::new(); + let mut x: u32 = 0; + let mut rng = SplitMix { + v: 0xCAFE_1234_FEED_5678, + }; + while x < 10000 { + v.push(rng.next_u8()); + x += 1; + } + v +} + +fn first_10000_vec() -> Vec { + let mut v = Vec::with_capacity(10000); + let mut x: T = ::zero(); + let one: T = ::one(); + let ten = (one << 3) + (one << 1); + let tenk = ten * ten * ten * ten; + while x < tenk { + v.push(x); + x = x + one; + } + v +} + +fn powers_10_vec() -> Vec { + let mut v = Vec::with_capacity(10000); + let mut count = 0; + let one = ::one(); + let mut x: T = one; + let ten = (one << 3) + (one << 1); + let end: T = ::max_value() / ten; + while count < 10000 { + v.push(x); + if x > end { + x = one; + } else { + x = x * ten; + } + count += 1; + } + v +} + +#[inline] +fn bench_pow10_slice(b: &mut Bencher, slice: &[T], func: F) +where + F: Fn(T) -> bool, +{ + b.iter(|| { + let mut sum = 0; + for i in slice.iter() { + if func(*i) { + sum += 1; + } + } + black_box(sum); + }); +} + +#[inline] +fn bench_log10_slice(b: &mut Bencher, slice: &[T], func: F) +where + F: Fn(T) -> u32, +{ + b.iter(|| { + let mut sum = 0; + for i in slice.iter() { + sum += func(*i); + } + black_box(sum); + }); +} + +struct SplitMix { + v: u64, +} + +impl SplitMix { + #[inline] + pub fn next_u8(&mut self) -> u8 { + self.next_u64() as u8 + } + + #[inline] + pub fn next_u16(&mut self) -> u16 { + self.next_u64() as u16 + } + + #[inline] + pub fn next_u64(&mut self) -> u64 { + let mut r = self.v; + r ^= r >> 26; + r = r.wrapping_mul(8238576523158062045u64); + r ^= r >> 35; + r = r.wrapping_mul(-6410243847380211633i64 as u64); + r ^= r >> 34; + self.v = r;; + self.v + } +} diff --git a/src/lib.rs b/src/lib.rs index 41e0139..6584ec6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,13 @@ mod roots; pub use roots::Roots; pub use roots::{sqrt, cbrt, nth_root}; +mod power10; +pub use power10::Power10; +pub use power10::{ + checked_next_power_of_ten, floor_log10, is_power_of_ten, next_power_of_ten, + wrapping_next_power_of_ten, +}; + pub trait Integer: Sized + Num + PartialOrd + Ord + Eq { /// Floored integer division. /// diff --git a/src/power10.rs b/src/power10.rs new file mode 100644 index 0000000..b1df063 --- /dev/null +++ b/src/power10.rs @@ -0,0 +1,757 @@ +use Integer; + +/// Provides methods to compute functions related to powers of 10. +pub trait Power10: Integer { + /// Returns `true` if the number is a power of 10. + /// + /// # Examples + /// + /// ~~~ + /// use num_integer::Power10; + /// assert_eq!(100u32.is_power_of_ten(), true); + /// assert_eq!(4u32.is_power_of_ten(), false); + /// ~~~ + fn is_power_of_ten(&self) -> bool; + + /// Returns the base 10 logarithm value, truncated down. + /// Returns zero for zero. + /// + /// # Examples + /// + /// ~~~ + /// use num_integer::Power10; + /// assert_eq!(100u32.floor_log10(), 2); + /// assert_eq!(4u32.floor_log10(), 0); + /// ~~~ + fn floor_log10(&self) -> u32; //note: u32 return type to allow BigInt types to implement. 10^(2^32) is 4 billion digits + + /// Returns a power of ten greater than or equal to the supplied value. + /// If the next power of ten is larger than `max_value()`, 0 is returned. + /// + /// # Examples + /// + /// ~~~ + /// use num_integer::Power10; + /// assert_eq!(100u32.wrapping_next_power_of_ten(), 100); + /// assert_eq!(4u32.wrapping_next_power_of_ten(), 10); + /// assert_eq!(20_000u16.wrapping_next_power_of_ten(), 0); + /// ~~~ + fn wrapping_next_power_of_ten(&self) -> Self; + + /// Returns a power of ten greater than or equal to the supplied value. + /// If the next power of ten is larger than `max_value()`, None is returned. + /// + /// # Examples + /// + /// ~~~ + /// use num_integer::Power10; + /// assert_eq!(100u32.checked_next_power_of_ten().unwrap(), 100); + /// assert_eq!(4u32.checked_next_power_of_ten().unwrap(), 10); + /// assert_eq!(20_000u16.checked_next_power_of_ten(), None); + /// ~~~ + fn checked_next_power_of_ten(&self) -> Option; + + /// Returns a power of ten greater than or equal to the supplied value. + /// If the next power of ten is larger than `max_value()`, 0 is returned in release mode. + /// If the next power of ten is larger than `max_value()`, the function will panic in debug mode. + /// + /// # Examples + /// + /// ~~~ + /// use num_integer::Power10; + /// assert_eq!(100u32.checked_next_power_of_ten().unwrap(), 100); + /// assert_eq!(4u32.checked_next_power_of_ten().unwrap(), 10); + /// assert_eq!(20_000u16.checked_next_power_of_ten(), None); + /// ~~~ + fn next_power_of_ten(&self) -> Self; +} + +/// Returns `true` if the number is a power of 10. +#[inline] +pub fn is_power_of_ten(x: T) -> bool { + x.is_power_of_ten() +} + +/// Returns the base 10 logarithm value, truncated down. +#[inline] +pub fn floor_log10(x: T) -> u32 { + x.floor_log10() +} + +/// Returns a power of ten greater than or equal to the supplied value. +/// If the next power of ten is larger than `max_value()`, 0 is returned. +#[inline] +pub fn wrapping_next_power_of_ten(x: T) -> T { + x.wrapping_next_power_of_ten() +} + +/// Returns a power of ten greater than or equal to the supplied value. +/// If the next power of ten is larger than `max_value()`, None is returned. +#[inline] +pub fn checked_next_power_of_ten(x: T) -> Option { + x.checked_next_power_of_ten() +} + +/// Returns a power of ten greater than or equal to the supplied value. +/// If the next power of ten is larger than `max_value()`, 0 is returned in release more +/// and panics in debug mode. +#[inline] +pub fn next_power_of_ten(x: T) -> T { + x.next_power_of_ten() +} + +// Implementation note: the is_power_of_ten algorithm for u16/u32 is based on a +// perfect hash setup with very simple hash functions. These hash functions only use 32-bit +// operations for portable-speed. +// This approach is slightly better than leading_zeros() (which is used for u64 and fast logarithms) +static POWER10_HASH_U16: [u32; 8] = [1, 10, 10000, 0, 100, 1000, 0, 0]; +static POWER10_HASH_U32: [u32; 16] = [ + 10000, 1, 10000000, 0, 100, 0, 100000, 100000000, 1000, 0, 10, 1000000000, 0, 1000000, 0, 0, +]; +static POWER10_LZ_U64: [u64; 65] = [ + 10000000000000000000, + ::max_value(), + ::max_value(), + ::max_value(), + 1000000000000000000, + ::max_value(), + ::max_value(), + 100000000000000000, + ::max_value(), + ::max_value(), + 10000000000000000, + ::max_value(), + ::max_value(), + ::max_value(), + 1000000000000000, + ::max_value(), + ::max_value(), + 100000000000000, + ::max_value(), + ::max_value(), + 10000000000000, + ::max_value(), + ::max_value(), + ::max_value(), + 1000000000000, + ::max_value(), + ::max_value(), + 100000000000, + ::max_value(), + ::max_value(), + 10000000000, + ::max_value(), + ::max_value(), + ::max_value(), + 1000000000, + ::max_value(), + ::max_value(), + 100000000, + ::max_value(), + ::max_value(), + 10000000, + ::max_value(), + ::max_value(), + ::max_value(), + 1000000, + ::max_value(), + ::max_value(), + 100000, + ::max_value(), + ::max_value(), + 10000, + ::max_value(), + ::max_value(), + ::max_value(), + 1000, + ::max_value(), + ::max_value(), + 100, + ::max_value(), + ::max_value(), + 10, + ::max_value(), + ::max_value(), + 1, + 1, +]; + +// implementation note: reverse search is a bit faster than hash lookup for u8 +#[inline] +fn is_pow10_u8(v: u8) -> bool { + if v >= 100 { + return v == 100; + } + if v >= 10 { + return v == 10; + } + v == 1 +} + +// implementation note: at least on x86-64, 32bit ops are far faster than 16 bit ones, even == +#[inline] +fn is_pow10_u16(v: u16) -> bool { + v as u32 == POWER10_HASH_U16[((v as u32 >> 3) & 7) as usize] +} + +#[inline] +fn is_pow10_u32(v: u32) -> bool { + let hash = v ^ (v >> 14); + v == POWER10_HASH_U32[(hash & 15) as usize] +} + +#[inline] +fn is_pow10_u64(v: u64) -> bool { + v == POWER10_LZ_U64[(v.leading_zeros() & 63) as usize] // & 63 may look redundant, but it prevents a range check +} + +#[cfg(target_pointer_width = "64")] +#[inline] +fn is_pow10_usize(v: usize) -> bool { + is_pow10_u64(v as u64) +} + +#[cfg(target_pointer_width = "32")] +#[inline] +fn is_pow10_usize(v: usize) -> bool { + is_pow10_u32(v as u32) +} + +static POWER10_DIGITS_U32: [(u32, u32); 34] = [ + (8, 1_000_000_000), + (8, 1_000_000_000), + (8, 1_000_000_000), + (8, 1000000000), + (8, 1000000000), + (7, 100000000), + (7, 100000000), + (7, 100000000), + (6, 10000000), + (6, 10000000), + (6, 10000000), + (6, 10000000), + (5, 1000000), + (5, 1000000), + (5, 1000000), + (4, 100000), + (4, 100000), + (4, 100000), + (3, 10000), + (3, 10000), + (3, 10000), + (3, 10000), + (2, 1000), + (2, 1000), + (2, 1000), + (1, 100), + (1, 100), + (1, 100), + (0, 10), + (0, 10), + (0, 10), + (0, 10), + (0, 1), + (0, 1), +]; + +static POWER10_DIGITS_U64: [(u32, u64); 66] = [ + (18, 10000000000000000000), + (18, 10000000000000000000), + (18, 10000000000000000000), + (18, 10000000000000000000), + (17, 1000000000000000000), + (17, 1000000000000000000), + (17, 1000000000000000000), + (16, 100000000000000000), + (16, 100000000000000000), + (16, 100000000000000000), + (15, 10000000000000000), + (15, 10000000000000000), + (15, 10000000000000000), + (15, 10000000000000000), + (14, 1000000000000000), + (14, 1000000000000000), + (14, 1000000000000000), + (13, 100000000000000), + (13, 100000000000000), + (13, 100000000000000), + (12, 10000000000000), + (12, 10000000000000), + (12, 10000000000000), + (12, 10000000000000), + (11, 1000000000000), + (11, 1000000000000), + (11, 1000000000000), + (10, 100000000000), + (10, 100000000000), + (10, 100000000000), + (9, 10000000000), + (9, 10000000000), + (9, 10000000000), + (9, 10000000000), + (8, 1000000000), + (8, 1000000000), + (8, 1000000000), + (7, 100000000), + (7, 100000000), + (7, 100000000), + (6, 10000000), + (6, 10000000), + (6, 10000000), + (6, 10000000), + (5, 1000000), + (5, 1000000), + (5, 1000000), + (4, 100000), + (4, 100000), + (4, 100000), + (3, 10000), + (3, 10000), + (3, 10000), + (3, 10000), + (2, 1000), + (2, 1000), + (2, 1000), + (1, 100), + (1, 100), + (1, 100), + (0, 10), + (0, 10), + (0, 10), + (0, 10), + (0, 1), + (0, 1), +]; + +#[inline] +fn floor_log10_u8(v: u8) -> u32 { + if v >= 100 { + return 2; + } + if v >= 10 { + return 1; + } + 0 +} + +#[inline] +fn floor_log10_u16(v: u16) -> u32 { + floor_log10_u32(v as u32) +} + +#[inline] +fn floor_log10_u32(v: u32) -> u32 { + let lz = v.leading_zeros(); + let (digits, pow10) = POWER10_DIGITS_U32[lz as usize]; + digits + ((v >= pow10) as u32) +} + +#[inline] +fn floor_log10_u64(v: u64) -> u32 { + let lz = v.leading_zeros(); + let (digits, pow10) = POWER10_DIGITS_U64[lz as usize]; + digits + ((v >= pow10) as u32) +} + +#[cfg(target_pointer_width = "64")] +#[inline] +fn floor_log10_usize(v: usize) -> u32 { + floor_log10_u64(v as u64) +} + +#[cfg(target_pointer_width = "32")] +#[inline] +fn floor_log10_usize(v: usize) -> u32 { + floor_log10_u32(v as u32) +} + +#[inline] +fn wrapping_next_power_of_ten_u8(v: u8) -> u8 { + if v > 100 { + return 0; + } + if v > 10 { + return 100; + } + if v > 1 { + return 10; + } + 1 +} + +#[inline] +fn wrapping_next_power_of_ten_u16(v: u16) -> u16 { + if v > 10000 { + return 0; + } + wrapping_next_power_of_ten_u32(v as u32) as u16 +} + +#[inline] +fn wrapping_next_power_of_ten_u32(v: u32) -> u32 { + if v > POWER10_DIGITS_U32[0].1 { + return 0; + } + let lz = v.leading_zeros(); + let (_, prev_pow10) = POWER10_DIGITS_U32[(lz + 1) as usize]; + if v == prev_pow10 { + prev_pow10 + } else if v > prev_pow10 { + POWER10_DIGITS_U32[(lz - 1) as usize].1 + } else { + POWER10_DIGITS_U32[lz as usize].1 + } +} + +#[inline] +fn wrapping_next_power_of_ten_u64(v: u64) -> u64 { + let lz = v.leading_zeros(); + if lz == 0 { + let max = POWER10_DIGITS_U64[0].1; + if v > max { + return 0; + } + return max; + } + let (_, prev_pow10) = POWER10_DIGITS_U64[(lz + 1) as usize]; + if v == prev_pow10 { + prev_pow10 + } else if v > prev_pow10 { + POWER10_DIGITS_U64[(lz - 1) as usize].1 + } else { + POWER10_DIGITS_U64[lz as usize].1 + } +} + +#[cfg(target_pointer_width = "64")] +#[inline] +fn wrapping_next_power_of_ten_usize(v: usize) -> usize { + wrapping_next_power_of_ten_u64(v as u64) as usize +} + +#[cfg(target_pointer_width = "32")] +#[inline] +fn wrapping_next_power_of_ten_usize(v: usize) -> usize { + wrapping_next_power_of_ten_u32(v as u32) as usize +} + +macro_rules! hide_u128 { + ($T:ty) => { + static POWER10_HASH_U128: [$T; 64] = [ + 100000000000000000000000000000000000, + 0, + 100000000000000000000000000000000, + 0, + 1000, + 0, + 0, + 0, + 0, + 1000000000000000000000000000000000000, + 1000000000, + 0, + 100000000000000, + 100, + 0, + 0, + 0, + 100000, + 0, + 0, + 10000000000000, + 100000000000, + 10000000000000000000, + 0, + 0, + 10000000000000000000000000000000000, + 100000000, + 0, + 1000000000000000000000000000000000, + 1000000000000, + 0, + 100000000000000000000000000000000000000, + 10000000000000000, + 100000000000000000000000000, + 0, + 10000000000000000000000000000000000000, + 1000000000000000000, + 1, + 10000000000000000000000000, + 1000000000000000000000000, + 100000000000000000000000000000, + 10000000, + 10000000000000000000000000000, + 0, + 1000000000000000000000000000, + 100000000000000000, + 10000, + 0, + 1000000, + 1000000000000000000000000000000, + 0, + 100000000000000000000, + 10, + 0, + 10000000000, + 10000000000000000000000, + 0, + 0, + 10000000000000000000000000000000, + 1000000000000000000000, + 0, + 100000000000000000000000, + 1000000000000000, + 0, + ]; + + #[inline] + pub fn is_pow10_u128(v: $T) -> bool { + let mut hash: u32 = v as u32 | (((v as u64) >> 32) as u32); + hash = hash.wrapping_mul(1249991743) >> 25; + v == POWER10_HASH_U128[(hash & 63) as usize] + } + + static POWER10_DIGITS_U128: [(u32, u128); 130] = [ + (37, 100000000000000000000000000000000000000), + (37, 100000000000000000000000000000000000000), + (37, 100000000000000000000000000000000000000), + (37, 100000000000000000000000000000000000000), + (37, 100000000000000000000000000000000000000), + (36, 10000000000000000000000000000000000000), + (36, 10000000000000000000000000000000000000), + (36, 10000000000000000000000000000000000000), + (35, 1000000000000000000000000000000000000), + (35, 1000000000000000000000000000000000000), + (35, 1000000000000000000000000000000000000), + (34, 100000000000000000000000000000000000), + (34, 100000000000000000000000000000000000), + (34, 100000000000000000000000000000000000), + (34, 100000000000000000000000000000000000), + (33, 10000000000000000000000000000000000), + (33, 10000000000000000000000000000000000), + (33, 10000000000000000000000000000000000), + (32, 1000000000000000000000000000000000), + (32, 1000000000000000000000000000000000), + (32, 1000000000000000000000000000000000), + (31, 100000000000000000000000000000000), + (31, 100000000000000000000000000000000), + (31, 100000000000000000000000000000000), + (31, 100000000000000000000000000000000), + (30, 10000000000000000000000000000000), + (30, 10000000000000000000000000000000), + (30, 10000000000000000000000000000000), + (29, 1000000000000000000000000000000), + (29, 1000000000000000000000000000000), + (29, 1000000000000000000000000000000), + (28, 100000000000000000000000000000), + (28, 100000000000000000000000000000), + (28, 100000000000000000000000000000), + (27, 10000000000000000000000000000), + (27, 10000000000000000000000000000), + (27, 10000000000000000000000000000), + (27, 10000000000000000000000000000), + (26, 1000000000000000000000000000), + (26, 1000000000000000000000000000), + (26, 1000000000000000000000000000), + (25, 100000000000000000000000000), + (25, 100000000000000000000000000), + (25, 100000000000000000000000000), + (24, 10000000000000000000000000), + (24, 10000000000000000000000000), + (24, 10000000000000000000000000), + (24, 10000000000000000000000000), + (23, 1000000000000000000000000), + (23, 1000000000000000000000000), + (23, 1000000000000000000000000), + (22, 100000000000000000000000), + (22, 100000000000000000000000), + (22, 100000000000000000000000), + (21, 10000000000000000000000), + (21, 10000000000000000000000), + (21, 10000000000000000000000), + (21, 10000000000000000000000), + (20, 1000000000000000000000), + (20, 1000000000000000000000), + (20, 1000000000000000000000), + (19, 100000000000000000000), + (19, 100000000000000000000), + (19, 100000000000000000000), + (18, 10000000000000000000), + (18, 10000000000000000000), + (18, 10000000000000000000), + (18, 10000000000000000000), + (17, 1000000000000000000), + (17, 1000000000000000000), + (17, 1000000000000000000), + (16, 100000000000000000), + (16, 100000000000000000), + (16, 100000000000000000), + (15, 10000000000000000), + (15, 10000000000000000), + (15, 10000000000000000), + (15, 10000000000000000), + (14, 1000000000000000), + (14, 1000000000000000), + (14, 1000000000000000), + (13, 100000000000000), + (13, 100000000000000), + (13, 100000000000000), + (12, 10000000000000), + (12, 10000000000000), + (12, 10000000000000), + (12, 10000000000000), + (11, 1000000000000), + (11, 1000000000000), + (11, 1000000000000), + (10, 100000000000), + (10, 100000000000), + (10, 100000000000), + (9, 10000000000), + (9, 10000000000), + (9, 10000000000), + (9, 10000000000), + (8, 1000000000), + (8, 1000000000), + (8, 1000000000), + (7, 100000000), + (7, 100000000), + (7, 100000000), + (6, 10000000), + (6, 10000000), + (6, 10000000), + (6, 10000000), + (5, 1000000), + (5, 1000000), + (5, 1000000), + (4, 100000), + (4, 100000), + (4, 100000), + (3, 10000), + (3, 10000), + (3, 10000), + (3, 10000), + (2, 1000), + (2, 1000), + (2, 1000), + (1, 100), + (1, 100), + (1, 100), + (0, 10), + (0, 10), + (0, 10), + (0, 10), + (0, 1), + (0, 1), + ]; + + #[inline] + fn floor_log10_u128(v: $T) -> u32 { + let lz = v.leading_zeros(); + let (digits, pow10) = POWER10_DIGITS_U128[lz as usize]; + digits + ((v >= pow10) as u32) + } + + #[inline] + fn wrapping_next_power_of_ten_u128(v: u128) -> u128 { + if v > POWER10_DIGITS_U128[0].1 { + return 0; + } + let lz = v.leading_zeros(); + let (_, prev_pow10) = POWER10_DIGITS_U128[(lz + 1) as usize]; + if v == prev_pow10 { + prev_pow10 + } else if v > prev_pow10 { + POWER10_DIGITS_U128[(lz - 1) as usize].1 + } else { + POWER10_DIGITS_U128[lz as usize].1 + } + } + }; +} + +#[cfg(has_i128)] +hide_u128!(u128); + +macro_rules! unsigned_power10 { + ($T:ty, $pow_fn: ident, $log_fn: ident, $wrap_next_fn: ident) => { + impl Power10 for $T { + #[inline] + fn is_power_of_ten(&self) -> bool { + $pow_fn(*self) + } + + #[inline] + fn floor_log10(&self) -> u32 { + $log_fn(*self) + } + + #[inline] + fn wrapping_next_power_of_ten(&self) -> $T { + $wrap_next_fn(*self) + } + + #[inline] + fn checked_next_power_of_ten(&self) -> Option<$T> { + let x = self.wrapping_next_power_of_ten(); + if x == 0 { + return None; + } + Some(x) + } + + #[cfg(debug_assertions)] + #[inline] + fn next_power_of_ten(&self) -> $T { + let x = self.wrapping_next_power_of_ten(); + if x == 0 { + panic!("overflow in next_power_of_ten for {}", *self); + } + x + } + + #[cfg(not(debug_assertions))] + #[inline] + fn next_power_of_ten(&self) -> $T { + self.wrapping_next_power_of_ten() + } + } + }; +} + +unsigned_power10!( + u8, + is_pow10_u8, + floor_log10_u8, + wrapping_next_power_of_ten_u8 +); //https://github.com/rust-lang/rust/issues/29599 +unsigned_power10!( + u16, + is_pow10_u16, + floor_log10_u16, + wrapping_next_power_of_ten_u16 +); +unsigned_power10!( + u32, + is_pow10_u32, + floor_log10_u32, + wrapping_next_power_of_ten_u32 +); +unsigned_power10!( + u64, + is_pow10_u64, + floor_log10_u64, + wrapping_next_power_of_ten_u64 +); +#[cfg(has_i128)] +unsigned_power10!( + u128, + is_pow10_u128, + floor_log10_u128, + wrapping_next_power_of_ten_u128 +); +unsigned_power10!( + usize, + is_pow10_usize, + floor_log10_usize, + wrapping_next_power_of_ten_usize +); diff --git a/tests/power10.rs b/tests/power10.rs new file mode 100644 index 0000000..7c7775c --- /dev/null +++ b/tests/power10.rs @@ -0,0 +1,215 @@ +extern crate num_integer; +extern crate num_traits; + +use num_integer::Power10; +use num_traits::ToPrimitive; + +fn log10_compare_with_f64(x: T) { + let expected = x.to_f64().unwrap().log10().floor() as u32; + assert_eq!(x.floor_log10(), expected, "{}", x.to_f64().unwrap()); +} + +macro_rules! unsigned_power10 { + ($T:ty, $I:ident) => { + mod $I { + use super::log10_compare_with_f64; + use num_integer::checked_next_power_of_ten; + use num_integer::floor_log10; + use num_integer::is_power_of_ten; + use num_integer::wrapping_next_power_of_ten; + use num_integer::Power10; + + #[test] + fn test_is_pow_10() { + assert_eq!((0 as $T).is_power_of_ten(), false); + assert_eq!(<$T>::max_value().is_power_of_ten(), false); + let mut x: $T = 1; + let end: $T = <$T>::max_value() / 10; + while x < end { + assert_eq!(is_power_of_ten(x), true); + assert_eq!((x - 1).is_power_of_ten(), false); + assert_eq!((x + 1).is_power_of_ten(), false); + x *= 10; + } + assert_eq!(x.is_power_of_ten(), true); + assert_eq!((x - 1).is_power_of_ten(), false); + assert_eq!((x + 1).is_power_of_ten(), false); + } + + #[test] + fn test_floor_log10() { + assert_eq!((0 as $T).floor_log10(), 0, "zero"); + log10_compare_with_f64(<$T>::max_value()); + log10_compare_with_f64(<$T>::max_value() - 1); + log10_compare_with_f64(<$T>::max_value() - 2); + let mut x: $T = 1; + let mut count: u32 = 0; + let end: $T = <$T>::max_value() / 10; + while x < end { + assert_eq!(floor_log10(x), count, "{}", x); + if x > 1 { + assert_eq!((x - 1).floor_log10(), count - 1, "{}", x - 1); + } + assert_eq!((x + 1).floor_log10(), count, "{}", x + 1); + x *= 10; + count += 1; + } + assert_eq!(x.floor_log10(), count, "{}", x); + assert_eq!((x - 1).floor_log10(), count - 1, "{}", (x - 1)); + assert_eq!((x + 1).floor_log10(), count, "{}", (x + 1)); + + //powers of 2 + x = 1; + while x != 0 { + log10_compare_with_f64(x); + log10_compare_with_f64(x + 1); + log10_compare_with_f64(x - 1); + x <<= 1; + } + } + + #[test] + fn test_wrap_next_power_10() { + assert_eq!((0 as $T).wrapping_next_power_of_ten(), 1, "zero"); + assert_eq!(<$T>::max_value().wrapping_next_power_of_ten(), 0, "max"); + assert_eq!( + (<$T>::max_value() - 1).wrapping_next_power_of_ten(), + 0, + "max - 1" + ); + assert_eq!( + (<$T>::max_value() - 2).wrapping_next_power_of_ten(), + 0, + "max - 2" + ); + let mut x: $T = 1; + let end: $T = <$T>::max_value() / 10; + while x < end { + assert_eq!(wrapping_next_power_of_ten(x), x, "{}", x); + if x > 1 { + assert_eq!((x - 1).wrapping_next_power_of_ten(), x, "{}", x); + } + assert_eq!((x + 1).wrapping_next_power_of_ten(), x * 10, "{}", x + 1); + x *= 10; + } + assert_eq!(x.wrapping_next_power_of_ten(), x, "{}", x); + assert_eq!((x - 1).wrapping_next_power_of_ten(), x, "{}", (x - 1)); + assert_eq!((x + 1).wrapping_next_power_of_ten(), 0, "{}", (x + 1)); + + //powers of 2 + x = 4; + let mut pow10 = 10; + while x != 0 { + assert_eq!(x.wrapping_next_power_of_ten(), pow10, "{}", x); + assert_eq!((x + 1).wrapping_next_power_of_ten(), pow10, "{}", x + 1); + assert_eq!((x - 1).wrapping_next_power_of_ten(), pow10, "{}", x - 1); + x <<= 1; + if x > pow10 { + if pow10 > end { + pow10 = 0; + } else { + pow10 *= 10; + } + } + } + } + + #[test] + #[should_panic] + fn test_next_power_10_panic1() { + assert_eq!(<$T>::max_value().next_power_of_ten(), 0, "max"); + } + + #[test] + #[should_panic] + fn test_next_power_10_panic2() { + assert_eq!((<$T>::max_value() - 1).next_power_of_ten(), 0, "max - 1"); + } + + #[test] + fn test_checked_next_power_10() { + assert_eq!((0 as $T).checked_next_power_of_ten(), Some(1), "zero"); + assert_eq!(<$T>::max_value().checked_next_power_of_ten(), None, "max"); + assert_eq!( + (<$T>::max_value() - 1).checked_next_power_of_ten(), + None, + "max - 1" + ); + assert_eq!( + (<$T>::max_value() - 2).checked_next_power_of_ten(), + None, + "max - 2" + ); + let mut x: $T = 1; + let end: $T = <$T>::max_value() / 10; + while x < end { + assert_eq!(checked_next_power_of_ten(x), Some(x), "{}", x); + if x > 1 { + assert_eq!((x - 1).checked_next_power_of_ten(), Some(x), "{}", x); + } + assert_eq!( + (x + 1).checked_next_power_of_ten(), + Some(x * 10), + "{}", + x + 1 + ); + x *= 10; + } + assert_eq!(x.checked_next_power_of_ten(), Some(x), "{}", x); + assert_eq!((x - 1).checked_next_power_of_ten(), Some(x), "{}", (x - 1)); + assert_eq!((x + 1).checked_next_power_of_ten(), None, "{}", (x + 1)); + + //powers of 2 + x = 4; + let mut pow10 = 10; + while x != 0 { + let c = if pow10 == 0 { None } else { Some(pow10) }; + assert_eq!(x.checked_next_power_of_ten(), c, "{}", x); + assert_eq!((x + 1).checked_next_power_of_ten(), c, "{}", x + 1); + assert_eq!((x - 1).checked_next_power_of_ten(), c, "{}", x - 1); + x <<= 1; + if x > pow10 { + if pow10 > end { + pow10 = 0; + } else { + pow10 *= 10; + } + } + } + } + } + }; +} + +unsigned_power10!(u8, u8); +unsigned_power10!(u16, u16); +unsigned_power10!(u32, u32); +unsigned_power10!(u64, u64); +#[cfg(has_i128)] +unsigned_power10!(u128, u128); +unsigned_power10!(usize, usize); + +// this function generates the table used for floor_log10 +// run with --release to avoid overflow +// it then has to be reversed (I used tac) +//#[test] +//fn print_digits() { +// println!("({}, {}), ", 0, 1); +// let mut x: u128 = 1; +// let mut count: u32 = 0; +// let mut pow10: u128 = 10; +// while x != 0 { +// print!("({}, {}), ", count, pow10); +// let digits = (x as f64).log10().floor() as u32; +// x <<= 1; +// if x != 0 { +// let next_digits = (x as f64).log10().floor() as u32; +// if next_digits > digits { +// count += 1; +// pow10 *= 10; +// println!(); +// } +// } +// } +// println!(""); +//} From 41b9f9acc219fbd4b62606fb7b4da4d40b645eaf Mon Sep 17 00:00:00 2001 From: Mohammad Rezaei Date: Mon, 19 Nov 2018 11:26:15 -0500 Subject: [PATCH 2/7] fix for older rust --- src/power10.rs | 88 +++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/power10.rs b/src/power10.rs index b1df063..9f876ea 100644 --- a/src/power10.rs +++ b/src/power10.rs @@ -110,68 +110,68 @@ static POWER10_HASH_U32: [u32; 16] = [ ]; static POWER10_LZ_U64: [u64; 65] = [ 10000000000000000000, - ::max_value(), - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, + -1i64 as u64, 1000000000000000000, - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, 100000000000000000, - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, 10000000000000000, - ::max_value(), - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, + -1i64 as u64, 1000000000000000, - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, 100000000000000, - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, 10000000000000, - ::max_value(), - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, + -1i64 as u64, 1000000000000, - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, 100000000000, - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, 10000000000, - ::max_value(), - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, + -1i64 as u64, 1000000000, - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, 100000000, - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, 10000000, - ::max_value(), - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, + -1i64 as u64, 1000000, - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, 100000, - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, 10000, - ::max_value(), - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, + -1i64 as u64, 1000, - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, 100, - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, 10, - ::max_value(), - ::max_value(), + -1i64 as u64, + -1i64 as u64, 1, 1, ]; From 8c1da4b5394828f338d4ffc24312d8358918fa90 Mon Sep 17 00:00:00 2001 From: Mohammad Rezaei Date: Mon, 19 Nov 2018 11:47:23 -0500 Subject: [PATCH 3/7] fix for rust 1.8.0 --- tests/power10.rs | 72 +++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/tests/power10.rs b/tests/power10.rs index 7c7775c..70748dd 100644 --- a/tests/power10.rs +++ b/tests/power10.rs @@ -6,7 +6,7 @@ use num_traits::ToPrimitive; fn log10_compare_with_f64(x: T) { let expected = x.to_f64().unwrap().log10().floor() as u32; - assert_eq!(x.floor_log10(), expected, "{}", x.to_f64().unwrap()); + assert_eq!(x.floor_log10(), expected); //, "{}", x.to_f64().unwrap() } macro_rules! unsigned_power10 { @@ -38,7 +38,7 @@ macro_rules! unsigned_power10 { #[test] fn test_floor_log10() { - assert_eq!((0 as $T).floor_log10(), 0, "zero"); + assert_eq!((0 as $T).floor_log10(), 0); log10_compare_with_f64(<$T>::max_value()); log10_compare_with_f64(<$T>::max_value() - 1); log10_compare_with_f64(<$T>::max_value() - 2); @@ -46,17 +46,17 @@ macro_rules! unsigned_power10 { let mut count: u32 = 0; let end: $T = <$T>::max_value() / 10; while x < end { - assert_eq!(floor_log10(x), count, "{}", x); + assert_eq!(floor_log10(x), count); if x > 1 { - assert_eq!((x - 1).floor_log10(), count - 1, "{}", x - 1); + assert_eq!((x - 1).floor_log10(), count - 1); } - assert_eq!((x + 1).floor_log10(), count, "{}", x + 1); + assert_eq!((x + 1).floor_log10(), count); x *= 10; count += 1; } - assert_eq!(x.floor_log10(), count, "{}", x); - assert_eq!((x - 1).floor_log10(), count - 1, "{}", (x - 1)); - assert_eq!((x + 1).floor_log10(), count, "{}", (x + 1)); + assert_eq!(x.floor_log10(), count); + assert_eq!((x - 1).floor_log10(), count - 1); + assert_eq!((x + 1).floor_log10(), count); //powers of 2 x = 1; @@ -70,8 +70,8 @@ macro_rules! unsigned_power10 { #[test] fn test_wrap_next_power_10() { - assert_eq!((0 as $T).wrapping_next_power_of_ten(), 1, "zero"); - assert_eq!(<$T>::max_value().wrapping_next_power_of_ten(), 0, "max"); + assert_eq!((0 as $T).wrapping_next_power_of_ten(), 1); + assert_eq!(<$T>::max_value().wrapping_next_power_of_ten(), 0); assert_eq!( (<$T>::max_value() - 1).wrapping_next_power_of_ten(), 0, @@ -85,24 +85,24 @@ macro_rules! unsigned_power10 { let mut x: $T = 1; let end: $T = <$T>::max_value() / 10; while x < end { - assert_eq!(wrapping_next_power_of_ten(x), x, "{}", x); + assert_eq!(wrapping_next_power_of_ten(x), x); if x > 1 { - assert_eq!((x - 1).wrapping_next_power_of_ten(), x, "{}", x); + assert_eq!((x - 1).wrapping_next_power_of_ten(), x); } - assert_eq!((x + 1).wrapping_next_power_of_ten(), x * 10, "{}", x + 1); + assert_eq!((x + 1).wrapping_next_power_of_ten(), x * 10); x *= 10; } - assert_eq!(x.wrapping_next_power_of_ten(), x, "{}", x); - assert_eq!((x - 1).wrapping_next_power_of_ten(), x, "{}", (x - 1)); - assert_eq!((x + 1).wrapping_next_power_of_ten(), 0, "{}", (x + 1)); + assert_eq!(x.wrapping_next_power_of_ten(), x); + assert_eq!((x - 1).wrapping_next_power_of_ten(), x); + assert_eq!((x + 1).wrapping_next_power_of_ten(), 0); //powers of 2 x = 4; let mut pow10 = 10; while x != 0 { - assert_eq!(x.wrapping_next_power_of_ten(), pow10, "{}", x); - assert_eq!((x + 1).wrapping_next_power_of_ten(), pow10, "{}", x + 1); - assert_eq!((x - 1).wrapping_next_power_of_ten(), pow10, "{}", x - 1); + assert_eq!(x.wrapping_next_power_of_ten(), pow10); + assert_eq!((x + 1).wrapping_next_power_of_ten(), pow10); + assert_eq!((x - 1).wrapping_next_power_of_ten(), pow10); x <<= 1; if x > pow10 { if pow10 > end { @@ -117,56 +117,52 @@ macro_rules! unsigned_power10 { #[test] #[should_panic] fn test_next_power_10_panic1() { - assert_eq!(<$T>::max_value().next_power_of_ten(), 0, "max"); + assert_eq!(<$T>::max_value().next_power_of_ten(), 0); } #[test] #[should_panic] fn test_next_power_10_panic2() { - assert_eq!((<$T>::max_value() - 1).next_power_of_ten(), 0, "max - 1"); + assert_eq!((<$T>::max_value() - 1).next_power_of_ten(), 0); } #[test] fn test_checked_next_power_10() { - assert_eq!((0 as $T).checked_next_power_of_ten(), Some(1), "zero"); - assert_eq!(<$T>::max_value().checked_next_power_of_ten(), None, "max"); + assert_eq!((0 as $T).checked_next_power_of_ten(), Some(1)); + assert_eq!(<$T>::max_value().checked_next_power_of_ten(), None); assert_eq!( (<$T>::max_value() - 1).checked_next_power_of_ten(), - None, - "max - 1" + None ); assert_eq!( (<$T>::max_value() - 2).checked_next_power_of_ten(), - None, - "max - 2" + None ); let mut x: $T = 1; let end: $T = <$T>::max_value() / 10; while x < end { - assert_eq!(checked_next_power_of_ten(x), Some(x), "{}", x); + assert_eq!(checked_next_power_of_ten(x), Some(x)); if x > 1 { - assert_eq!((x - 1).checked_next_power_of_ten(), Some(x), "{}", x); + assert_eq!((x - 1).checked_next_power_of_ten(), Some(x)); } assert_eq!( (x + 1).checked_next_power_of_ten(), - Some(x * 10), - "{}", - x + 1 + Some(x * 10) ); x *= 10; } - assert_eq!(x.checked_next_power_of_ten(), Some(x), "{}", x); - assert_eq!((x - 1).checked_next_power_of_ten(), Some(x), "{}", (x - 1)); - assert_eq!((x + 1).checked_next_power_of_ten(), None, "{}", (x + 1)); + assert_eq!(x.checked_next_power_of_ten(), Some(x)); + assert_eq!((x - 1).checked_next_power_of_ten(), Some(x)); + assert_eq!((x + 1).checked_next_power_of_ten(), None); //powers of 2 x = 4; let mut pow10 = 10; while x != 0 { let c = if pow10 == 0 { None } else { Some(pow10) }; - assert_eq!(x.checked_next_power_of_ten(), c, "{}", x); - assert_eq!((x + 1).checked_next_power_of_ten(), c, "{}", x + 1); - assert_eq!((x - 1).checked_next_power_of_ten(), c, "{}", x - 1); + assert_eq!(x.checked_next_power_of_ten(), c); + assert_eq!((x + 1).checked_next_power_of_ten(), c); + assert_eq!((x - 1).checked_next_power_of_ten(), c); x <<= 1; if x > pow10 { if pow10 > end { From c0c55194b11666282b86e15620592e91d2929ea7 Mon Sep 17 00:00:00 2001 From: Mohammad Rezaei Date: Mon, 19 Nov 2018 14:43:23 -0500 Subject: [PATCH 4/7] fix for rust 1.8.0 --- tests/power10.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/power10.rs b/tests/power10.rs index 70748dd..3e43581 100644 --- a/tests/power10.rs +++ b/tests/power10.rs @@ -74,13 +74,11 @@ macro_rules! unsigned_power10 { assert_eq!(<$T>::max_value().wrapping_next_power_of_ten(), 0); assert_eq!( (<$T>::max_value() - 1).wrapping_next_power_of_ten(), - 0, - "max - 1" + 0 ); assert_eq!( (<$T>::max_value() - 2).wrapping_next_power_of_ten(), - 0, - "max - 2" + 0 ); let mut x: $T = 1; let end: $T = <$T>::max_value() / 10; From f10417a826b77fb1db1eb25b80a50b8473209ea2 Mon Sep 17 00:00:00 2001 From: Mohammad Rezaei Date: Fri, 7 Dec 2018 15:36:54 -0500 Subject: [PATCH 5/7] renamed floor_log10 to log10, added checked/unchecked variants fixed cargo test --release --- benches/power10.rs | 10 ++--- src/lib.rs | 4 +- src/power10.rs | 103 +++++++++++++++++++++++++++++++++++---------- tests/power10.rs | 60 ++++++++++++++++++++------ 4 files changed, 135 insertions(+), 42 deletions(-) diff --git a/benches/power10.rs b/benches/power10.rs index 7c89bb9..ee2ea39 100644 --- a/benches/power10.rs +++ b/benches/power10.rs @@ -4,7 +4,7 @@ extern crate num_integer; extern crate num_traits; extern crate test; -use num_integer::floor_log10; +use num_integer::log10; use num_integer::is_power_of_ten; use num_integer::Power10; use num_traits::One; @@ -15,25 +15,25 @@ use test::{black_box, Bencher}; #[bench] fn benchl10_u32_only_powers_of_ten(b: &mut Bencher) { let v = powers_10_vec::(); - bench_log10_slice(b, &v, floor_log10); + bench_log10_slice(b, &v, log10); } #[bench] fn benchl10_u32_up_to_10000(b: &mut Bencher) { let v = first_10000_vec::(); - bench_log10_slice(b, &v, floor_log10); + bench_log10_slice(b, &v, log10); } #[bench] fn benchl10_u64_only_powers_of_ten(b: &mut Bencher) { let v = powers_10_vec::(); - bench_log10_slice(b, &v, floor_log10); + bench_log10_slice(b, &v, log10); } #[bench] fn benchl10_u64_up_to_10000(b: &mut Bencher) { let v = first_10000_vec::(); - bench_log10_slice(b, &v, floor_log10); + bench_log10_slice(b, &v, log10); } #[bench] diff --git a/src/lib.rs b/src/lib.rs index 6584ec6..34647d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,8 +33,8 @@ pub use roots::{sqrt, cbrt, nth_root}; mod power10; pub use power10::Power10; pub use power10::{ - checked_next_power_of_ten, floor_log10, is_power_of_ten, next_power_of_ten, - wrapping_next_power_of_ten, + checked_next_power_of_ten, log10, unchecked_log10, checked_log10, + is_power_of_ten, next_power_of_ten, wrapping_next_power_of_ten, }; pub trait Integer: Sized + Num + PartialOrd + Ord + Eq { diff --git a/src/power10.rs b/src/power10.rs index 9f876ea..bc57f4f 100644 --- a/src/power10.rs +++ b/src/power10.rs @@ -14,16 +14,41 @@ pub trait Power10: Integer { fn is_power_of_ten(&self) -> bool; /// Returns the base 10 logarithm value, truncated down. - /// Returns zero for zero. + /// Returns zero for zero in release mode, panics in debug mode. /// /// # Examples /// /// ~~~ /// use num_integer::Power10; - /// assert_eq!(100u32.floor_log10(), 2); - /// assert_eq!(4u32.floor_log10(), 0); + /// assert_eq!(100u32.log10(), 2); + /// assert_eq!(4u32.log10(), 0); /// ~~~ - fn floor_log10(&self) -> u32; //note: u32 return type to allow BigInt types to implement. 10^(2^32) is 4 billion digits + fn log10(&self) -> u32; //note: u32 return type to allow BigInt types to implement. 10^(2^32) is 4 billion digits + + /// Returns the base 10 logarithm value, truncated down. + /// Panics for zero. + /// + /// # Examples + /// + /// ~~~ + /// use num_integer::Power10; + /// assert_eq!(100u32.checked_log10(), 2); + /// assert_eq!(4u32.checked_log10(), 0); + /// ~~~ + fn checked_log10(&self) -> u32; + + /// Returns the base 10 logarithm value, truncated down. + /// Returns zero for zero. (This matches f64::log10()::floor() for zero.) + /// + /// # Examples + /// + /// ~~~ + /// use num_integer::Power10; + /// assert_eq!(100u32.unchecked_log10(), 2); + /// assert_eq!(4u32.unchecked_log10(), 0); + /// assert_eq!(0u32.unchecked_log10(), 0); + /// ~~~ + fn unchecked_log10(&self) -> u32; /// Returns a power of ten greater than or equal to the supplied value. /// If the next power of ten is larger than `max_value()`, 0 is returned. @@ -74,8 +99,20 @@ pub fn is_power_of_ten(x: T) -> bool { /// Returns the base 10 logarithm value, truncated down. #[inline] -pub fn floor_log10(x: T) -> u32 { - x.floor_log10() +pub fn log10(x: T) -> u32 { + x.log10() +} + +/// Returns the base 10 logarithm value, truncated down. +#[inline] +pub fn checked_log10(x: T) -> u32 { + x.checked_log10() +} + +/// Returns the base 10 logarithm value, truncated down. +#[inline] +pub fn unchecked_log10(x: T) -> u32 { + x.unchecked_log10() } /// Returns a power of ten greater than or equal to the supplied value. @@ -324,7 +361,7 @@ static POWER10_DIGITS_U64: [(u32, u64); 66] = [ ]; #[inline] -fn floor_log10_u8(v: u8) -> u32 { +fn log10_u8(v: u8) -> u32 { if v >= 100 { return 2; } @@ -335,19 +372,19 @@ fn floor_log10_u8(v: u8) -> u32 { } #[inline] -fn floor_log10_u16(v: u16) -> u32 { - floor_log10_u32(v as u32) +fn log10_u16(v: u16) -> u32 { + log10_u32(v as u32) } #[inline] -fn floor_log10_u32(v: u32) -> u32 { +fn log10_u32(v: u32) -> u32 { let lz = v.leading_zeros(); let (digits, pow10) = POWER10_DIGITS_U32[lz as usize]; digits + ((v >= pow10) as u32) } #[inline] -fn floor_log10_u64(v: u64) -> u32 { +fn log10_u64(v: u64) -> u32 { let lz = v.leading_zeros(); let (digits, pow10) = POWER10_DIGITS_U64[lz as usize]; digits + ((v >= pow10) as u32) @@ -355,14 +392,14 @@ fn floor_log10_u64(v: u64) -> u32 { #[cfg(target_pointer_width = "64")] #[inline] -fn floor_log10_usize(v: usize) -> u32 { - floor_log10_u64(v as u64) +fn log10_usize(v: usize) -> u32 { + log10_u64(v as u64) } #[cfg(target_pointer_width = "32")] #[inline] -fn floor_log10_usize(v: usize) -> u32 { - floor_log10_u32(v as u32) +fn log10_usize(v: usize) -> u32 { + log10_u32(v as u32) } #[inline] @@ -645,7 +682,7 @@ macro_rules! hide_u128 { ]; #[inline] - fn floor_log10_u128(v: $T) -> u32 { + fn log10_u128(v: $T) -> u32 { let lz = v.leading_zeros(); let (digits, pow10) = POWER10_DIGITS_U128[lz as usize]; digits + ((v >= pow10) as u32) @@ -681,10 +718,30 @@ macro_rules! unsigned_power10 { } #[inline] - fn floor_log10(&self) -> u32 { + fn unchecked_log10(&self) -> u32 { $log_fn(*self) } + #[inline] + fn checked_log10(&self) -> u32 { + if 0 == *self { + panic!("undefined value for log of zero"); + } + $log_fn(*self) + } + + #[cfg(debug_assertions)] + #[inline] + fn log10(&self) -> u32 { + self.checked_log10() + } + + #[cfg(not(debug_assertions))] + #[inline] + fn log10(&self) -> u32 { + $log_fn(*self) + } + #[inline] fn wrapping_next_power_of_ten(&self) -> $T { $wrap_next_fn(*self) @@ -721,37 +778,37 @@ macro_rules! unsigned_power10 { unsigned_power10!( u8, is_pow10_u8, - floor_log10_u8, + log10_u8, wrapping_next_power_of_ten_u8 ); //https://github.com/rust-lang/rust/issues/29599 unsigned_power10!( u16, is_pow10_u16, - floor_log10_u16, + log10_u16, wrapping_next_power_of_ten_u16 ); unsigned_power10!( u32, is_pow10_u32, - floor_log10_u32, + log10_u32, wrapping_next_power_of_ten_u32 ); unsigned_power10!( u64, is_pow10_u64, - floor_log10_u64, + log10_u64, wrapping_next_power_of_ten_u64 ); #[cfg(has_i128)] unsigned_power10!( u128, is_pow10_u128, - floor_log10_u128, + log10_u128, wrapping_next_power_of_ten_u128 ); unsigned_power10!( usize, is_pow10_usize, - floor_log10_usize, + log10_usize, wrapping_next_power_of_ten_usize ); diff --git a/tests/power10.rs b/tests/power10.rs index 3e43581..d178c65 100644 --- a/tests/power10.rs +++ b/tests/power10.rs @@ -6,7 +6,7 @@ use num_traits::ToPrimitive; fn log10_compare_with_f64(x: T) { let expected = x.to_f64().unwrap().log10().floor() as u32; - assert_eq!(x.floor_log10(), expected); //, "{}", x.to_f64().unwrap() + assert_eq!(x.log10(), expected); //, "{}", x.to_f64().unwrap() } macro_rules! unsigned_power10 { @@ -14,7 +14,9 @@ macro_rules! unsigned_power10 { mod $I { use super::log10_compare_with_f64; use num_integer::checked_next_power_of_ten; - use num_integer::floor_log10; + use num_integer::log10; + use num_integer::checked_log10; + use num_integer::unchecked_log10; use num_integer::is_power_of_ten; use num_integer::wrapping_next_power_of_ten; use num_integer::Power10; @@ -36,9 +38,39 @@ macro_rules! unsigned_power10 { assert_eq!((x + 1).is_power_of_ten(), false); } + #[cfg(debug_assertions)] #[test] - fn test_floor_log10() { - assert_eq!((0 as $T).floor_log10(), 0); + #[should_panic] + fn test_log10_zero_debug() { + assert_eq!((0 as $T).log10(), 0); + } + + #[cfg(not(debug_assertions))] + #[test] + fn test_log10_zero_release() { + assert_eq!((0 as $T).log10(), 0); + } + + #[test] + fn test_unchecked_log10_zero() { + assert_eq!((0 as $T).unchecked_log10(), 0); + assert_eq!(unchecked_log10(0 as $T), 0); + } + + #[test] + #[should_panic] + fn test_checked_log10_zero() { + assert_eq!((0 as $T).checked_log10(), 0); + } + + #[test] + #[should_panic] + fn test_fn_checked_log10_zero() { + assert_eq!(checked_log10(0 as $T), 0); + } + + #[test] + fn test_log10() { log10_compare_with_f64(<$T>::max_value()); log10_compare_with_f64(<$T>::max_value() - 1); log10_compare_with_f64(<$T>::max_value() - 2); @@ -46,24 +78,26 @@ macro_rules! unsigned_power10 { let mut count: u32 = 0; let end: $T = <$T>::max_value() / 10; while x < end { - assert_eq!(floor_log10(x), count); + assert_eq!(log10(x), count); if x > 1 { - assert_eq!((x - 1).floor_log10(), count - 1); + assert_eq!((x - 1).log10(), count - 1); } - assert_eq!((x + 1).floor_log10(), count); + assert_eq!((x + 1).log10(), count); x *= 10; count += 1; } - assert_eq!(x.floor_log10(), count); - assert_eq!((x - 1).floor_log10(), count - 1); - assert_eq!((x + 1).floor_log10(), count); + assert_eq!(x.log10(), count); + assert_eq!((x - 1).log10(), count - 1); + assert_eq!((x + 1).log10(), count); //powers of 2 x = 1; while x != 0 { log10_compare_with_f64(x); log10_compare_with_f64(x + 1); - log10_compare_with_f64(x - 1); + if x > 1 { + log10_compare_with_f64(x - 1); + } x <<= 1; } } @@ -112,12 +146,14 @@ macro_rules! unsigned_power10 { } } + #[cfg(debug_assertions)] #[test] #[should_panic] fn test_next_power_10_panic1() { assert_eq!(<$T>::max_value().next_power_of_ten(), 0); } + #[cfg(debug_assertions)] #[test] #[should_panic] fn test_next_power_10_panic2() { @@ -183,7 +219,7 @@ unsigned_power10!(u64, u64); unsigned_power10!(u128, u128); unsigned_power10!(usize, usize); -// this function generates the table used for floor_log10 +// this function generates the table used for log10 // run with --release to avoid overflow // it then has to be reversed (I used tac) //#[test] From 617a87cd3996b4728e5abfcb6cb9bab971d38e2b Mon Sep 17 00:00:00 2001 From: Mohammad Rezaei Date: Fri, 14 Dec 2018 17:41:48 -0500 Subject: [PATCH 6/7] removed unchecked_log10, made checked_log10 return Option and log10 panic on zero --- src/lib.rs | 2 +- src/power10.rs | 51 ++++++++++++------------------------------------ tests/power10.rs | 24 +++-------------------- 3 files changed, 16 insertions(+), 61 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 34647d4..7fc3719 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,7 @@ pub use roots::{sqrt, cbrt, nth_root}; mod power10; pub use power10::Power10; pub use power10::{ - checked_next_power_of_ten, log10, unchecked_log10, checked_log10, + checked_next_power_of_ten, log10, checked_log10, is_power_of_ten, next_power_of_ten, wrapping_next_power_of_ten, }; diff --git a/src/power10.rs b/src/power10.rs index bc57f4f..25dec67 100644 --- a/src/power10.rs +++ b/src/power10.rs @@ -14,7 +14,7 @@ pub trait Power10: Integer { fn is_power_of_ten(&self) -> bool; /// Returns the base 10 logarithm value, truncated down. - /// Returns zero for zero in release mode, panics in debug mode. + /// Panics if the input is zero. /// /// # Examples /// @@ -32,23 +32,11 @@ pub trait Power10: Integer { /// /// ~~~ /// use num_integer::Power10; - /// assert_eq!(100u32.checked_log10(), 2); - /// assert_eq!(4u32.checked_log10(), 0); + /// assert_eq!(100u32.checked_log10(), Some(2)); + /// assert_eq!(4u32.checked_log10(), Some(0)); + /// assert_eq!(0u32.checked_log10(), None); /// ~~~ - fn checked_log10(&self) -> u32; - - /// Returns the base 10 logarithm value, truncated down. - /// Returns zero for zero. (This matches f64::log10()::floor() for zero.) - /// - /// # Examples - /// - /// ~~~ - /// use num_integer::Power10; - /// assert_eq!(100u32.unchecked_log10(), 2); - /// assert_eq!(4u32.unchecked_log10(), 0); - /// assert_eq!(0u32.unchecked_log10(), 0); - /// ~~~ - fn unchecked_log10(&self) -> u32; + fn checked_log10(&self) -> Option; /// Returns a power of ten greater than or equal to the supplied value. /// If the next power of ten is larger than `max_value()`, 0 is returned. @@ -105,16 +93,10 @@ pub fn log10(x: T) -> u32 { /// Returns the base 10 logarithm value, truncated down. #[inline] -pub fn checked_log10(x: T) -> u32 { +pub fn checked_log10(x: T) -> Option { x.checked_log10() } -/// Returns the base 10 logarithm value, truncated down. -#[inline] -pub fn unchecked_log10(x: T) -> u32 { - x.unchecked_log10() -} - /// Returns a power of ten greater than or equal to the supplied value. /// If the next power of ten is larger than `max_value()`, 0 is returned. #[inline] @@ -718,27 +700,18 @@ macro_rules! unsigned_power10 { } #[inline] - fn unchecked_log10(&self) -> u32 { - $log_fn(*self) - } - - #[inline] - fn checked_log10(&self) -> u32 { + fn checked_log10(&self) -> Option { if 0 == *self { - panic!("undefined value for log of zero"); + return None; } - $log_fn(*self) - } - - #[cfg(debug_assertions)] - #[inline] - fn log10(&self) -> u32 { - self.checked_log10() + Some($log_fn(*self)) } - #[cfg(not(debug_assertions))] #[inline] fn log10(&self) -> u32 { + if 0 == *self { + panic!("undefined value for log of zero"); + } $log_fn(*self) } diff --git a/tests/power10.rs b/tests/power10.rs index d178c65..1028a77 100644 --- a/tests/power10.rs +++ b/tests/power10.rs @@ -16,7 +16,6 @@ macro_rules! unsigned_power10 { use num_integer::checked_next_power_of_ten; use num_integer::log10; use num_integer::checked_log10; - use num_integer::unchecked_log10; use num_integer::is_power_of_ten; use num_integer::wrapping_next_power_of_ten; use num_integer::Power10; @@ -38,39 +37,22 @@ macro_rules! unsigned_power10 { assert_eq!((x + 1).is_power_of_ten(), false); } - #[cfg(debug_assertions)] #[test] #[should_panic] fn test_log10_zero_debug() { assert_eq!((0 as $T).log10(), 0); } - #[cfg(not(debug_assertions))] - #[test] - fn test_log10_zero_release() { - assert_eq!((0 as $T).log10(), 0); - } - - #[test] - fn test_unchecked_log10_zero() { - assert_eq!((0 as $T).unchecked_log10(), 0); - assert_eq!(unchecked_log10(0 as $T), 0); - } - - #[test] - #[should_panic] - fn test_checked_log10_zero() { - assert_eq!((0 as $T).checked_log10(), 0); - } - #[test] #[should_panic] fn test_fn_checked_log10_zero() { - assert_eq!(checked_log10(0 as $T), 0); + assert_eq!(log10(0 as $T), 0); } #[test] fn test_log10() { + assert_eq!((0 as $T).checked_log10(), None); + assert_eq!(checked_log10(1 as $T), Some(0)); log10_compare_with_f64(<$T>::max_value()); log10_compare_with_f64(<$T>::max_value() - 1); log10_compare_with_f64(<$T>::max_value() - 2); From 230ff4b98664a13a894e9bef22d1981182179670 Mon Sep 17 00:00:00 2001 From: Mohammad Rezaei Date: Fri, 14 Dec 2018 18:54:42 -0500 Subject: [PATCH 7/7] avoid zero in the benchmarks for log --- benches/power10.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benches/power10.rs b/benches/power10.rs index ee2ea39..dddea87 100644 --- a/benches/power10.rs +++ b/benches/power10.rs @@ -450,11 +450,11 @@ fn random_10000_vec_u8() -> Vec { fn first_10000_vec() -> Vec { let mut v = Vec::with_capacity(10000); - let mut x: T = ::zero(); + let mut x: T = ::one(); let one: T = ::one(); let ten = (one << 3) + (one << 1); let tenk = ten * ten * ten * ten; - while x < tenk { + while x <= tenk { v.push(x); x = x + one; }