litex/rational_expression/
evaluate.rs1use crate::common::count_range_integer::{
2 count_closed_range_integer_endpoints, count_half_open_range_integer_endpoints,
3};
4use crate::prelude::*;
5use crate::rational_expression::evaluate_div::safe_div;
6
7impl Obj {
8 pub fn evaluate_to_normalized_decimal_number(&self) -> Option<Number> {
9 let result = match self {
10 Obj::Number(number) => Some(number.clone()),
11 Obj::Add(add) => {
12 let left_number = add.left.evaluate_to_normalized_decimal_number();
13 let right_number = add.right.evaluate_to_normalized_decimal_number();
14 if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
15 let a = &left_number.normalized_value;
16 let b = &right_number.normalized_value;
17 let sum = if normalized_decimal_str_is_non_negative(a)
18 && normalized_decimal_str_is_non_negative(b)
19 {
20 add_decimal_str_and_normalize(a, b)
22 } else {
23 add_signed_decimal_str(a, b)
24 };
25 Some(Number::new(sum))
26 } else {
27 None
28 }
29 }
30 Obj::Sub(sub) => {
31 let left_number = sub.left.evaluate_to_normalized_decimal_number();
32 let right_number = sub.right.evaluate_to_normalized_decimal_number();
33 if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
34 let a = &left_number.normalized_value;
35 let b = &right_number.normalized_value;
36 let diff = if normalized_decimal_str_is_non_negative(a)
37 && normalized_decimal_str_is_non_negative(b)
38 {
39 sub_decimal_str_and_normalize(a, b)
41 } else {
42 sub_signed_decimal_str(a, b)
43 };
44 Some(Number::new(diff))
45 } else {
46 None
47 }
48 }
49 Obj::Mul(mul) => {
50 let left_number = mul.left.evaluate_to_normalized_decimal_number();
51 let right_number = mul.right.evaluate_to_normalized_decimal_number();
52 if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
53 Some(Number::new(mul_signed_decimal_str(
54 &left_number.normalized_value,
55 &right_number.normalized_value,
56 )))
57 } else {
58 None
59 }
60 }
61 Obj::Mod(mod_obj) => {
62 let left_number = mod_obj.left.evaluate_to_normalized_decimal_number();
63 let right_number = mod_obj.right.evaluate_to_normalized_decimal_number();
64 if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
65 Some(Number::new(mod_decimal_str_and_normalize(
66 &left_number.normalized_value,
67 &right_number.normalized_value,
68 )))
69 } else {
70 None
71 }
72 }
73 Obj::Pow(pow_obj) => {
74 let base_number = pow_obj.base.evaluate_to_normalized_decimal_number();
75 let exponent_number = pow_obj.exponent.evaluate_to_normalized_decimal_number();
76 if let (Some(base_number), Some(exponent_number)) = (base_number, exponent_number) {
77 pow_decimal_str_and_normalize(
78 &base_number.normalized_value,
79 &exponent_number.normalized_value,
80 )
81 .map(Number::new)
82 } else {
83 None
84 }
85 }
86 Obj::Div(div) => {
87 let left_number = div.left.evaluate_to_normalized_decimal_number();
88 let right_number = div.right.evaluate_to_normalized_decimal_number();
89 if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
90 let exact_quotient_string = safe_div(
91 &left_number.normalized_value,
92 &right_number.normalized_value,
93 );
94
95 if let Some(exact_quotient_string) = exact_quotient_string {
96 Some(Number::new(exact_quotient_string))
97 } else {
98 None
99 }
100 } else {
101 return None;
102 }
103 }
104 Obj::Abs(a) => match a.arg.evaluate_to_normalized_decimal_number() {
105 Some(inner) => {
106 let s = inner.normalized_value.trim();
107 if let Some(rest) = s.strip_prefix('-') {
108 Some(Number::new(rest.trim().to_string()))
109 } else {
110 Some(inner)
111 }
112 }
113 None => None,
114 },
115 Obj::Max(m) => {
116 let left_number = m.left.evaluate_to_normalized_decimal_number();
117 let right_number = m.right.evaluate_to_normalized_decimal_number();
118 if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
119 let a = left_number.normalized_value.trim();
120 let b = right_number.normalized_value.trim();
121 let diff = sub_signed_decimal_str(a, b);
122 let d = diff.trim();
123 if d.starts_with('-') {
124 Some(right_number)
125 } else {
126 Some(left_number)
127 }
128 } else {
129 None
130 }
131 }
132 Obj::Min(m) => {
133 let left_number = m.left.evaluate_to_normalized_decimal_number();
134 let right_number = m.right.evaluate_to_normalized_decimal_number();
135 if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
136 let a = left_number.normalized_value.trim();
137 let b = right_number.normalized_value.trim();
138 let diff = sub_signed_decimal_str(a, b);
139 let d = diff.trim();
140 if d.starts_with('-') || d == "0" {
141 Some(left_number)
142 } else {
143 Some(right_number)
144 }
145 } else {
146 None
147 }
148 }
149 Obj::CartDim(cart_dim) => match &*cart_dim.set {
150 Obj::Cart(cart) => Some(Number::new(cart.args.len().to_string())),
151 _ => None,
152 },
153 Obj::TupleDim(tuple_dim) => match &*tuple_dim.arg {
154 Obj::Tuple(tuple) => Some(Number::new(tuple.args.len().to_string())),
155 _ => None,
156 },
157 Obj::Count(count) => match &*count.set {
158 Obj::ListSet(list_set) => Some(Number::new(list_set.list.len().to_string())),
159 Obj::ClosedRange(cr) => {
160 let a = cr.start.evaluate_to_normalized_decimal_number()?;
161 let b = cr.end.evaluate_to_normalized_decimal_number()?;
162 count_closed_range_integer_endpoints(&a, &b)
163 }
164 Obj::Range(r) => {
165 let a = r.start.evaluate_to_normalized_decimal_number()?;
166 let b = r.end.evaluate_to_normalized_decimal_number()?;
167 count_half_open_range_integer_endpoints(&a, &b)
168 }
169 Obj::Cart(cart) => {
171 let mut acc = "1".to_string();
172 for arg in cart.args.iter() {
173 let factor_count = Obj::Count(Count::new((**arg).clone()))
174 .evaluate_to_normalized_decimal_number()?;
175 acc = mul_signed_decimal_str(
176 acc.trim(),
177 factor_count.normalized_value.trim(),
178 );
179 }
180 Some(Number::new(acc))
181 }
182 _ => None,
183 },
184 _ => None,
185 };
186
187 match result {
188 Some(number) => Some(number),
189 None => None,
190 }
191 }
192
193 pub fn two_objs_can_be_calculated_and_equal_by_calculation(&self, other: &Obj) -> bool {
194 match (
195 self.evaluate_to_normalized_decimal_number(),
196 other.evaluate_to_normalized_decimal_number(),
197 ) {
198 (Some(left_number), Some(right_number)) => {
199 return left_number.normalized_value == right_number.normalized_value;
200 }
201 _ => return false,
202 }
203 }
204}
205
206fn normalized_decimal_str_is_non_negative(s: &str) -> bool {
208 !s.trim().starts_with('-')
209}
210
211fn split_sign_and_magnitude(number_string: &str) -> (bool, String) {
212 let trimmed_number_string = number_string.trim();
213 if let Some(stripped_number_string) = trimmed_number_string.strip_prefix('-') {
214 (true, stripped_number_string.trim().to_string())
215 } else {
216 (false, trimmed_number_string.to_string())
217 }
218}
219
220pub fn mul_signed_decimal_str(left_number_string: &str, right_number_string: &str) -> String {
221 let (left_is_negative, left_magnitude_number_string) =
222 split_sign_and_magnitude(left_number_string);
223 let (right_is_negative, right_magnitude_number_string) =
224 split_sign_and_magnitude(right_number_string);
225 let multiplied_magnitude_number_string = mul_decimal_str_and_normalize(
226 &left_magnitude_number_string,
227 &right_magnitude_number_string,
228 );
229 let multiplied_magnitude_is_zero = multiplied_magnitude_number_string == "0";
230 let multiplied_result_is_negative = left_is_negative ^ right_is_negative;
231 if multiplied_result_is_negative && !multiplied_magnitude_is_zero {
232 normalize_decimal_number_string(&format!("-{}", multiplied_magnitude_number_string))
233 } else {
234 normalize_decimal_number_string(&multiplied_magnitude_number_string)
235 }
236}
237
238pub fn add_signed_decimal_str(a: &str, b: &str) -> String {
240 let (a_neg, a_mag) = split_sign_and_magnitude(a);
241 let (b_neg, b_mag) = split_sign_and_magnitude(b);
242 match (a_neg, b_neg) {
243 (false, false) => add_decimal_str_and_normalize(&a_mag, &b_mag),
244 (true, true) => {
245 let sum_mag = add_decimal_str_and_normalize(&a_mag, &b_mag);
246 if sum_mag == "0" {
247 "0".to_string()
248 } else {
249 normalize_decimal_number_string(&format!("-{}", sum_mag))
250 }
251 }
252 (false, true) => sub_decimal_str_and_normalize(&a_mag, &b_mag),
253 (true, false) => sub_decimal_str_and_normalize(&b_mag, &a_mag),
254 }
255}
256
257pub fn sub_signed_decimal_str(a: &str, b: &str) -> String {
259 add_signed_decimal_str(a, &mul_signed_decimal_str(b, "-1"))
260}
261
262impl Obj {
263 pub fn replace_with_numeric_result_if_can_be_calculated(&self) -> (Obj, bool) {
264 if let Some(calculated_number) = self.evaluate_to_normalized_decimal_number() {
265 (Obj::Number(calculated_number), true)
266 } else {
267 (self.clone(), false)
268 }
269 }
270}
271
272pub fn add_decimal_str_and_normalize(a: &str, b: &str) -> String {
274 let (mut int_a, mut frac_a) = parse_decimal_parts(a);
275 let (mut int_b, mut frac_b) = parse_decimal_parts(b);
276 let frac_len = frac_a.len().max(frac_b.len());
277 frac_a.resize(frac_len, 0);
278 frac_b.resize(frac_len, 0);
279 let int_len = int_a.len().max(int_b.len());
280 int_a.reverse();
281 int_b.reverse();
282 int_a.resize(int_len, 0);
283 int_b.resize(int_len, 0);
284
285 let mut out_frac = vec![0u8; frac_len];
286 let mut carry = 0u8;
287 for i in (0..frac_len).rev() {
288 let sum = frac_a[i] + frac_b[i] + carry;
289 out_frac[i] = sum % 10;
290 carry = sum / 10;
291 }
292 let mut out_int = Vec::with_capacity(int_len + 1);
293 for i in 0..int_len {
294 let sum = int_a[i] + int_b[i] + carry;
295 out_int.push(sum % 10);
296 carry = sum / 10;
297 }
298 if carry > 0 {
299 out_int.push(carry);
300 }
301 out_int.reverse();
302
303 let int_str: String = out_int.iter().map(|&d| (b'0' + d) as char).collect();
304 let frac_str: String = out_frac.iter().map(|&d| (b'0' + d) as char).collect();
305 let result = if frac_str.is_empty() || out_frac.iter().all(|&d| d == 0) {
306 int_str
307 } else {
308 format!("{}.{}", int_str, frac_str.trim_end_matches('0'))
309 };
310 normalize_decimal_number_string(&result)
311}
312
313pub fn sub_decimal_str_and_normalize(a: &str, b: &str) -> String {
315 let (int_a, frac_a) = parse_decimal_parts(a);
316 let (int_b, frac_b) = parse_decimal_parts(b);
317 let frac_len = frac_a.len().max(frac_b.len());
318 let mut fa: Vec<u8> = frac_a.iter().cloned().collect();
319 let mut fb: Vec<u8> = frac_b.iter().cloned().collect();
320 fa.resize(frac_len, 0);
321 fb.resize(frac_len, 0);
322 let int_len = int_a.len().max(int_b.len());
323 let mut ia: Vec<u8> = int_a.iter().cloned().collect();
324 let mut ib: Vec<u8> = int_b.iter().cloned().collect();
325 ia.reverse();
326 ib.reverse();
327 ia.resize(int_len, 0);
328 ib.resize(int_len, 0);
329
330 let cmp = compare_decimal_parts(&ia, &fa, &ib, &fb);
331 let (top_int, top_frac, bot_int, bot_frac) = if cmp >= 0 {
332 (ia, fa, ib, fb)
333 } else {
334 let inner = sub_decimal_str_and_normalize(b, a);
335 return normalize_decimal_number_string(&format!("-{}", inner));
336 };
337
338 let mut out_frac = vec![0u8; frac_len];
339 let mut borrow: i16 = 0;
340 for i in (0..frac_len).rev() {
341 let mut d = top_frac[i] as i16 - bot_frac[i] as i16 - borrow;
342 borrow = 0;
343 if d < 0 {
344 d += 10;
345 borrow = 1;
346 }
347 out_frac[i] = d as u8;
348 }
349 let mut out_int = Vec::with_capacity(int_len);
350 for i in 0..int_len {
351 let mut d = top_int[i] as i16 - bot_int[i] as i16 - borrow;
352 borrow = 0;
353 if d < 0 {
354 d += 10;
355 borrow = 1;
356 }
357 out_int.push(d as u8);
358 }
359 out_int.reverse();
360 let start = out_int
361 .iter()
362 .position(|&d| d != 0)
363 .unwrap_or(out_int.len().saturating_sub(1));
364 let out_int = out_int[start..].to_vec();
365
366 let int_str: String = if out_int.is_empty() {
367 "0".to_string()
368 } else {
369 out_int.iter().map(|&d| (b'0' + d) as char).collect()
370 };
371 let frac_str: String = out_frac.iter().map(|&d| (b'0' + d) as char).collect();
372 let frac_trim = frac_str.trim_end_matches('0');
373 let result = if frac_trim.is_empty() {
374 int_str
375 } else {
376 format!("{}.{}", int_str, frac_trim)
377 };
378 normalize_decimal_number_string(&result)
379}
380
381fn compare_decimal_parts(int_a: &[u8], frac_a: &[u8], int_b: &[u8], frac_b: &[u8]) -> i32 {
382 let len_a = int_a.len();
383 let len_b = int_b.len();
384 if len_a != len_b {
385 return (len_a as i32) - (len_b as i32);
386 }
387 for i in (0..len_a).rev() {
388 if int_a[i] != int_b[i] {
389 return int_a[i] as i32 - int_b[i] as i32;
390 }
391 }
392 for i in 0..frac_a.len().max(frac_b.len()) {
393 let da = match frac_a.get(i) {
394 Some(&d) => d,
395 None => 0,
396 };
397 let db = match frac_b.get(i) {
398 Some(&d) => d,
399 None => 0,
400 };
401 if da != db {
402 return da as i32 - db as i32;
403 }
404 }
405 0
406}
407
408pub fn mul_decimal_str_and_normalize(a: &str, b: &str) -> String {
410 let (int_a, frac_a) = parse_decimal_parts(a);
411 let (int_b, frac_b) = parse_decimal_parts(b);
412 let frac_places = frac_a.len() + frac_b.len();
413 let digits_a: Vec<u8> = int_a
414 .iter()
415 .cloned()
416 .chain(frac_a.iter().cloned())
417 .collect();
418 let digits_b: Vec<u8> = int_b
419 .iter()
420 .cloned()
421 .chain(frac_b.iter().cloned())
422 .collect();
423 let len_a = digits_a.len();
424 let len_b = digits_b.len();
425 let mut product = vec![0u32; len_a + len_b];
426 for (i, &da) in digits_a.iter().enumerate() {
427 for (j, &db) in digits_b.iter().enumerate() {
428 let place = (len_a - 1 - i) + (len_b - 1 - j);
429 product[place] += da as u32 * db as u32;
430 }
431 }
432 let mut carry = 0u32;
433 for p in product.iter_mut() {
434 *p += carry;
435 carry = *p / 10;
436 *p %= 10;
437 }
438 while carry > 0 {
439 product.push(carry % 10);
440 carry /= 10;
441 }
442 let total_len = product.len();
443 let int_part: String = if frac_places >= total_len {
444 "0".to_string()
445 } else {
446 product[frac_places..]
447 .iter()
448 .rev()
449 .map(|&d| (b'0' + d as u8) as char)
450 .collect::<String>()
451 .trim_start_matches('0')
452 .to_string()
453 };
454 let frac_part: String = if frac_places == 0 {
455 String::new()
456 } else {
457 product[..frac_places.min(total_len)]
458 .iter()
459 .rev()
460 .map(|&d| (b'0' + d as u8) as char)
461 .collect::<String>()
462 .trim_end_matches('0')
463 .to_string()
464 };
465 let int_str = if int_part.is_empty() { "0" } else { &int_part };
466 let result = if frac_part.is_empty() {
467 int_str.to_string()
468 } else {
469 format!("{}.{}", int_str, frac_part)
470 };
471 normalize_decimal_number_string(&result)
472}
473
474pub fn mod_decimal_str_and_normalize(a: &str, b: &str) -> String {
476 let (int_a, _) = parse_decimal_parts(a);
477 let (int_b, _) = parse_decimal_parts(b);
478 let a_digits = trim_leading_zeros(&int_a);
479 let b_digits = trim_leading_zeros(&int_b);
480 if a_digits.is_empty() {
481 return "0".to_string();
482 }
483 if b_digits.is_empty() || (b_digits.len() == 1 && b_digits[0] == 0) {
484 return "0".to_string();
485 }
486 if compare_digits(&a_digits, &b_digits) == std::cmp::Ordering::Less {
487 return digits_to_string(&a_digits);
488 }
489 let mut current: Vec<u8> = vec![];
490 for &da in &a_digits {
491 current.push(da);
492 current = trim_leading_zeros(¤t);
493 let mut d = 9u8;
494 loop {
495 let product = mul_digit(&b_digits, d);
496 if compare_digits(¤t, &product) != std::cmp::Ordering::Less {
497 current = sub_digits(¤t, &product);
498 break;
499 }
500 if d == 0 {
501 break;
502 }
503 d -= 1;
504 }
505 }
506 normalize_decimal_number_string(&digits_to_string(¤t))
507}
508
509pub fn pow_decimal_str_and_normalize(base: &str, exp: &str) -> Option<String> {
511 let (exp_int, exp_frac) = parse_decimal_parts(exp);
512 if exp_frac.iter().any(|&d| d != 0) {
513 return None;
514 }
515 let mut n = 0usize;
516 for &d in &exp_int {
517 n = n.saturating_mul(10).saturating_add(d as usize);
518 }
519 if n == 0 {
520 return Some("1".to_string());
521 }
522 let mut acc = "1".to_string();
523 let mut b = base.to_string();
524 let mut e = n;
525 while e > 0 {
526 if e % 2 == 1 {
527 acc = mul_decimal_str_and_normalize(&acc, &b);
528 }
529 b = mul_decimal_str_and_normalize(&b, &b);
530 e /= 2;
531 }
532 Some(normalize_decimal_number_string(&acc))
533}
534
535fn trim_leading_zeros(d: &[u8]) -> Vec<u8> {
536 let start = d.iter().position(|&x| x != 0).unwrap_or(d.len());
537 d[start..].to_vec()
538}
539
540fn digits_to_string(d: &[u8]) -> String {
542 let t = trim_leading_zeros(d);
543 if t.is_empty() {
544 return "0".to_string();
545 }
546 t.iter().map(|&x| (b'0' + x) as char).collect()
547}
548
549fn mul_digit(b: &[u8], d: u8) -> Vec<u8> {
551 if d == 0 {
552 return vec![0];
553 }
554 let mut b = b.to_vec();
555 b.reverse();
556 let mut carry = 0u16;
557 for x in b.iter_mut() {
558 let p = *x as u16 * d as u16 + carry;
559 *x = (p % 10) as u8;
560 carry = p / 10;
561 }
562 while carry > 0 {
563 b.push((carry % 10) as u8);
564 carry /= 10;
565 }
566 b.reverse();
567 trim_leading_zeros(&b)
568}
569
570fn compare_digits(a: &[u8], b: &[u8]) -> std::cmp::Ordering {
572 let a = trim_leading_zeros(a);
573 let b = trim_leading_zeros(b);
574 if a.len() != b.len() {
575 return a.len().cmp(&b.len());
576 }
577 for (x, y) in a.iter().zip(b.iter()) {
578 if x != y {
579 return x.cmp(y);
580 }
581 }
582 std::cmp::Ordering::Equal
583}
584
585fn sub_digits(a: &[u8], b: &[u8]) -> Vec<u8> {
587 let mut a = a.to_vec();
588 let mut b = b.to_vec();
589 let len = a.len().max(b.len());
590 a.reverse();
591 b.reverse();
592 a.resize(len, 0);
593 b.resize(len, 0);
594 let mut borrow: i16 = 0;
595 let mut out = Vec::with_capacity(len);
596 for i in 0..len {
597 let mut d = a[i] as i16 - b[i] as i16 - borrow;
598 borrow = 0;
599 if d < 0 {
600 d += 10;
601 borrow = 1;
602 }
603 out.push(d as u8);
604 }
605 out.reverse();
606 trim_leading_zeros(&out)
607}
608
609pub fn normalize_decimal_number_string(s: &str) -> String {
611 let s = s.trim();
612 if s.is_empty() {
613 return "0".to_string();
614 }
615 let minus_count = s.chars().take_while(|&c| c == '-').count();
616 let rest = s[minus_count..].trim();
617 let negative = (minus_count % 2) == 1;
618
619 let magnitude = if rest.contains('.') {
620 let (int_str, frac_str) = rest.split_once('.').unwrap_or((rest, ""));
621 let frac_trimmed = frac_str.trim_end_matches('0');
622 let int_trimmed = int_str.trim_start_matches('0');
623 let int_part = if int_trimmed.is_empty() || int_trimmed == "." {
624 "0"
625 } else {
626 int_trimmed
627 };
628 if frac_trimmed.is_empty() {
629 int_part.to_string()
630 } else {
631 format!("{}.{}", int_part, frac_trimmed)
632 }
633 } else {
634 let t = rest.trim_start_matches('0');
635 if t.is_empty() { "0" } else { t }.to_string()
636 };
637
638 let is_zero = magnitude == "0"
639 || (magnitude.starts_with("0.") && magnitude[2..].chars().all(|c| c == '0'));
640 if is_zero {
641 "0".to_string()
642 } else if negative {
643 format!("-{}", magnitude)
644 } else {
645 magnitude
646 }
647}
648
649fn parse_decimal_parts(s: &str) -> (Vec<u8>, Vec<u8>) {
651 let s = s.trim();
652 let (int_str, frac_str) = match s.find('.') {
653 Some(i) => (&s[..i], &s[i + 1..]),
654 None => (s, ""),
655 };
656 let int_digits: Vec<u8> = if int_str.is_empty() || int_str == "-" {
657 vec![0]
658 } else {
659 int_str
660 .chars()
661 .filter(|c| c.is_ascii_digit())
662 .map(|c| c as u8 - b'0')
663 .collect()
664 };
665 let frac_digits: Vec<u8> = frac_str
666 .chars()
667 .filter(|c| c.is_ascii_digit())
668 .map(|c| c as u8 - b'0')
669 .collect();
670 let int_digits = if int_digits.is_empty() {
671 vec![0]
672 } else {
673 int_digits
674 };
675 (int_digits, frac_digits)
676}