1#[derive(Clone, Copy, Debug)]
17pub struct Base(u32);
18
19impl Base {
20 pub const fn new(base: u32) -> Option<Base> {
21 if base >= 2 { Some(Base(base)) } else { None }
22 }
23
24 pub const fn get(self) -> u32 {
25 self.0
26 }
27}
28
29macro_rules! impl_int_part {
30 ($u:ident) => {
31 pub const fn $u(val: NonZero<$u>, base: Base) -> i32 {
32 const MAX_TABLE_SIZE: usize = ($u::BITS.ilog2() - 1) as usize;
33
34 let val = val.get();
35 let base = base.get();
36
37 let baseu = base as $u;
38 if baseu as u32 != base || val < baseu {
39 return 0;
40 }
41
42 let mut base_powers: [$u; MAX_TABLE_SIZE] = [0; MAX_TABLE_SIZE];
44
45 let mut i = 0;
46 let mut partial_log = 1u32;
47 let mut partial_val = baseu;
48
49 loop {
50 let square = match partial_val.checked_mul(partial_val) {
51 Some(s) if val >= s => s,
52 _ => break,
53 };
54 base_powers[i] = partial_val;
55 i += 1;
56 partial_log *= 2;
57 partial_val = square;
58 }
59 let mut dlog = partial_log;
60 while i > 0 {
61 i -= 1;
62 dlog /= 2;
63 if let Some(mid) = partial_val.checked_mul(base_powers[i]) {
64 if val >= mid {
65 partial_val = mid;
66 partial_log += dlog;
67 }
68 }
69 }
70 return partial_log as i32;
71 }
72 };
73}
74
75pub mod int_part {
76 use crate::log::Base;
77 use core::num::NonZero;
78
79 impl_int_part! { u8 }
80 impl_int_part! { u16 }
81 impl_int_part! { u32 }
82 impl_int_part! { u64 }
83 impl_int_part! { u128 }
84}
85
86macro_rules! impl_frac_part {
87 ($u:ident) => {
88 pub const fn $u(val: NonZero<$u>, base: Base) -> i32 {
89 const MAX_TABLE_SIZE: usize = ($u::BITS.ilog2() - 1) as usize;
90
91 let val = val.get();
92 let base = base.get();
93
94 let baseu = base as $u;
95 if baseu as u32 != base || val.checked_mul(baseu).is_none() {
96 return -1;
97 }
98
99 let mut base_powers: [$u; MAX_TABLE_SIZE] = [0; MAX_TABLE_SIZE];
101
102 let mut i = 0;
103 let mut partial_log = 1u32;
104 let mut partial_val = baseu;
105
106 loop {
107 let square = match partial_val.checked_mul(partial_val) {
108 Some(s) if val.checked_mul(s).is_some() => s,
109 _ => break,
110 };
111 base_powers[i] = partial_val;
112 i += 1;
113 partial_log *= 2;
114 partial_val = square;
115 }
116 let mut dlog = partial_log;
117 while i > 0 {
118 i -= 1;
119 dlog /= 2;
120 if let Some(mid) = partial_val.checked_mul(base_powers[i]) {
121 if val.checked_mul(mid).is_some() {
122 partial_val = mid;
123 partial_log += dlog;
124 }
125 }
126 }
127 return -1 - partial_log as i32;
128 }
129 };
130}
131
132pub mod frac_part {
133 use crate::log::Base;
134 use core::num::NonZero;
135
136 impl_frac_part! { u8 }
137 impl_frac_part! { u16 }
138 impl_frac_part! { u32 }
139 impl_frac_part! { u64 }
140 impl_frac_part! { u128 }
141}
142
143#[cfg(test)]
144mod tests {
145 use crate::log;
146 use crate::log::Base;
147 use core::num::NonZero;
148
149 #[test]
151 fn check_table_size_is_sufficient() {
152 let bin = Base::new(2).unwrap();
153
154 assert_eq!(log::int_part::u8(NonZero::<u8>::MAX, bin), 7);
155 assert_eq!(log::int_part::u16(NonZero::<u16>::MAX, bin), 15);
156 assert_eq!(log::int_part::u32(NonZero::<u32>::MAX, bin), 31);
157 assert_eq!(log::int_part::u64(NonZero::<u64>::MAX, bin), 63);
158 assert_eq!(log::int_part::u128(NonZero::<u128>::MAX, bin), 127);
159
160 assert_eq!(log::frac_part::u8(NonZero::<u8>::new(1).unwrap(), bin), -8);
161 assert_eq!(
162 log::frac_part::u16(NonZero::<u16>::new(1).unwrap(), bin),
163 -16
164 );
165 assert_eq!(
166 log::frac_part::u32(NonZero::<u32>::new(1).unwrap(), bin),
167 -32
168 );
169 assert_eq!(
170 log::frac_part::u64(NonZero::<u64>::new(1).unwrap(), bin),
171 -64
172 );
173 assert_eq!(
174 log::frac_part::u128(NonZero::<u128>::new(1).unwrap(), bin),
175 -128
176 );
177 }
178}