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 core::cmp::Ordering;
71
72 use dashu_base::EstimatedLog2;
73
74 use crate::{
75 arch::word::{DoubleWord, Word},
76 buffer::Buffer,
77 cmp::cmp_in_place,
78 div,
79 error::panic_invalid_log_oprand,
80 helper_macros::debug_assert_zero,
81 math::max_exp_in_word,
82 mul, mul_ops, pow,
83 primitive::{extend_word, highest_dword, shrink_dword, split_dword, WORD_BITS_USIZE},
84 radix,
85 repr::{
86 Repr,
87 TypedReprRef::{self, *},
88 },
89 };
90
91 impl TypedReprRef<'_> {
92 pub fn log(self, base: TypedReprRef<'_>) -> (usize, Repr) {
94 if let RefSmall(dw) = base {
96 match dw {
97 0 | 1 => panic_invalid_log_oprand(),
98 2 => {
99 return (
100 self.bit_len() - 1,
101 Repr::zero().into_typed().set_bit(self.bit_len()),
102 )
103 }
104 b if b.is_power_of_two() => {
105 let base_bits = b.trailing_zeros() as usize;
106 let exp = (self.bit_len() - 1) / base_bits;
107 return (exp, Repr::zero().into_typed().set_bit(exp * base_bits));
108 }
109 _ => {}
110 }
111 }
112
113 match (self, base) {
114 (RefSmall(dword), RefSmall(base_dword)) => log_dword(dword, base_dword),
115 (RefSmall(_), RefLarge(_)) => (0, Repr::one()),
116 (RefLarge(words), RefSmall(base_dword)) => {
117 if let Some(base_word) = shrink_dword(base_dword) {
118 log_word_base(words, base_word)
119 } else {
120 let mut buffer: [Word; 2] = [0; 2];
121 let (lo, hi) = split_dword(base_dword);
122 buffer[0] = lo;
123 buffer[1] = hi;
124 log_large(words, &buffer)
125 }
126 }
127 (RefLarge(words), RefLarge(base_words)) => match cmp_in_place(words, base_words) {
128 Ordering::Less => (0, Repr::one()),
129 Ordering::Equal => (1, Repr::from_buffer(Buffer::from(words))),
130 Ordering::Greater => log_large(words, base_words),
131 },
132 }
133 }
134
135 pub fn log2_bounds(self) -> (f32, f32) {
136 match self {
137 RefSmall(dword) => dword.log2_bounds(),
138 RefLarge(words) => log2_bounds_large(words),
139 }
140 }
141 }
142
143 fn log_dword(target: DoubleWord, base: DoubleWord) -> (usize, Repr) {
144 debug_assert!(base > 1);
145
146 match target {
148 0 => panic_invalid_log_oprand(),
149 1 => return (0, Repr::one()),
150 i if i < base => return (0, Repr::one()),
151 i if i == base => return (1, Repr::from_dword(base)),
152 _ => {}
153 }
154
155 let log2_self = target.log2_bounds().0;
156 let log2_base = base.log2_bounds().1;
157
158 let mut est = (log2_self / log2_base) as u32; let mut est_pow = base.pow(est);
160 assert!(est_pow <= target);
161
162 while let Some(next_pow) = est_pow.checked_mul(base) {
163 let cmp = next_pow.cmp(&target);
164 if cmp.is_le() {
165 est_pow = next_pow;
166 est += 1;
167 }
168 if cmp.is_ge() {
169 break;
170 }
171 }
172 (est as usize, Repr::from_dword(est_pow))
173 }
174
175 pub(crate) fn log_word_base(target: &[Word], base: Word) -> (usize, Repr) {
176 let log2_self = log2_bounds_large(target).0;
177 let (wexp, wbase) = if base == 10 {
178 (radix::RADIX10_INFO.digits_per_word, radix::RADIX10_INFO.range_per_word)
180 } else {
181 max_exp_in_word(base)
182 };
183 let log2_wbase = wbase.log2_bounds().1;
184
185 let mut est = (log2_self * wexp as f32 / log2_wbase) as usize; let mut est_pow = if est == 1 {
187 Repr::from_word(base)
188 } else {
189 pow::repr::pow_word_base(base, est)
190 }
191 .into_buffer();
192 assert!(cmp_in_place(&est_pow, target).is_le());
193
194 while est_pow.len() < target.len() {
196 if est_pow.len() == target.len() - 1 {
197 let target_hi = highest_dword(target);
198 let next_hi = (extend_word(*est_pow.last().unwrap()) + 1) * extend_word(wbase); if next_hi > target_hi {
200 break;
201 }
202 }
203 let carry = mul::mul_word_in_place(&mut est_pow, wbase);
204 est_pow.push_resizing(carry);
205 est += wexp;
206 }
207
208 loop {
210 match cmp_in_place(&est_pow, target) {
211 Ordering::Less => {
212 let carry = mul::mul_word_in_place(&mut est_pow, base);
213 est_pow.push_resizing(carry);
214 est += 1;
215 }
216 Ordering::Equal => break,
217 Ordering::Greater => {
218 debug_assert_zero!(div::div_by_word_in_place(&mut est_pow, base));
220 est -= 1;
221 break;
222 }
223 }
224 }
225
226 (est, Repr::from_buffer(est_pow))
227 }
228
229 fn log_large(target: &[Word], base: &[Word]) -> (usize, Repr) {
230 debug_assert!(cmp_in_place(target, base).is_ge()); let log2_self = log2_bounds_large(target).0;
234 let log2_base = log2_bounds_large(base).1;
235 let mut est = (log2_self / log2_base) as usize; est = est.max(1); let mut est_pow = if est == 1 {
238 Repr::from_buffer(Buffer::from(base))
239 } else if base.len() == 2 {
240 let base_dword = highest_dword(base);
241 pow::repr::pow_dword_base(base_dword, est)
242 } else {
243 pow::repr::pow_large_base(base, est)
244 };
245 assert!(cmp_in_place(est_pow.as_slice(), target).is_le());
246
247 loop {
249 let next_pow = mul_ops::repr::mul_large(est_pow.as_slice(), base);
250 let cmp = cmp_in_place(next_pow.as_slice(), target);
251 if cmp.is_le() {
252 est_pow = next_pow;
253 est += 1;
254 }
255 if cmp.is_ge() {
256 break;
257 }
258 }
259 (est, est_pow)
260 }
261
262 #[inline]
263 fn log2_bounds_large(words: &[Word]) -> (f32, f32) {
264 let hi = highest_dword(words);
267 let rem_bits = (words.len() - 2) * WORD_BITS_USIZE;
268 let (hi_lb, hi_ub) = hi.log2_bounds();
269
270 const ADJUST: f32 = 2. * f32::EPSILON;
272 let est_lb = (hi_lb + rem_bits as f32) * (1. - ADJUST);
273 let est_ub = (hi_ub + rem_bits as f32) * (1. + ADJUST);
274 (est_lb, est_ub)
275 }
276}