feanor_math/algorithms/
int_bisect.rs1use crate::ring::*;
2use crate::integer::*;
3use crate::ordered::*;
4
5pub fn bisect_floor<R, F>(ZZ: R, left: El<R>, right: El<R>, mut func: F) -> El<R>
13 where R: RingStore, R::Type: IntegerRing, F: FnMut(&El<R>) -> El<R>
14{
15 assert!(ZZ.is_lt(&left, &right));
16 let mut l = left;
17 let mut r = right;
18 assert!(!ZZ.is_pos(&func(&l)));
19 assert!(ZZ.is_pos(&func(&r)));
20 loop {
21 let mut mid = ZZ.add_ref(&l, &r);
22 ZZ.euclidean_div_pow_2(&mut mid, 1);
23
24 if ZZ.eq_el(&mid, &l) || ZZ.eq_el(&mid, &r) {
25 return l;
26 } else if ZZ.is_pos(&func(&mid)) {
27 r = mid;
28 } else {
29 l = mid;
30 }
31 }
32}
33
34pub fn find_root_floor<R, F>(ZZ: R, approx: El<R>, mut func: F) -> El<R>
45 where R: RingStore, R::Type: IntegerRing, F: FnMut(&El<R>) -> El<R>
46{
47 let mut left = ZZ.clone_el(&approx);
48 let mut step = ZZ.one();
49 while ZZ.is_pos(&func(&left)) {
50 ZZ.sub_assign_ref(&mut left, &step);
51 ZZ.mul_pow_2(&mut step, 1);
52 }
53 step = ZZ.one();
54 let mut right = approx;
55 while !ZZ.is_pos(&func(&right)) {
56 ZZ.add_assign_ref(&mut right, &step);
57 ZZ.mul_pow_2(&mut step, 1);
58 }
59 return bisect_floor(ZZ, left, right, func);
60}
61
62pub fn root_floor<R>(ZZ: R, n: El<R>, root: usize) -> El<R>
93 where R: RingStore, R::Type: IntegerRing
94{
95 assert!(root > 0);
96 if root == 1 {
97 return n;
98 }
99 if ZZ.is_zero(&n) {
100 return ZZ.zero();
101 }
102 assert!(!ZZ.is_neg(&n));
103 let log2_ceil_n = ZZ.abs_log2_ceil(&n).unwrap();
104
105 return find_root_floor(
106 &ZZ,
107 ZZ.from_float_approx(ZZ.to_float_approx(&n).powf(1. / root as f64)).unwrap_or(ZZ.zero()),
108 |x| {
109 let x_pow_root_half = ZZ.pow(ZZ.clone_el(x), root / 2);
110 if ZZ.abs_log2_ceil(x).unwrap_or(0) >= 2 && (ZZ.abs_log2_ceil(&x_pow_root_half).unwrap() - 1) * 2 >= log2_ceil_n {
112 if ZZ.is_neg(x) {
113 return ZZ.negate(ZZ.clone_el(&n));
114 } else {
115 return ZZ.clone_el(&n);
116 }
117 } else {
118 let x_pow_root = if root % 2 == 0 {
119 ZZ.pow(x_pow_root_half, 2)
120 } else {
121 ZZ.mul_ref_snd(ZZ.pow(x_pow_root_half, 2), x)
122 };
123 return ZZ.sub_ref_snd(x_pow_root, &n);
124 }
125 }
126 );
127}
128
129#[cfg(test)]
130use crate::primitive_int::StaticRing;
131
132#[test]
133fn test_bisect_floor() {
134 assert_eq!(0, bisect_floor(&StaticRing::<i64>::RING, 0, 10, |x| if *x == 0 { 0 } else { 1 }));
135 assert_eq!(9, bisect_floor(&StaticRing::<i64>::RING, 0, 10, |x| if *x == 10 { 1 } else { 0 }));
136 assert_eq!(-15, bisect_floor(&StaticRing::<i64>::RING, -20, -10, |x| *x + 15));
137}
138
139#[test]
140fn test_root_floor() {
141 assert_eq!(4, root_floor(&StaticRing::<i64>::RING, 16, 2));
142 assert_eq!(3, root_floor(&StaticRing::<i64>::RING, 27, 3));
143 assert_eq!(4, root_floor(&StaticRing::<i64>::RING, 17, 2));
144 assert_eq!(3, root_floor(&StaticRing::<i64>::RING, 28, 3));
145 assert_eq!(4, root_floor(&StaticRing::<i64>::RING, 24, 2));
146 assert_eq!(3, root_floor(&StaticRing::<i64>::RING, 63, 3));
147 assert_eq!(5, root_floor(&StaticRing::<i64>::RING, StaticRing::<i64>::RING.pow(5, 25), 25));
148 assert_eq!(4, root_floor(&StaticRing::<i64>::RING, StaticRing::<i64>::RING.pow(5, 25), 26));
149 assert_eq!(4, root_floor(&StaticRing::<i64>::RING, StaticRing::<i64>::RING.pow(5, 25), 27));
150 assert_eq!(4, root_floor(&StaticRing::<i64>::RING, StaticRing::<i64>::RING.pow(5, 25), 28));
151}