qfall_math/integer/z/arithmetic/
logarithm.rs1use crate::{error::MathError, integer::Z, rational::Q};
12use flint_sys::fmpz::{fmpz_clog, fmpz_dlog, fmpz_flog};
13
14impl Z {
15 pub fn log_ceil(&self, base: impl Into<Z>) -> Result<Z, MathError> {
43 let base: Z = base.into();
44 if base <= Z::ONE {
45 Err(MathError::InvalidIntegerInput(format!(
46 "The base must be greater than 1, but the provided is {base}"
47 )))
48 } else if self <= &Z::ZERO {
49 Err(MathError::NonPositive(self.to_string()))
50 } else {
51 Ok(Z::from(unsafe { fmpz_clog(&self.value, &base.value) }))
52 }
53 }
54
55 pub fn log_floor(&self, base: impl Into<Z>) -> Result<Z, MathError> {
83 let base: Z = base.into();
84 if base <= Z::ONE {
85 Err(MathError::InvalidIntegerInput(format!(
86 "The base must be greater than 1, but the provided is {base}"
87 )))
88 } else if self <= &Z::ZERO {
89 Err(MathError::NonPositive(self.to_string()))
90 } else {
91 Ok(Z::from(unsafe { fmpz_flog(&self.value, &base.value) }))
92 }
93 }
94
95 pub fn ln(&self) -> Result<Q, MathError> {
120 if self <= &Z::ZERO {
121 Err(MathError::NonPositive(self.to_string()))
122 } else {
123 Ok(Q::from(unsafe { fmpz_dlog(&self.value) }))
124 }
125 }
126
127 pub fn log(&self, base: impl Into<Z>) -> Result<Q, MathError> {
158 let base: Z = base.into();
159 if base <= Z::ONE {
160 return Err(MathError::InvalidIntegerInput(format!(
161 "The base must be greater than 1, but the provided is {base}"
162 )));
163 }
164
165 let ln_value = self.ln()?;
166 let ln_base = base.ln()?;
167 let log_b = ln_value / ln_base;
168
169 Ok(log_b)
170 }
171}
172
173#[cfg(test)]
174#[allow(clippy::needless_borrows_for_generic_args)]
175mod test_log_ceil {
176 use crate::integer::Z;
177
178 #[test]
180 fn base_too_small() {
181 let value = Z::from(17);
182
183 assert!(value.log_ceil(&Z::ZERO).is_err());
184 assert!(value.log_ceil(&Z::ONE).is_err());
185 assert!(value.log_ceil(&Z::MINUS_ONE).is_err());
186 assert!(value.log_ceil(&Z::from(i64::MIN)).is_err());
187 }
188
189 #[test]
191 fn value_too_small() {
192 let base = Z::from(2);
193
194 assert!(Z::ZERO.log_ceil(&base).is_err());
195 assert!(Z::MINUS_ONE.log_ceil(&base).is_err());
196 assert!(Z::from(i64::MIN).log_ceil(&base).is_err());
197 }
198
199 #[test]
201 fn rounded_up() {
202 let base = Z::from(2);
203
204 assert_eq!(Z::ZERO, Z::from(1).log_ceil(&base).unwrap());
205 assert_eq!(Z::ONE, Z::from(2).log_ceil(&base).unwrap());
206 assert_eq!(Z::from(2), Z::from(3).log_ceil(&base).unwrap());
207 assert_eq!(Z::from(2), Z::from(4).log_ceil(&base).unwrap());
208 assert_eq!(Z::from(64), Z::from(u64::MAX).log_ceil(&base).unwrap());
209 assert_eq!(Z::from(32), Z::from(u64::MAX).log_ceil(Z::from(4)).unwrap());
210 }
211
212 #[test]
215 fn availability() {
216 let value = Z::from(5);
217
218 let _ = value.log_ceil(2_u8).unwrap();
219 let _ = value.log_ceil(2_u16).unwrap();
220 let _ = value.log_ceil(2_u32).unwrap();
221 let _ = value.log_ceil(2_u64).unwrap();
222 let _ = value.log_ceil(2_i8).unwrap();
223 let _ = value.log_ceil(2_i16).unwrap();
224 let _ = value.log_ceil(2_i32).unwrap();
225 let _ = value.log_ceil(2_i64).unwrap();
226 let _ = value.log_ceil(&value).unwrap();
227 }
228}
229
230#[cfg(test)]
231mod test_log_floor {
232 use crate::integer::Z;
233
234 #[test]
236 fn base_too_small() {
237 let value = Z::from(17);
238
239 assert!(value.log_floor(&Z::ZERO).is_err());
240 assert!(value.log_floor(&Z::ONE).is_err());
241 assert!(value.log_floor(&Z::MINUS_ONE).is_err());
242 assert!(value.log_floor(Z::from(i64::MIN)).is_err());
243 }
244
245 #[test]
247 fn value_too_small() {
248 let base = Z::from(2);
249
250 assert!(Z::ZERO.log_floor(&base).is_err());
251 assert!(Z::MINUS_ONE.log_floor(&base).is_err());
252 assert!(Z::from(i64::MIN).log_floor(&base).is_err());
253 }
254
255 #[test]
257 fn rounded_down() {
258 let base = Z::from(2);
259
260 assert_eq!(Z::ZERO, Z::from(1).log_floor(&base).unwrap());
261 assert_eq!(Z::ONE, Z::from(2).log_floor(&base).unwrap());
262 assert_eq!(Z::ONE, Z::from(3).log_floor(&base).unwrap());
263 assert_eq!(Z::from(2), Z::from(4).log_floor(&base).unwrap());
264 assert_eq!(Z::from(63), Z::from(u64::MAX).log_floor(&base).unwrap());
265 assert_eq!(
266 Z::from(31),
267 Z::from(u64::MAX).log_floor(Z::from(4)).unwrap()
268 );
269 }
270
271 #[test]
274 fn availability() {
275 let value = Z::from(5);
276
277 let _ = value.log_floor(2_u8).unwrap();
278 let _ = value.log_floor(2_u16).unwrap();
279 let _ = value.log_floor(2_u32).unwrap();
280 let _ = value.log_floor(2_u64).unwrap();
281 let _ = value.log_floor(2_i8).unwrap();
282 let _ = value.log_floor(2_i16).unwrap();
283 let _ = value.log_floor(2_i32).unwrap();
284 let _ = value.log_floor(2_i64).unwrap();
285 let _ = value.log_floor(&value).unwrap();
286 }
287}
288
289#[cfg(test)]
290mod test_natural_ln {
291 use crate::{integer::Z, rational::Q};
292 use std::f64::consts::{LN_2, LN_10};
293
294 #[test]
296 fn value_too_small() {
297 assert!(Z::ZERO.ln().is_err());
298 assert!(Z::MINUS_ONE.ln().is_err());
299 assert!(Z::from(i64::MIN).ln().is_err());
300 }
301
302 #[test]
305 fn static_known_values() {
306 assert_eq!(Q::ZERO, Z::ONE.ln().unwrap());
307 assert_eq!(Q::from(LN_2), Z::from(2).ln().unwrap());
308 assert_eq!(Q::from(LN_10), Z::from(10).ln().unwrap());
309 }
310}
311
312#[cfg(test)]
313mod test_log {
314 use crate::{integer::Z, rational::Q, traits::Distance};
315
316 #[test]
318 fn base_too_small() {
319 let value = Z::from(17);
320
321 assert!(value.log(&Z::ZERO).is_err());
322 assert!(value.log(&Z::ONE).is_err());
323 assert!(value.log(&Z::MINUS_ONE).is_err());
324 assert!(value.log(Z::from(i64::MIN)).is_err());
325 }
326
327 #[test]
329 fn value_too_small() {
330 let base = Z::from(2);
331
332 assert!(Z::ZERO.log(&base).is_err());
333 assert!(Z::MINUS_ONE.log(&base).is_err());
334 assert!(Z::from(i64::MIN).log(&base).is_err());
335 }
336
337 #[test]
339 fn small_values() {
340 let z_0 = Z::from(1);
341 let z_1 = Z::from(2);
342 let z_2 = Z::from(6);
343 let z_3 = Z::from(9);
344 let cmp_0 = Q::from(6_f64.log2());
345 let cmp_1 = Q::from(2);
346 let max_distance = Q::from((1, 1_000_000_000));
347
348 let res_0 = z_0.log(2).unwrap();
349 let res_1 = z_1.log(2).unwrap();
350 let res_2 = z_2.log(2).unwrap();
351 let res_3 = z_3.log(3).unwrap();
352
353 assert_eq!(Q::ZERO, res_0);
354 assert_eq!(Q::ONE, res_1);
355 assert!(cmp_0.distance(res_2) < max_distance);
356 assert!(cmp_1.distance(res_3) < max_distance);
357 }
358
359 #[test]
361 fn large_values() {
362 let z_0 = Z::from(i64::MAX as u64 + 1);
363 let z_1 = Z::from(i64::MAX);
364 let z_2 = Z::from(i32::MAX);
365 let cmp_0 = Q::from(63);
366 let cmp_1 = Q::from((i64::MAX as f64).log2());
367 let max_distance = Q::from((1, 1_000_000_000));
368
369 let res_0 = z_0.log(2).unwrap();
370 let res_1 = z_1.log(2).unwrap();
371 let res_2 = z_2.log(i32::MAX).unwrap();
372
373 assert!(cmp_0.distance(res_0) < max_distance);
374 assert!(cmp_1.distance(res_1) < max_distance);
375 assert_eq!(Q::ONE, res_2);
376 }
377
378 #[test]
381 fn availability() {
382 let value = Z::from(5);
383
384 let _ = value.log(2_u8).unwrap();
385 let _ = value.log(2_u16).unwrap();
386 let _ = value.log(2_u32).unwrap();
387 let _ = value.log(2_u64).unwrap();
388 let _ = value.log(2_i8).unwrap();
389 let _ = value.log(2_i16).unwrap();
390 let _ = value.log(2_i32).unwrap();
391 let _ = value.log(2_i64).unwrap();
392 let _ = value.log(&value).unwrap();
393 }
394}