1use crate::{ibig::IBig, ops::EstimatedLog2, ubig::UBig};
4
5impl UBig {
6 #[inline]
25 pub fn ilog(&self, base: &UBig) -> usize {
26 self.repr().log(base.repr()).0
27 }
28}
29
30impl EstimatedLog2 for UBig {
31 #[inline]
32 fn log2_bounds(&self) -> (f32, f32) {
33 self.repr().log2_bounds()
34 }
35}
36
37impl IBig {
38 #[inline]
57 pub fn ilog(&self, base: &UBig) -> usize {
58 self.as_sign_repr().1.log(base.repr()).0
59 }
60}
61
62impl EstimatedLog2 for IBig {
63 #[inline]
64 fn log2_bounds(&self) -> (f32, f32) {
65 self.as_sign_repr().1.log2_bounds()
66 }
67}
68
69pub(crate) mod repr {
70 use alloc::vec::Vec;
71 use core::cmp::Ordering;
72
73 use dashu_base::EstimatedLog2;
74
75 use crate::{
76 arch::word::{DoubleWord, Word},
77 buffer::Buffer,
78 cmp::cmp_in_place,
79 div,
80 error::panic_invalid_log_oprand,
81 helper_macros::debug_assert_zero,
82 math::{bit_len, max_exp_in_word},
83 memory::MemoryAllocation,
84 mul, mul_ops, pow,
85 primitive::{extend_word, highest_dword, shrink_dword, split_dword, WORD_BITS_USIZE},
86 radix,
87 repr::{
88 Repr,
89 TypedReprRef::{self, *},
90 },
91 shift,
92 };
93
94 impl TypedReprRef<'_> {
95 pub fn log(self, base: TypedReprRef<'_>) -> (usize, Repr) {
97 if let RefSmall(dw) = base {
99 match dw {
100 0 | 1 => panic_invalid_log_oprand(),
101 2 => {
102 return (
103 self.bit_len() - 1,
104 Repr::zero().into_typed().set_bit(self.bit_len()),
105 )
106 }
107 b if b.is_power_of_two() => {
108 let base_bits = b.trailing_zeros() as usize;
109 let exp = (self.bit_len() - 1) / base_bits;
110 return (exp, Repr::zero().into_typed().set_bit(exp * base_bits));
111 }
112 _ => {}
113 }
114 }
115
116 match (self, base) {
117 (RefSmall(dword), RefSmall(base_dword)) => log_dword(dword, base_dword),
118 (RefSmall(_), RefLarge(_)) => (0, Repr::one()),
119 (RefLarge(words), RefSmall(base_dword)) => {
120 if let Some(base_word) = shrink_dword(base_dword) {
121 log_word_base(words, base_word)
122 } else {
123 let mut buffer: [Word; 2] = [0; 2];
124 let (lo, hi) = split_dword(base_dword);
125 buffer[0] = lo;
126 buffer[1] = hi;
127 log_large(words, &buffer)
128 }
129 }
130 (RefLarge(words), RefLarge(base_words)) => match cmp_in_place(words, base_words) {
131 Ordering::Less => (0, Repr::one()),
132 Ordering::Equal => (1, Repr::from_buffer(Buffer::from(words))),
133 Ordering::Greater => log_large(words, base_words),
134 },
135 }
136 }
137
138 pub fn log2_bounds(self) -> (f32, f32) {
139 match self {
140 RefSmall(dword) => dword.log2_bounds(),
141 RefLarge(words) => log2_bounds_large(words),
142 }
143 }
144 }
145
146 fn log_dword(target: DoubleWord, base: DoubleWord) -> (usize, Repr) {
147 debug_assert!(base > 1);
148
149 match target {
151 0 => panic_invalid_log_oprand(),
152 1 => return (0, Repr::one()),
153 i if i < base => return (0, Repr::one()),
154 i if i == base => return (1, Repr::from_dword(base)),
155 _ => {}
156 }
157
158 let log2_self = target.log2_bounds().0;
159 let log2_base = base.log2_bounds().1;
160
161 let mut est = (log2_self / log2_base) as u32; let mut est_pow = base.pow(est);
163 assert!(est_pow <= target);
164
165 while let Some(next_pow) = est_pow.checked_mul(base) {
166 let cmp = next_pow.cmp(&target);
167 if cmp.is_le() {
168 est_pow = next_pow;
169 est += 1;
170 }
171 if cmp.is_ge() {
172 break;
173 }
174 }
175 (est as usize, Repr::from_dword(est_pow))
176 }
177
178 pub(crate) fn log_word_base(target: &[Word], base: Word) -> (usize, Repr) {
179 let log2_self = log2_bounds_large(target).0;
180 let (wexp, wbase) = if base == 10 {
181 (radix::RADIX10_INFO.digits_per_word, radix::RADIX10_INFO.range_per_word)
183 } else {
184 max_exp_in_word(base)
185 };
186 let log2_wbase = wbase.log2_bounds().1;
187
188 let mut est = (log2_self * wexp as f32 / log2_wbase) as usize; let mut est_pow = if est == 1 {
190 Repr::from_word(base)
191 } else {
192 pow::repr::pow_word_base(base, est)
193 }
194 .into_buffer();
195 assert!(cmp_in_place(&est_pow, target).is_le());
196
197 while est_pow.len() < target.len() {
199 if est_pow.len() == target.len() - 1 {
200 let target_hi = highest_dword(target);
201 let next_hi = (extend_word(*est_pow.last().unwrap()) + 1) * extend_word(wbase); if next_hi > target_hi {
203 break;
204 }
205 }
206 let carry = mul::mul_word_in_place(&mut est_pow, wbase);
207 est_pow.push_resizing(carry);
208 est += wexp;
209 }
210
211 loop {
213 match cmp_in_place(&est_pow, target) {
214 Ordering::Less => {
215 let carry = mul::mul_word_in_place(&mut est_pow, base);
216 est_pow.push_resizing(carry);
217 est += 1;
218 }
219 Ordering::Equal => break,
220 Ordering::Greater => {
221 debug_assert_zero!(div::div_by_word_in_place(&mut est_pow, base));
223 est -= 1;
224 break;
225 }
226 }
227 }
228
229 (est, Repr::from_buffer(est_pow))
230 }
231
232 fn log_large(target: &[Word], base: &[Word]) -> (usize, Repr) {
233 debug_assert!(cmp_in_place(target, base).is_ge()); let target_bits = target.len() * WORD_BITS_USIZE;
241 let base_bits = base.len() * WORD_BITS_USIZE;
242 let max_powers = bit_len(target_bits / base_bits) as usize + 2;
243 let mut powers: Vec<Repr> = Vec::with_capacity(max_powers);
244 powers.push(Repr::from_buffer(Buffer::from(base))); loop {
247 let prev = powers.last().unwrap();
248 if 2 * prev.len() - 1 > target.len() {
249 break;
250 }
251 let next = mul_ops::repr::square_large(prev.as_slice());
252 if cmp_in_place(next.as_slice(), target).is_gt() {
253 break;
254 }
255 powers.push(next);
256 }
257
258 let mut current = Buffer::from(target);
261 let mut est = 0usize;
262
263 for (i, p) in powers.iter().enumerate().rev() {
264 let p_words = p.as_slice();
265 if current.len() < p_words.len() {
266 continue;
267 }
268 if current.len() == p_words.len() && cmp_in_place(¤t, p_words).is_lt() {
269 continue;
270 }
271
272 let mut p_buf = Buffer::from(p_words);
274 let mut allocation = MemoryAllocation::new(crate::memory::add_layout(
275 crate::memory::array_layout::<Word>(current.len() + 1),
276 div::memory_requirement_exact(current.len(), p_buf.len()),
277 ));
278 let (shift, fast_div_top) = div::normalize(&mut p_buf);
279 let quo_carry = div::div_rem_unshifted_in_place(
280 &mut current,
281 &p_buf,
282 shift,
283 fast_div_top,
284 &mut allocation.memory(),
285 );
286 current.push_resizing(quo_carry);
287
288 let n = p_buf.len();
291 debug_assert_zero!(shift::shr_in_place(&mut current[..n], shift));
292
293 let quo_len = current.len() - n;
295 if quo_len == 0 {
296 current.truncate(0);
297 } else {
298 current.erase_front(n);
299 }
300
301 est += 1 << i;
302 }
303
304 drop(powers);
305
306 let est_pow = compute_power(base, est);
308
309 assert!(cmp_in_place(est_pow.as_slice(), target).is_le());
311 let next_pow = mul_ops::repr::mul_large(est_pow.as_slice(), base);
312 if cmp_in_place(next_pow.as_slice(), target).is_le() {
313 return (est + 1, next_pow);
314 }
315
316 (est, est_pow)
317 }
318
319 fn compute_power(base: &[Word], exp: usize) -> Repr {
320 if exp <= 1 {
321 Repr::from_buffer(Buffer::from(base))
322 } else if base.len() == 2 {
323 let base_dword = highest_dword(base);
324 pow::repr::pow_dword_base(base_dword, exp)
325 } else {
326 pow::repr::pow_large_base(base, exp)
327 }
328 }
329
330 #[inline]
331 fn log2_bounds_large(words: &[Word]) -> (f32, f32) {
332 let hi = highest_dword(words);
335 let rem_bits = (words.len() - 2) * WORD_BITS_USIZE;
336 let (hi_lb, hi_ub) = hi.log2_bounds();
337
338 const ADJUST: f32 = 2. * f32::EPSILON;
340 let est_lb = (hi_lb + rem_bits as f32) * (1. - ADJUST);
341 let est_ub = (hi_ub + rem_bits as f32) * (1. + ADJUST);
342 (est_lb, est_ub)
343 }
344}