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