1use crate::util;
2
3use num::{BigInt, BigRational, One, Signed, ToPrimitive, Zero};
4
5fn reduce_trig_arg(arg: BigRational) -> BigRational {
7 if arg < BigRational::new((-1).into(), 2.into()) || arg >= BigRational::new(3.into(), 2.into())
8 {
9 let floor = (arg.clone() + BigRational::one()).floor().to_integer();
10 let offset = floor & !BigInt::one();
12 return arg - BigRational::from_integer(offset);
13 }
14 arg
15}
16
17#[derive(Debug, Clone, PartialEq, PartialOrd)]
23pub enum Kind {
24 One,
25 Pi,
26 Sqrt,
28 Exp,
30 Ln,
32 Log,
34 SinPi,
36 TanPi,
38 Asin,
40 Atan,
42 Irrational,
43}
44
45#[derive(Debug, Clone, PartialEq)]
47pub struct Property {
48 pub kind: Kind,
49 pub arg: Option<BigRational>,
50}
51
52impl Property {
53 pub(crate) fn one() -> Self {
54 Self {
55 kind: Kind::One,
56 arg: None,
57 }
58 }
59
60 pub(crate) fn pi() -> Self {
61 Self {
62 kind: Kind::Pi,
63 arg: None,
64 }
65 }
66
67 pub(crate) fn irrational() -> Self {
68 Self {
69 kind: Kind::Irrational,
70 arg: None,
71 }
72 }
73
74 pub(crate) fn clone_raw_with_arg(&self, arg: BigRational) -> RawProperty {
75 RawProperty::new(self.kind.clone(), Some(arg))
76 }
77
78 pub(crate) fn clone_with_arg(&self, arg: BigRational) -> Option<Self> {
79 self.clone_raw_with_arg(arg).validate()
80 }
81
82 pub(crate) fn irreducible_sqrt(&self) -> bool {
84 match &self.arg {
85 Some(arg) => match self.kind {
86 Kind::Sqrt => {
87 const EXTRACT_SQUARE_MAX: u64 = 3179;
92 arg.numer().bits() <= 30
94 && arg.denom().bits() <= 30
95 && arg.numer().abs() < EXTRACT_SQUARE_MAX.into()
96 && arg.denom().abs() < EXTRACT_SQUARE_MAX.into()
97 }
98 _ => false,
99 },
100 None => true,
101 }
102 }
103
104 pub(crate) fn symbolic(&self) -> Option<String> {
106 fn pi_multiple(r: &BigRational) -> String {
107 if r.is_zero() {
108 "".to_string()
109 } else if r.is_one() {
110 "π".to_string()
111 } else {
112 format!("{}π", r)
113 }
114 }
115
116 match self.kind {
117 Kind::One => Some("".to_string()),
118 Kind::Pi => Some("π".to_string()),
119 Kind::Exp => match &self.arg {
120 None => None,
121 Some(r) if r.is_one() => Some("e".to_string()),
122 Some(r) => Some(format!("exp({})", r)),
123 },
124 Kind::Sqrt => match &self.arg {
125 None => None,
126 Some(r) if r.is_integer() => Some(format!("√{}", r)),
127 Some(r) => Some(format!("√({})", r)),
128 },
129 Kind::Ln => self.arg.as_ref().map(|r| format!("ln({})", r)),
130 Kind::Log => self.arg.as_ref().map(|r| format!("log({})", r)),
131 Kind::SinPi => self
132 .arg
133 .as_ref()
134 .map(|r| format!("sin({})", pi_multiple(r))),
135 Kind::TanPi => self
136 .arg
137 .as_ref()
138 .map(|r| format!("tan({})", pi_multiple(r))),
139 Kind::Asin => self.arg.as_ref().map(|r| format!("asin({})", r)),
140 Kind::Atan => self.arg.as_ref().map(|r| format!("atan({})", r)),
141 Kind::Irrational => None,
142 }
143 }
144
145 pub(crate) fn msb_bound(&self) -> Option<i32> {
148 match self.kind {
149 Kind::One => Some(0),
150 Kind::Pi => Some(1),
151 Kind::Sqrt => match &self.arg {
152 Some(r) => {
153 let bits = util::rational::estimate_whole_bits(r)?;
154 Some((bits >> 1) - 2)
155 }
156 None => None,
157 },
158 Kind::Ln | Kind::Log => match &self.arg {
159 Some(r) => {
160 if r >= &BigRational::from_integer(2.into()) {
161 Some(-2)
163 } else {
164 None
165 }
166 }
167 None => None,
168 },
169 Kind::Exp => match &self.arg {
170 Some(r) => {
171 let floor = r.floor().to_integer();
172 if floor.bits() <= 30 {
173 let floor = floor.to_i32().unwrap();
174 if floor < 0 {
175 Some((floor / 2 - 1) * 3)
177 } else {
178 Some(floor * 7 / 5)
180 }
181 } else if floor.is_positive() {
182 Some(100_000_000)
183 } else {
184 None
185 }
186 }
187 None => None,
188 },
189 Kind::SinPi | Kind::TanPi | Kind::Asin | Kind::Atan => match &self.arg {
190 Some(r) => {
191 if r >= &BigRational::new(1.into(), 1024.into()) {
192 Some(-11)
193 } else {
194 None
195 }
196 }
197 None => None,
198 },
199 Kind::Irrational => None,
200 }
201 }
202
203 pub(crate) fn is_non_zero(&self) -> bool {
208 match self.kind {
209 Kind::Exp => match &self.arg {
210 Some(r) => *r >= BigRational::from_integer((-10000).into()),
211 None => false,
212 },
213 _ => true,
214 }
215 }
216}
217
218pub struct RawProperty {
219 pub kind: Kind,
220 pub arg: Option<BigRational>,
221}
222
223impl RawProperty {
224 pub fn new(kind: Kind, arg: Option<BigRational>) -> Self {
225 RawProperty { kind, arg }
226 }
227
228 pub fn clone_with_arg(&self, arg: BigRational) -> Self {
229 Self::new(self.kind.clone(), Some(arg))
230 }
231
232 pub fn sqrt(arg: BigRational) -> Self {
233 Self::new(Kind::Sqrt, Some(arg))
234 }
235
236 pub fn exp(arg: BigRational) -> Self {
237 Self::new(Kind::Exp, Some(arg))
238 }
239
240 pub fn log(arg: BigRational) -> Self {
241 Self::new(Kind::Log, Some(arg))
242 }
243
244 pub fn ln(arg: BigRational) -> Self {
245 Self::new(Kind::Ln, Some(arg))
246 }
247
248 pub fn sin(arg: BigRational) -> Self {
249 Self::new(Kind::SinPi, Some(reduce_trig_arg(arg)))
250 }
251
252 pub fn tan(arg: BigRational) -> Self {
253 Self::new(Kind::TanPi, Some(reduce_trig_arg(arg)))
254 }
255
256 pub fn asin(arg: BigRational) -> Self {
257 Self::new(Kind::Asin, Some(arg))
258 }
259
260 pub fn atan(arg: BigRational) -> Self {
261 Self::new(Kind::Atan, Some(arg))
262 }
263
264 pub fn validate(&self) -> Option<Property> {
265 use Kind::*;
266
267 let validated = match self.kind {
268 One | Pi | Irrational => true,
269 _ => match &self.arg {
270 None => false,
271 Some(r) => match self.kind {
272 Sqrt => r.is_positive() && *r != BigRational::one(),
273 Exp => !r.is_zero(),
274 Log => {
275 *r > BigRational::one()
276 && (!r.is_integer()
277 || util::int::exact_log(&r.to_integer(), 10).is_none())
278 }
279 Ln => *r > BigRational::one(),
280 SinPi | TanPi => {
281 r.is_positive()
282 && r < &BigRational::new(1.into(), 2.into())
283 && r != &BigRational::new(1.into(), 3.into())
284 && r != &BigRational::new(1.into(), 4.into())
285 && r != &BigRational::new(1.into(), 6.into())
286 }
287 Asin => {
288 *r > -BigRational::one()
289 && *r < BigRational::one()
290 && *r != BigRational::new(1.into(), 2.into())
291 && *r != BigRational::new((-1).into(), 2.into())
292 && *r != BigRational::zero()
293 }
294 Atan => {
295 *r != BigRational::one()
296 && *r != -BigRational::one()
297 && *r != BigRational::zero()
298 }
299 _ => false,
300 },
301 },
302 };
303
304 if validated {
305 Some(Property {
306 kind: self.kind.clone(),
307 arg: self.arg.clone(),
308 })
309 } else {
310 None
311 }
312 }
313}