diff --git a/benches/power10.rs b/benches/power10.rs new file mode 100644 index 0000000..dddea87 --- /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::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, log10); +} + +#[bench] +fn benchl10_u32_up_to_10000(b: &mut Bencher) { + let v = first_10000_vec::(); + 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, log10); +} + +#[bench] +fn benchl10_u64_up_to_10000(b: &mut Bencher) { + let v = first_10000_vec::(); + bench_log10_slice(b, &v, 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 = ::one(); + 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..7fc3719 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, log10, checked_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..25dec67 --- /dev/null +++ b/src/power10.rs @@ -0,0 +1,787 @@ +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. + /// Panics if the input is zero. + /// + /// # Examples + /// + /// ~~~ + /// use num_integer::Power10; + /// assert_eq!(100u32.log10(), 2); + /// assert_eq!(4u32.log10(), 0); + /// ~~~ + 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(), Some(2)); + /// assert_eq!(4u32.checked_log10(), Some(0)); + /// assert_eq!(0u32.checked_log10(), None); + /// ~~~ + 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. + /// + /// # 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 log10(x: T) -> u32 { + x.log10() +} + +/// Returns the base 10 logarithm value, truncated down. +#[inline] +pub fn checked_log10(x: T) -> Option { + x.checked_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, + -1i64 as u64, + -1i64 as u64, + -1i64 as u64, + 1000000000000000000, + -1i64 as u64, + -1i64 as u64, + 100000000000000000, + -1i64 as u64, + -1i64 as u64, + 10000000000000000, + -1i64 as u64, + -1i64 as u64, + -1i64 as u64, + 1000000000000000, + -1i64 as u64, + -1i64 as u64, + 100000000000000, + -1i64 as u64, + -1i64 as u64, + 10000000000000, + -1i64 as u64, + -1i64 as u64, + -1i64 as u64, + 1000000000000, + -1i64 as u64, + -1i64 as u64, + 100000000000, + -1i64 as u64, + -1i64 as u64, + 10000000000, + -1i64 as u64, + -1i64 as u64, + -1i64 as u64, + 1000000000, + -1i64 as u64, + -1i64 as u64, + 100000000, + -1i64 as u64, + -1i64 as u64, + 10000000, + -1i64 as u64, + -1i64 as u64, + -1i64 as u64, + 1000000, + -1i64 as u64, + -1i64 as u64, + 100000, + -1i64 as u64, + -1i64 as u64, + 10000, + -1i64 as u64, + -1i64 as u64, + -1i64 as u64, + 1000, + -1i64 as u64, + -1i64 as u64, + 100, + -1i64 as u64, + -1i64 as u64, + 10, + -1i64 as u64, + -1i64 as u64, + 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 log10_u8(v: u8) -> u32 { + if v >= 100 { + return 2; + } + if v >= 10 { + return 1; + } + 0 +} + +#[inline] +fn log10_u16(v: u16) -> u32 { + log10_u32(v as u32) +} + +#[inline] +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 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 log10_usize(v: usize) -> u32 { + log10_u64(v as u64) +} + +#[cfg(target_pointer_width = "32")] +#[inline] +fn log10_usize(v: usize) -> u32 { + 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 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 checked_log10(&self) -> Option { + if 0 == *self { + return None; + } + Some($log_fn(*self)) + } + + #[inline] + fn log10(&self) -> u32 { + if 0 == *self { + panic!("undefined value for log of zero"); + } + $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, + log10_u8, + wrapping_next_power_of_ten_u8 +); //https://github.com/rust-lang/rust/issues/29599 +unsigned_power10!( + u16, + is_pow10_u16, + log10_u16, + wrapping_next_power_of_ten_u16 +); +unsigned_power10!( + u32, + is_pow10_u32, + log10_u32, + wrapping_next_power_of_ten_u32 +); +unsigned_power10!( + u64, + is_pow10_u64, + log10_u64, + wrapping_next_power_of_ten_u64 +); +#[cfg(has_i128)] +unsigned_power10!( + u128, + is_pow10_u128, + log10_u128, + wrapping_next_power_of_ten_u128 +); +unsigned_power10!( + usize, + is_pow10_usize, + log10_usize, + wrapping_next_power_of_ten_usize +); diff --git a/tests/power10.rs b/tests/power10.rs new file mode 100644 index 0000000..1028a77 --- /dev/null +++ b/tests/power10.rs @@ -0,0 +1,227 @@ +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.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::log10; + use num_integer::checked_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] + #[should_panic] + fn test_log10_zero_debug() { + assert_eq!((0 as $T).log10(), 0); + } + + #[test] + #[should_panic] + fn test_fn_checked_log10_zero() { + 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); + let mut x: $T = 1; + let mut count: u32 = 0; + let end: $T = <$T>::max_value() / 10; + while x < end { + assert_eq!(log10(x), count); + if x > 1 { + assert_eq!((x - 1).log10(), count - 1); + } + assert_eq!((x + 1).log10(), count); + x *= 10; + count += 1; + } + 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); + if 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); + assert_eq!(<$T>::max_value().wrapping_next_power_of_ten(), 0); + assert_eq!( + (<$T>::max_value() - 1).wrapping_next_power_of_ten(), + 0 + ); + assert_eq!( + (<$T>::max_value() - 2).wrapping_next_power_of_ten(), + 0 + ); + let mut x: $T = 1; + let end: $T = <$T>::max_value() / 10; + while x < end { + assert_eq!(wrapping_next_power_of_ten(x), x); + if x > 1 { + assert_eq!((x - 1).wrapping_next_power_of_ten(), x); + } + assert_eq!((x + 1).wrapping_next_power_of_ten(), x * 10); + x *= 10; + } + 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); + 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 { + pow10 = 0; + } else { + pow10 *= 10; + } + } + } + } + + #[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() { + 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)); + assert_eq!(<$T>::max_value().checked_next_power_of_ten(), None); + assert_eq!( + (<$T>::max_value() - 1).checked_next_power_of_ten(), + None + ); + assert_eq!( + (<$T>::max_value() - 2).checked_next_power_of_ten(), + 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)); + if x > 1 { + assert_eq!((x - 1).checked_next_power_of_ten(), Some(x)); + } + assert_eq!( + (x + 1).checked_next_power_of_ten(), + Some(x * 10) + ); + x *= 10; + } + 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); + 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 { + 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 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!(""); +//}