1use crate::ast::{BinaryOp, Expression, Function, Variable};
26use std::collections::HashMap;
27use std::fmt;
28
29fn try_expr_to_f64(expr: &Expression) -> Option<f64> {
32 use crate::ast::{SymbolicConstant, UnaryOp};
33 match expr {
34 Expression::Integer(n) => Some(*n as f64),
35 Expression::Float(f) => Some(*f),
36 Expression::Rational(r) => Some(*r.numer() as f64 / *r.denom() as f64),
37 Expression::Constant(c) => match c {
38 SymbolicConstant::Pi => Some(std::f64::consts::PI),
39 SymbolicConstant::E => Some(std::f64::consts::E),
40 SymbolicConstant::I => None,
41 },
42 Expression::Unary(op, inner) => {
43 let val = try_expr_to_f64(inner)?;
44 match op {
45 UnaryOp::Neg => Some(-val),
46 UnaryOp::Abs => Some(val.abs()),
47 _ => None,
48 }
49 }
50 Expression::Binary(op, left, right) => {
51 let l = try_expr_to_f64(left)?;
52 let r = try_expr_to_f64(right)?;
53 match op {
54 BinaryOp::Add => Some(l + r),
55 BinaryOp::Sub => Some(l - r),
56 BinaryOp::Mul => Some(l * r),
57 BinaryOp::Div if r.abs() > 1e-15 => Some(l / r),
58 _ => None,
59 }
60 }
61 Expression::Power(base, exp) => {
62 let b = try_expr_to_f64(base)?;
63 let e = try_expr_to_f64(exp)?;
64 Some(b.powf(e))
65 }
66 _ => None,
67 }
68}
69
70#[derive(Debug, Clone, PartialEq)]
72#[non_exhaustive]
73pub enum SeriesError {
74 CannotExpand(String),
76 InvalidCenter(String),
78 DivisionByZero,
80 DerivativeFailed(String),
82 EvaluationFailed(String),
84 InvalidOrder(String),
86}
87
88impl fmt::Display for SeriesError {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 match self {
91 SeriesError::CannotExpand(msg) => write!(f, "Cannot expand expression: {}", msg),
92 SeriesError::InvalidCenter(msg) => write!(f, "Invalid center point: {}", msg),
93 SeriesError::DivisionByZero => write!(f, "Division by zero in series expansion"),
94 SeriesError::DerivativeFailed(msg) => write!(f, "Differentiation failed: {}", msg),
95 SeriesError::EvaluationFailed(msg) => write!(f, "Evaluation at center failed: {}", msg),
96 SeriesError::InvalidOrder(msg) => write!(f, "Invalid order: {}", msg),
97 }
98 }
99}
100
101impl std::error::Error for SeriesError {}
102
103pub type SeriesResult<T> = Result<T, SeriesError>;
105
106#[derive(Debug, Clone, PartialEq)]
108pub struct SeriesTerm {
109 pub coefficient: Expression,
111 pub power: u32,
113}
114
115impl SeriesTerm {
116 pub fn new(coefficient: Expression, power: u32) -> Self {
118 SeriesTerm { coefficient, power }
119 }
120
121 pub fn is_zero(&self) -> bool {
123 matches!(&self.coefficient, Expression::Integer(0))
124 || matches!(&self.coefficient, Expression::Float(x) if x.abs() < 1e-15)
125 }
126}
127
128impl fmt::Display for SeriesTerm {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 if self.power == 0 {
131 write!(f, "{}", self.coefficient)
132 } else if self.power == 1 {
133 write!(f, "{}·x", self.coefficient)
134 } else {
135 write!(f, "{}·x^{}", self.coefficient, self.power)
136 }
137 }
138}
139
140#[derive(Debug, Clone, PartialEq)]
142pub enum RemainderTerm {
143 Lagrange {
145 bound: Expression,
147 order: u32,
149 },
150 BigO {
152 order: u32,
154 },
155}
156
157impl RemainderTerm {
158 pub fn order(&self) -> u32 {
160 match self {
161 RemainderTerm::Lagrange { order, .. } => *order,
162 RemainderTerm::BigO { order } => *order,
163 }
164 }
165
166 pub fn to_latex(&self) -> String {
168 match self {
169 RemainderTerm::Lagrange { bound, order } => {
170 format!("R_{{{}}}(x) \\leq {}", order, bound)
171 }
172 RemainderTerm::BigO { order } => {
173 format!("O(x^{{{}}})", order)
174 }
175 }
176 }
177}
178
179impl fmt::Display for RemainderTerm {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 match self {
182 RemainderTerm::Lagrange { order, .. } => write!(f, "R_{}(x)", order),
183 RemainderTerm::BigO { order } => write!(f, "O(x^{})", order),
184 }
185 }
186}
187
188#[derive(Debug, Clone, PartialEq)]
190pub struct Series {
191 pub terms: Vec<SeriesTerm>,
193 pub center: Expression,
195 pub variable: Variable,
197 pub order: u32,
199 pub remainder: Option<RemainderTerm>,
201}
202
203impl Series {
204 pub fn new(variable: Variable, center: Expression, order: u32) -> Self {
206 Series {
207 terms: Vec::new(),
208 center,
209 variable,
210 order,
211 remainder: None,
212 }
213 }
214
215 pub fn add_term(&mut self, term: SeriesTerm) {
217 if !term.is_zero() {
218 self.terms.push(term);
219 }
220 }
221
222 pub fn set_remainder(&mut self, remainder: RemainderTerm) {
224 self.remainder = Some(remainder);
225 }
226
227 pub fn term_count(&self) -> usize {
229 self.terms.len()
230 }
231
232 pub fn get_term(&self, power: u32) -> Option<&SeriesTerm> {
234 self.terms.iter().find(|t| t.power == power)
235 }
236
237 pub fn to_expression(&self) -> Expression {
239 if self.terms.is_empty() {
240 return Expression::Integer(0);
241 }
242
243 let var_expr = Expression::Variable(self.variable.clone());
244 let is_centered_at_zero = matches!(&self.center, Expression::Integer(0))
245 || matches!(&self.center, Expression::Float(x) if x.abs() < 1e-15);
246
247 let mut result: Option<Expression> = None;
248
249 for term in &self.terms {
250 let power_base = if is_centered_at_zero {
252 var_expr.clone()
253 } else {
254 Expression::Binary(
255 BinaryOp::Sub,
256 Box::new(var_expr.clone()),
257 Box::new(self.center.clone()),
258 )
259 };
260
261 let term_expr = if term.power == 0 {
262 term.coefficient.clone()
263 } else if term.power == 1 {
264 Expression::Binary(
265 BinaryOp::Mul,
266 Box::new(term.coefficient.clone()),
267 Box::new(power_base),
268 )
269 } else {
270 Expression::Binary(
271 BinaryOp::Mul,
272 Box::new(term.coefficient.clone()),
273 Box::new(Expression::Power(
274 Box::new(power_base),
275 Box::new(Expression::Integer(term.power as i64)),
276 )),
277 )
278 };
279
280 result = Some(match result {
281 None => term_expr,
282 Some(acc) => Expression::Binary(BinaryOp::Add, Box::new(acc), Box::new(term_expr)),
283 });
284 }
285
286 result.unwrap_or(Expression::Integer(0)).simplify()
287 }
288
289 pub fn to_latex(&self) -> String {
291 if self.terms.is_empty() {
292 return "0".to_string();
293 }
294
295 let is_centered_at_zero = matches!(&self.center, Expression::Integer(0));
296 let var_name = &self.variable.name;
297
298 let mut parts = Vec::new();
299 for (i, term) in self.terms.iter().enumerate() {
300 let coeff_str = format_coefficient_latex(&term.coefficient);
301
302 let term_str = if term.power == 0 {
303 coeff_str
304 } else {
305 let var_part = if is_centered_at_zero {
306 if term.power == 1 {
307 var_name.clone()
308 } else {
309 format!("{}^{{{}}}", var_name, term.power)
310 }
311 } else {
312 if term.power == 1 {
313 format!("({} - {})", var_name, self.center)
314 } else {
315 format!("({} - {})^{{{}}}", var_name, self.center, term.power)
316 }
317 };
318
319 if coeff_str == "1" {
320 var_part
321 } else if coeff_str == "-1" {
322 format!("-{}", var_part)
323 } else {
324 format!("{} {}", coeff_str, var_part)
325 }
326 };
327
328 if i == 0 {
329 parts.push(term_str);
330 } else {
331 if term_str.starts_with('-') {
333 parts.push(format!(" - {}", &term_str[1..]));
334 } else {
335 parts.push(format!(" + {}", term_str));
336 }
337 }
338 }
339
340 let mut result = parts.join("");
341
342 if let Some(ref remainder) = self.remainder {
343 result.push_str(&format!(" + {}", remainder.to_latex()));
344 }
345
346 result
347 }
348
349 pub fn evaluate(&self, x: f64) -> Option<f64> {
351 let center_val = self.center.evaluate(&HashMap::new())?;
352 let dx = x - center_val;
353
354 let mut sum = 0.0;
355 for term in &self.terms {
356 let coeff = term.coefficient.evaluate(&HashMap::new())?;
357 sum += coeff * dx.powi(term.power as i32);
358 }
359
360 Some(sum)
361 }
362
363 fn coeff_f64(&self, power: u32) -> f64 {
365 self.get_term(power)
366 .and_then(|t| try_expr_to_f64(&t.coefficient))
367 .unwrap_or(0.0)
368 }
369
370 pub fn reciprocal(&self) -> SeriesResult<Series> {
373 let a0 = self.coeff_f64(0);
375 if a0.abs() < 1e-15 {
376 return Err(SeriesError::CannotExpand(
377 "Cannot compute reciprocal: constant term is zero".into(),
378 ));
379 }
380
381 let mut result = Series::new(self.variable.clone(), self.center.clone(), self.order);
382
383 result.add_term(SeriesTerm::new(Expression::Float(1.0 / a0), 0));
385
386 for n in 1..=self.order {
388 let mut sum = 0.0;
389 for k in 1..=n {
390 let a_k = self.coeff_f64(k);
391 let b_n_k = result.coeff_f64(n - k);
392 sum += a_k * b_n_k;
393 }
394 let b_n = -sum / a0;
395 if b_n.abs() > 1e-15 {
396 result.add_term(SeriesTerm::new(Expression::Float(b_n), n));
397 }
398 }
399
400 Ok(result)
401 }
402
403 pub fn differentiate(&self) -> Series {
406 let new_order = if self.order > 0 { self.order - 1 } else { 0 };
407 let mut result = Series::new(self.variable.clone(), self.center.clone(), new_order);
408
409 for term in &self.terms {
410 if term.power > 0 {
411 let new_coeff = Expression::Binary(
413 BinaryOp::Mul,
414 Box::new(Expression::Integer(term.power as i64)),
415 Box::new(term.coefficient.clone()),
416 )
417 .simplify();
418 result.add_term(SeriesTerm::new(new_coeff, term.power - 1));
419 }
420 }
421
422 result
423 }
424
425 pub fn integrate(&self, constant: Expression) -> Series {
428 let mut result = Series::new(self.variable.clone(), self.center.clone(), self.order + 1);
429
430 result.add_term(SeriesTerm::new(constant, 0));
432
433 for term in &self.terms {
434 let new_coeff = Expression::Binary(
436 BinaryOp::Div,
437 Box::new(term.coefficient.clone()),
438 Box::new(Expression::Integer((term.power + 1) as i64)),
439 )
440 .simplify();
441 result.add_term(SeriesTerm::new(new_coeff, term.power + 1));
442 }
443
444 result
445 }
446}
447
448use std::ops::{Add, Div, Mul, Sub};
450
451impl Add for Series {
452 type Output = SeriesResult<Series>;
453
454 fn add(self, rhs: Series) -> SeriesResult<Series> {
455 if self.center != rhs.center {
457 return Err(SeriesError::InvalidCenter(
458 "Cannot add series with different centers".into(),
459 ));
460 }
461 if self.variable.name != rhs.variable.name {
462 return Err(SeriesError::CannotExpand(
463 "Cannot add series with different variables".into(),
464 ));
465 }
466
467 let min_order = self.order.min(rhs.order);
468 let mut result = Series::new(self.variable.clone(), self.center.clone(), min_order);
469
470 let mut coeffs: HashMap<u32, Expression> = HashMap::new();
472
473 for term in &self.terms {
474 if term.power <= min_order {
475 coeffs.insert(term.power, term.coefficient.clone());
476 }
477 }
478
479 for term in &rhs.terms {
480 if term.power <= min_order {
481 let coeff = coeffs.entry(term.power).or_insert(Expression::Integer(0));
482 *coeff = Expression::Binary(
483 BinaryOp::Add,
484 Box::new(coeff.clone()),
485 Box::new(term.coefficient.clone()),
486 )
487 .simplify();
488 }
489 }
490
491 for (power, coeff) in coeffs {
492 result.add_term(SeriesTerm::new(coeff, power));
493 }
494
495 result.terms.sort_by_key(|t| t.power);
497
498 Ok(result)
499 }
500}
501
502impl Sub for Series {
503 type Output = SeriesResult<Series>;
504
505 fn sub(self, rhs: Series) -> SeriesResult<Series> {
506 if self.center != rhs.center {
508 return Err(SeriesError::InvalidCenter(
509 "Cannot subtract series with different centers".into(),
510 ));
511 }
512 if self.variable.name != rhs.variable.name {
513 return Err(SeriesError::CannotExpand(
514 "Cannot subtract series with different variables".into(),
515 ));
516 }
517
518 let min_order = self.order.min(rhs.order);
519 let mut result = Series::new(self.variable.clone(), self.center.clone(), min_order);
520
521 let mut coeffs: HashMap<u32, Expression> = HashMap::new();
523
524 for term in &self.terms {
525 if term.power <= min_order {
526 coeffs.insert(term.power, term.coefficient.clone());
527 }
528 }
529
530 for term in &rhs.terms {
531 if term.power <= min_order {
532 let coeff = coeffs.entry(term.power).or_insert(Expression::Integer(0));
533 *coeff = Expression::Binary(
534 BinaryOp::Sub,
535 Box::new(coeff.clone()),
536 Box::new(term.coefficient.clone()),
537 )
538 .simplify();
539 }
540 }
541
542 for (power, coeff) in coeffs {
543 result.add_term(SeriesTerm::new(coeff, power));
544 }
545
546 result.terms.sort_by_key(|t| t.power);
548
549 Ok(result)
550 }
551}
552
553impl Mul for Series {
554 type Output = SeriesResult<Series>;
555
556 fn mul(self, rhs: Series) -> SeriesResult<Series> {
557 if self.center != rhs.center {
559 return Err(SeriesError::InvalidCenter(
560 "Cannot multiply series with different centers".into(),
561 ));
562 }
563 if self.variable.name != rhs.variable.name {
564 return Err(SeriesError::CannotExpand(
565 "Cannot multiply series with different variables".into(),
566 ));
567 }
568
569 let min_order = self.order.min(rhs.order);
570 let mut result = Series::new(self.variable.clone(), self.center.clone(), min_order);
571
572 let mut coeffs: HashMap<u32, Expression> = HashMap::new();
574
575 for term_a in &self.terms {
576 for term_b in &rhs.terms {
577 let new_power = term_a.power + term_b.power;
578 if new_power <= min_order {
579 let product = Expression::Binary(
580 BinaryOp::Mul,
581 Box::new(term_a.coefficient.clone()),
582 Box::new(term_b.coefficient.clone()),
583 )
584 .simplify();
585
586 let coeff = coeffs.entry(new_power).or_insert(Expression::Integer(0));
587 *coeff = Expression::Binary(
588 BinaryOp::Add,
589 Box::new(coeff.clone()),
590 Box::new(product),
591 )
592 .simplify();
593 }
594 }
595 }
596
597 for (power, coeff) in coeffs {
598 result.add_term(SeriesTerm::new(coeff, power));
599 }
600
601 result.terms.sort_by_key(|t| t.power);
603
604 Ok(result)
605 }
606}
607
608impl Div for Series {
609 type Output = SeriesResult<Series>;
610
611 fn div(self, rhs: Series) -> SeriesResult<Series> {
612 let reciprocal = rhs.reciprocal()?;
614 self * reciprocal
615 }
616}
617
618pub fn compose_series(outer: &Series, inner: &Series) -> SeriesResult<Series> {
621 if outer.center != inner.center {
623 return Err(SeriesError::InvalidCenter(
624 "Cannot compose series with different centers".into(),
625 ));
626 }
627 if outer.variable.name != inner.variable.name {
628 return Err(SeriesError::CannotExpand(
629 "Cannot compose series with different variables".into(),
630 ));
631 }
632
633 let inner_a0 = inner.coeff_f64(0);
635 if inner_a0.abs() > 1e-15 {
636 return Err(SeriesError::CannotExpand(
637 "Cannot compose series: inner series must have zero constant term".into(),
638 ));
639 }
640
641 let order = outer.order.min(inner.order);
642 let mut result = Series::new(outer.variable.clone(), outer.center.clone(), order);
643
644 let mut inner_powers: Vec<Series> = Vec::new();
647
648 let mut inner_pow_0 = Series::new(inner.variable.clone(), inner.center.clone(), order);
650 inner_pow_0.add_term(SeriesTerm::new(Expression::Integer(1), 0));
651 inner_powers.push(inner_pow_0);
652
653 for _ in 1..=order {
655 let prev = inner_powers.last().unwrap().clone();
656 let next = (prev * inner.clone())?;
657 inner_powers.push(next);
658 }
659
660 let mut coeffs: HashMap<u32, Expression> = HashMap::new();
662
663 for term in &outer.terms {
664 if term.power as usize <= inner_powers.len() - 1 {
665 let inner_pow = &inner_powers[term.power as usize];
666 for inner_term in &inner_pow.terms {
667 if inner_term.power <= order {
668 let contribution = Expression::Binary(
669 BinaryOp::Mul,
670 Box::new(term.coefficient.clone()),
671 Box::new(inner_term.coefficient.clone()),
672 )
673 .simplify();
674
675 let coeff = coeffs
676 .entry(inner_term.power)
677 .or_insert(Expression::Integer(0));
678 *coeff = Expression::Binary(
679 BinaryOp::Add,
680 Box::new(coeff.clone()),
681 Box::new(contribution),
682 )
683 .simplify();
684 }
685 }
686 }
687 }
688
689 for (power, coeff) in coeffs {
690 result.add_term(SeriesTerm::new(coeff, power));
691 }
692
693 result.terms.sort_by_key(|t| t.power);
694
695 Ok(result)
696}
697
698pub fn reversion(series: &Series) -> SeriesResult<Series> {
702 let a0 = series.coeff_f64(0);
703 let a1 = series.coeff_f64(1);
704
705 if a0.abs() > 1e-15 {
706 return Err(SeriesError::CannotExpand(
707 "Cannot compute reversion: constant term must be zero".into(),
708 ));
709 }
710 if a1.abs() < 1e-15 {
711 return Err(SeriesError::CannotExpand(
712 "Cannot compute reversion: linear coefficient must be non-zero".into(),
713 ));
714 }
715
716 let order = series.order;
717 let mut result = Series::new(series.variable.clone(), series.center.clone(), order);
718
719 let b1 = 1.0 / a1;
721 result.add_term(SeriesTerm::new(Expression::Float(b1), 1));
722
723 for n in 2..=order {
728 let mut sum = 0.0;
732
733 for k in 1..=n {
734 let tk_coeff_n = compute_power_coeff(&result, k, n);
736 let a_k = series.coeff_f64(k);
737 sum += a_k * tk_coeff_n;
738 }
739
740 let contribution_without_b_n = sum - a1 * 0.0; let b_n = -contribution_without_b_n / a1;
747
748 if b_n.abs() > 1e-15 {
753 result.add_term(SeriesTerm::new(Expression::Float(b_n), n));
754 }
755 }
756
757 result.terms.sort_by_key(|t| t.power);
758
759 Ok(result)
760}
761
762fn compute_power_coeff(series: &Series, k: u32, n: u32) -> f64 {
764 if k == 0 {
765 return if n == 0 { 1.0 } else { 0.0 };
766 }
767 if k == 1 {
768 return series.coeff_f64(n);
769 }
770
771 let mut sum = 0.0;
774 for j in 0..=n {
775 let t_j = series.coeff_f64(j);
776 let t_k1_nj = compute_power_coeff(series, k - 1, n - j);
777 sum += t_j * t_k1_nj;
778 }
779 sum
780}
781
782fn format_coefficient_latex(expr: &Expression) -> String {
784 match expr {
785 Expression::Integer(n) => n.to_string(),
786 Expression::Float(x) => {
787 if (x - x.round()).abs() < 1e-10 {
788 format!("{}", x.round() as i64)
789 } else {
790 format!("{:.6}", x)
791 }
792 }
793 Expression::Rational(r) => {
794 format!("\\frac{{{}}}{{{}}}", r.numer(), r.denom())
795 }
796 _ => format!("{}", expr),
797 }
798}
799
800pub fn factorial(n: u32) -> u64 {
802 if n <= 1 {
803 1
804 } else {
805 (2..=n as u64).product()
806 }
807}
808
809pub fn factorial_expr(n: u32) -> Expression {
811 Expression::Integer(factorial(n) as i64)
812}
813
814pub fn evaluate_at(
816 expr: &Expression,
817 var: &Variable,
818 value: &Expression,
819) -> SeriesResult<Expression> {
820 let substituted = substitute(expr, var, value);
822
823 let simplified = substituted.simplify();
825
826 if let Some(val) = simplified.evaluate(&HashMap::new()) {
828 if val.is_nan() {
829 return Err(SeriesError::EvaluationFailed(format!(
830 "Expression evaluates to NaN at {} = {}",
831 var.name, value
832 )));
833 }
834 if val.is_infinite() {
835 return Err(SeriesError::EvaluationFailed(format!(
836 "Expression evaluates to infinity at {} = {}",
837 var.name, value
838 )));
839 }
840 return Ok(Expression::Float(val));
841 }
842
843 Ok(simplified)
844}
845
846fn substitute(expr: &Expression, var: &Variable, value: &Expression) -> Expression {
848 match expr {
849 Expression::Variable(v) if v.name == var.name => value.clone(),
850 Expression::Binary(op, left, right) => Expression::Binary(
851 *op,
852 Box::new(substitute(left, var, value)),
853 Box::new(substitute(right, var, value)),
854 ),
855 Expression::Unary(op, inner) => {
856 Expression::Unary(*op, Box::new(substitute(inner, var, value)))
857 }
858 Expression::Function(func, args) => Expression::Function(
859 func.clone(),
860 args.iter().map(|a| substitute(a, var, value)).collect(),
861 ),
862 Expression::Power(base, exp) => Expression::Power(
863 Box::new(substitute(base, var, value)),
864 Box::new(substitute(exp, var, value)),
865 ),
866 _ => expr.clone(),
867 }
868}
869
870pub fn compute_nth_derivative(
872 expr: &Expression,
873 var: &Variable,
874 n: u32,
875) -> SeriesResult<Expression> {
876 let mut result = expr.clone();
877 for _ in 0..n {
878 result = result.differentiate(&var.name);
879 result = result.simplify();
880 }
881 Ok(result)
882}
883
884pub fn taylor(
895 expr: &Expression,
896 var: &Variable,
897 center: &Expression,
898 order: u32,
899) -> SeriesResult<Series> {
900 if let Some(series) = try_known_series(expr, var, center, order) {
902 return Ok(series);
903 }
904
905 let mut series = Series::new(var.clone(), center.clone(), order);
906
907 for n in 0..=order {
908 let nth_deriv = compute_nth_derivative(expr, var, n)?;
910
911 let deriv_at_center = evaluate_at(&nth_deriv, var, center)?;
913
914 let n_fact = factorial(n) as i64;
916 let coefficient = if n_fact == 1 {
917 deriv_at_center
918 } else {
919 Expression::Binary(
920 BinaryOp::Div,
921 Box::new(deriv_at_center),
922 Box::new(Expression::Integer(n_fact)),
923 )
924 .simplify()
925 };
926
927 let term = SeriesTerm::new(coefficient, n);
929 series.add_term(term);
930 }
931
932 series.set_remainder(RemainderTerm::BigO { order: order + 1 });
934
935 Ok(series)
936}
937
938pub fn maclaurin(expr: &Expression, var: &Variable, order: u32) -> SeriesResult<Series> {
940 taylor(expr, var, &Expression::Integer(0), order)
941}
942
943fn try_known_series(
945 expr: &Expression,
946 var: &Variable,
947 center: &Expression,
948 order: u32,
949) -> Option<Series> {
950 if !matches!(center, Expression::Integer(0)) {
952 return None;
953 }
954
955 match expr {
956 Expression::Function(Function::Exp, args) if args.len() == 1 => {
957 if matches!(&args[0], Expression::Variable(v) if v.name == var.name) {
958 return Some(exp_series(var, order));
959 }
960 }
961 Expression::Function(Function::Sin, args) if args.len() == 1 => {
962 if matches!(&args[0], Expression::Variable(v) if v.name == var.name) {
963 return Some(sin_series(var, order));
964 }
965 }
966 Expression::Function(Function::Cos, args) if args.len() == 1 => {
967 if matches!(&args[0], Expression::Variable(v) if v.name == var.name) {
968 return Some(cos_series(var, order));
969 }
970 }
971 Expression::Function(Function::Ln, args) if args.len() == 1 => {
972 if let Expression::Binary(BinaryOp::Add, left, right) = &args[0] {
974 if matches!(**left, Expression::Integer(1))
975 && matches!(**right, Expression::Variable(ref v) if v.name == var.name)
976 {
977 return Some(ln_1_plus_x_series(var, order));
978 }
979 }
980 }
981 Expression::Function(Function::Atan, args) if args.len() == 1 => {
982 if matches!(&args[0], Expression::Variable(v) if v.name == var.name) {
983 return Some(arctan_series(var, order));
984 }
985 }
986 _ => {}
987 }
988
989 None
990}
991
992pub fn exp_series(var: &Variable, order: u32) -> Series {
994 let mut series = Series::new(var.clone(), Expression::Integer(0), order);
995
996 for n in 0..=order {
997 let coeff = if n == 0 {
998 Expression::Integer(1)
999 } else {
1000 let n_fact = factorial(n);
1001 Expression::Rational(num_rational::Ratio::new(1, n_fact as i64))
1002 };
1003 series.add_term(SeriesTerm::new(coeff, n));
1004 }
1005
1006 series.set_remainder(RemainderTerm::BigO { order: order + 1 });
1007 series
1008}
1009
1010pub fn sin_series(var: &Variable, order: u32) -> Series {
1012 let mut series = Series::new(var.clone(), Expression::Integer(0), order);
1013
1014 let mut n = 0u32;
1015 while 2 * n + 1 <= order {
1016 let power = 2 * n + 1;
1017 let sign = if n % 2 == 0 { 1i64 } else { -1i64 };
1018 let fact = factorial(power) as i64;
1019 let coeff = Expression::Rational(num_rational::Ratio::new(sign, fact));
1020 series.add_term(SeriesTerm::new(coeff, power));
1021 n += 1;
1022 }
1023
1024 series.set_remainder(RemainderTerm::BigO { order: order + 1 });
1025 series
1026}
1027
1028pub fn cos_series(var: &Variable, order: u32) -> Series {
1030 let mut series = Series::new(var.clone(), Expression::Integer(0), order);
1031
1032 let mut n = 0u32;
1033 while 2 * n <= order {
1034 let power = 2 * n;
1035 let sign = if n % 2 == 0 { 1i64 } else { -1i64 };
1036 let fact = if power == 0 {
1037 1
1038 } else {
1039 factorial(power) as i64
1040 };
1041 let coeff = Expression::Rational(num_rational::Ratio::new(sign, fact));
1042 series.add_term(SeriesTerm::new(coeff, power));
1043 n += 1;
1044 }
1045
1046 series.set_remainder(RemainderTerm::BigO { order: order + 1 });
1047 series
1048}
1049
1050pub fn ln_1_plus_x_series(var: &Variable, order: u32) -> Series {
1052 let mut series = Series::new(var.clone(), Expression::Integer(0), order);
1053
1054 for n in 1..=order {
1055 let sign = if n % 2 == 1 { 1i64 } else { -1i64 };
1056 let coeff = Expression::Rational(num_rational::Ratio::new(sign, n as i64));
1057 series.add_term(SeriesTerm::new(coeff, n));
1058 }
1059
1060 series.set_remainder(RemainderTerm::BigO { order: order + 1 });
1061 series
1062}
1063
1064pub fn arctan_series(var: &Variable, order: u32) -> Series {
1066 let mut series = Series::new(var.clone(), Expression::Integer(0), order);
1067
1068 let mut n = 0u32;
1069 while 2 * n + 1 <= order {
1070 let power = 2 * n + 1;
1071 let sign = if n % 2 == 0 { 1i64 } else { -1i64 };
1072 let coeff = Expression::Rational(num_rational::Ratio::new(sign, power as i64));
1073 series.add_term(SeriesTerm::new(coeff, power));
1074 n += 1;
1075 }
1076
1077 series.set_remainder(RemainderTerm::BigO { order: order + 1 });
1078 series
1079}
1080
1081pub fn binomial_series(exponent: &Expression, var: &Variable, order: u32) -> SeriesResult<Series> {
1084 let mut series = Series::new(var.clone(), Expression::Integer(0), order);
1085
1086 let a = match exponent.evaluate(&HashMap::new()) {
1088 Some(val) => val,
1089 None => {
1090 return Err(SeriesError::CannotExpand(
1091 "Binomial series requires numeric exponent".to_string(),
1092 ))
1093 }
1094 };
1095
1096 let mut binom_coeff = 1.0;
1098 for n in 0..=order {
1099 if n > 0 {
1100 binom_coeff *= (a - (n as f64 - 1.0)) / (n as f64);
1101 }
1102
1103 if binom_coeff.abs() > 1e-15 {
1104 let coeff = if (binom_coeff - binom_coeff.round()).abs() < 1e-10 {
1105 Expression::Integer(binom_coeff.round() as i64)
1106 } else {
1107 Expression::Float(binom_coeff)
1108 };
1109 series.add_term(SeriesTerm::new(coeff, n));
1110 }
1111
1112 if a.fract() == 0.0 && a >= 0.0 && n as f64 >= a {
1114 break;
1115 }
1116 }
1117
1118 if a.fract() != 0.0 || a < 0.0 {
1120 series.set_remainder(RemainderTerm::BigO { order: order + 1 });
1121 }
1122
1123 Ok(series)
1124}
1125
1126#[derive(Debug, Clone, PartialEq)]
1132pub enum SingularityType {
1133 Removable,
1135 Pole(u32),
1137 Essential,
1139}
1140
1141impl fmt::Display for SingularityType {
1142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1143 match self {
1144 SingularityType::Removable => write!(f, "removable singularity"),
1145 SingularityType::Pole(order) => write!(f, "pole of order {}", order),
1146 SingularityType::Essential => write!(f, "essential singularity"),
1147 }
1148 }
1149}
1150
1151#[derive(Debug, Clone)]
1153pub struct Singularity {
1154 pub location: Expression,
1156 pub singularity_type: SingularityType,
1158}
1159
1160impl Singularity {
1161 pub fn new(location: Expression, singularity_type: SingularityType) -> Self {
1163 Self {
1164 location,
1165 singularity_type,
1166 }
1167 }
1168
1169 pub fn is_pole(&self) -> bool {
1171 matches!(self.singularity_type, SingularityType::Pole(_))
1172 }
1173
1174 pub fn pole_order(&self) -> Option<u32> {
1176 match self.singularity_type {
1177 SingularityType::Pole(order) => Some(order),
1178 _ => None,
1179 }
1180 }
1181}
1182
1183#[derive(Debug, Clone)]
1189pub struct LaurentSeries {
1190 pub positive_terms: Vec<SeriesTerm>,
1192 pub negative_terms: Vec<SeriesTerm>,
1195 pub center: Expression,
1197 pub variable: Variable,
1199 pub principal_part_order: u32,
1201 pub analytic_part_order: u32,
1203}
1204
1205impl LaurentSeries {
1206 pub fn new(variable: Variable, center: Expression, neg_order: u32, pos_order: u32) -> Self {
1208 LaurentSeries {
1209 positive_terms: Vec::new(),
1210 negative_terms: Vec::new(),
1211 center,
1212 variable,
1213 principal_part_order: neg_order,
1214 analytic_part_order: pos_order,
1215 }
1216 }
1217
1218 pub fn add_positive_term(&mut self, term: SeriesTerm) {
1220 if !term.is_zero() {
1221 self.positive_terms.push(term);
1222 }
1223 }
1224
1225 pub fn add_negative_term(&mut self, coefficient: Expression, neg_power: u32) {
1228 if neg_power > 0 {
1229 let term = SeriesTerm::new(coefficient, neg_power);
1230 if !term.is_zero() {
1231 self.negative_terms.push(term);
1232 }
1233 }
1234 }
1235
1236 pub fn residue(&self) -> Expression {
1238 self.negative_terms
1239 .iter()
1240 .find(|t| t.power == 1)
1241 .map(|t| t.coefficient.clone())
1242 .unwrap_or(Expression::Integer(0))
1243 }
1244
1245 pub fn principal_part(&self) -> LaurentSeries {
1247 let mut result = LaurentSeries::new(
1248 self.variable.clone(),
1249 self.center.clone(),
1250 self.principal_part_order,
1251 0,
1252 );
1253 result.negative_terms = self.negative_terms.clone();
1254 result
1255 }
1256
1257 pub fn analytic_part(&self) -> Series {
1259 let mut result = Series::new(
1260 self.variable.clone(),
1261 self.center.clone(),
1262 self.analytic_part_order,
1263 );
1264 for term in &self.positive_terms {
1265 result.add_term(term.clone());
1266 }
1267 result
1268 }
1269
1270 pub fn is_taylor(&self) -> bool {
1272 self.negative_terms.is_empty()
1273 }
1274
1275 pub fn to_taylor(&self) -> Option<Series> {
1277 if self.is_taylor() {
1278 Some(self.analytic_part())
1279 } else {
1280 None
1281 }
1282 }
1283
1284 pub fn evaluate(&self, x: f64) -> Option<f64> {
1286 let center_val = try_expr_to_f64(&self.center)?;
1287 let dx = x - center_val;
1288
1289 if dx.abs() < 1e-15 && !self.negative_terms.is_empty() {
1290 return None;
1292 }
1293
1294 let mut sum = 0.0;
1295
1296 for term in &self.positive_terms {
1298 let coeff = try_expr_to_f64(&term.coefficient)?;
1299 sum += coeff * dx.powi(term.power as i32);
1300 }
1301
1302 for term in &self.negative_terms {
1304 let coeff = try_expr_to_f64(&term.coefficient)?;
1305 sum += coeff / dx.powi(term.power as i32);
1306 }
1307
1308 Some(sum)
1309 }
1310
1311 pub fn to_latex(&self) -> String {
1313 let is_centered_at_zero = matches!(&self.center, Expression::Integer(0))
1314 || matches!(&self.center, Expression::Float(x) if x.abs() < 1e-15);
1315
1316 let var_name = &self.variable.name;
1317 let mut parts = Vec::new();
1318
1319 let mut neg_sorted: Vec<_> = self.negative_terms.iter().collect();
1321 neg_sorted.sort_by(|a, b| b.power.cmp(&a.power));
1322
1323 for term in neg_sorted {
1324 let coeff_str = format_coefficient_latex(&term.coefficient);
1325 let var_part = if is_centered_at_zero {
1326 format!("{}^{{-{}}}", var_name, term.power)
1327 } else {
1328 format!("({} - {})^{{-{}}}", var_name, self.center, term.power)
1329 };
1330
1331 let term_str = if coeff_str == "1" {
1332 var_part
1333 } else if coeff_str == "-1" {
1334 format!("-{}", var_part)
1335 } else {
1336 format!("{} {}", coeff_str, var_part)
1337 };
1338
1339 if parts.is_empty() {
1340 parts.push(term_str);
1341 } else if term_str.starts_with('-') {
1342 parts.push(format!(" - {}", &term_str[1..]));
1343 } else {
1344 parts.push(format!(" + {}", term_str));
1345 }
1346 }
1347
1348 for term in &self.positive_terms {
1350 let coeff_str = format_coefficient_latex(&term.coefficient);
1351
1352 let term_str = if term.power == 0 {
1353 coeff_str
1354 } else {
1355 let var_part = if is_centered_at_zero {
1356 if term.power == 1 {
1357 var_name.clone()
1358 } else {
1359 format!("{}^{{{}}}", var_name, term.power)
1360 }
1361 } else if term.power == 1 {
1362 format!("({} - {})", var_name, self.center)
1363 } else {
1364 format!("({} - {})^{{{}}}", var_name, self.center, term.power)
1365 };
1366
1367 if coeff_str == "1" {
1368 var_part
1369 } else if coeff_str == "-1" {
1370 format!("-{}", var_part)
1371 } else {
1372 format!("{} {}", coeff_str, var_part)
1373 }
1374 };
1375
1376 if parts.is_empty() {
1377 parts.push(term_str);
1378 } else if term_str.starts_with('-') {
1379 parts.push(format!(" - {}", &term_str[1..]));
1380 } else {
1381 parts.push(format!(" + {}", term_str));
1382 }
1383 }
1384
1385 if parts.is_empty() {
1386 "0".to_string()
1387 } else {
1388 parts.join("")
1389 }
1390 }
1391}
1392
1393impl fmt::Display for LaurentSeries {
1394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1395 let latex = self.to_latex();
1396 let plain = latex
1398 .replace("^{-", "^(-")
1399 .replace("^{", "^")
1400 .replace("}", ")")
1401 .replace("{", "");
1402 write!(f, "{}", plain)
1403 }
1404}
1405
1406pub fn laurent(
1433 expr: &Expression,
1434 var: &Variable,
1435 center: &Expression,
1436 neg_order: u32,
1437 pos_order: u32,
1438) -> SeriesResult<LaurentSeries> {
1439 let mut series = LaurentSeries::new(var.clone(), center.clone(), neg_order, pos_order);
1440
1441 match expr {
1443 Expression::Binary(BinaryOp::Div, num, denom) => {
1445 if is_power_of_var(denom, var, center) {
1446 let power = get_var_power(denom, var);
1447 if power > 0 {
1448 let num_taylor = taylor(num, var, center, pos_order + power as u32)?;
1451
1452 for term in &num_taylor.terms {
1453 let new_power = term.power as i32 - power;
1454 if new_power < 0 && (-new_power as u32) <= neg_order {
1455 series.add_negative_term(term.coefficient.clone(), (-new_power) as u32);
1456 } else if new_power >= 0 && (new_power as u32) <= pos_order {
1457 series.add_positive_term(SeriesTerm::new(
1458 term.coefficient.clone(),
1459 new_power as u32,
1460 ));
1461 }
1462 }
1463
1464 return Ok(series);
1465 }
1466 }
1467
1468 let num_taylor = taylor(num, var, center, pos_order + neg_order)?;
1472 let denom_taylor = taylor(denom, var, center, pos_order + neg_order)?;
1473
1474 let denom_zero_order = find_leading_power(&denom_taylor);
1476
1477 if denom_zero_order > 0 {
1478 let result = divide_series(&num_taylor, &denom_taylor, neg_order, pos_order)?;
1483
1484 for (power, coeff) in result {
1485 if power < 0 && (-power as u32) <= neg_order {
1486 series.add_negative_term(coeff, (-power) as u32);
1487 } else if power >= 0 && (power as u32) <= pos_order {
1488 series.add_positive_term(SeriesTerm::new(coeff, power as u32));
1489 }
1490 }
1491 } else {
1492 let regular_taylor = taylor(expr, var, center, pos_order)?;
1494 for term in regular_taylor.terms {
1495 series.add_positive_term(term);
1496 }
1497 }
1498 }
1499
1500 Expression::Power(base, exp) => {
1501 if let Expression::Unary(crate::ast::UnaryOp::Neg, inner_exp) = exp.as_ref() {
1503 if is_var_minus_center(base, var, center) {
1504 if let Some(n) = extract_positive_integer(inner_exp) {
1505 if n <= neg_order {
1507 series.add_negative_term(Expression::Integer(1), n);
1508 }
1509 return Ok(series);
1510 }
1511 }
1512 }
1513
1514 let regular_taylor = taylor(expr, var, center, pos_order)?;
1516 for term in regular_taylor.terms {
1517 series.add_positive_term(term);
1518 }
1519 }
1520
1521 _ => {
1522 let regular_taylor = taylor(expr, var, center, pos_order)?;
1524 for term in regular_taylor.terms {
1525 series.add_positive_term(term);
1526 }
1527 }
1528 }
1529
1530 Ok(series)
1531}
1532
1533pub fn residue(expr: &Expression, var: &Variable, pole: &Expression) -> SeriesResult<Expression> {
1556 let laurent_series = laurent(expr, var, pole, 5, 0)?;
1558 Ok(laurent_series.residue())
1559}
1560
1561pub fn pole_order(expr: &Expression, var: &Variable, point: &Expression) -> SeriesResult<u32> {
1565 let laurent_series = laurent(expr, var, point, 10, 0)?;
1566
1567 let mut max_order = 0;
1569 for term in &laurent_series.negative_terms {
1570 if term.power > max_order {
1571 max_order = term.power;
1572 }
1573 }
1574
1575 Ok(max_order)
1576}
1577
1578pub fn find_singularities(expr: &Expression, var: &Variable) -> Vec<Singularity> {
1582 let mut singularities = Vec::new();
1583
1584 match expr {
1585 Expression::Binary(BinaryOp::Div, _num, denom) => {
1586 let zeros = find_zeros_of_expression(denom, var);
1588
1589 for zero in zeros {
1590 let order = get_zero_multiplicity(denom, var, &zero);
1592 let sing_type = if order > 0 {
1593 SingularityType::Pole(order)
1594 } else {
1595 SingularityType::Removable
1596 };
1597
1598 singularities.push(Singularity::new(zero, sing_type));
1599 }
1600 }
1601 _ => {
1602 }
1605 }
1606
1607 singularities
1608}
1609
1610fn is_power_of_var(expr: &Expression, var: &Variable, center: &Expression) -> bool {
1613 match expr {
1614 Expression::Variable(v) if v.name == var.name => {
1615 matches!(center, Expression::Integer(0))
1616 }
1617 Expression::Power(base, _) => is_var_minus_center(base, var, center),
1618 Expression::Binary(BinaryOp::Sub, left, right) => {
1619 matches!(left.as_ref(), Expression::Variable(v) if v.name == var.name)
1620 && expressions_equal(right, center)
1621 }
1622 _ => false,
1623 }
1624}
1625
1626fn is_var_minus_center(expr: &Expression, var: &Variable, center: &Expression) -> bool {
1627 match expr {
1628 Expression::Variable(v) if v.name == var.name => {
1629 matches!(center, Expression::Integer(0))
1630 }
1631 Expression::Binary(BinaryOp::Sub, left, right) => {
1632 matches!(left.as_ref(), Expression::Variable(v) if v.name == var.name)
1633 && expressions_equal(right, center)
1634 }
1635 _ => false,
1636 }
1637}
1638
1639fn get_var_power(expr: &Expression, var: &Variable) -> i32 {
1640 match expr {
1641 Expression::Variable(v) if v.name == var.name => 1,
1642 Expression::Power(base, exp) => {
1643 if let Expression::Variable(v) = base.as_ref() {
1644 if v.name == var.name {
1645 if let Some(n) = extract_integer(exp) {
1646 return n as i32;
1647 }
1648 }
1649 }
1650 1
1651 }
1652 Expression::Binary(BinaryOp::Sub, left, _) => {
1653 if let Expression::Variable(v) = left.as_ref() {
1654 if v.name == var.name {
1655 return 1;
1656 }
1657 }
1658 0
1659 }
1660 _ => 0,
1661 }
1662}
1663
1664fn extract_integer(expr: &Expression) -> Option<i64> {
1665 match expr {
1666 Expression::Integer(n) => Some(*n),
1667 Expression::Float(f) if f.fract() == 0.0 => Some(*f as i64),
1668 _ => None,
1669 }
1670}
1671
1672fn extract_positive_integer(expr: &Expression) -> Option<u32> {
1673 extract_integer(expr).and_then(|n| if n > 0 { Some(n as u32) } else { None })
1674}
1675
1676fn expressions_equal(a: &Expression, b: &Expression) -> bool {
1677 match (a, b) {
1679 (Expression::Integer(x), Expression::Integer(y)) => x == y,
1680 (Expression::Float(x), Expression::Float(y)) => (x - y).abs() < 1e-15,
1681 (Expression::Integer(x), Expression::Float(y))
1682 | (Expression::Float(y), Expression::Integer(x)) => (*x as f64 - y).abs() < 1e-15,
1683 (Expression::Variable(v1), Expression::Variable(v2)) => v1.name == v2.name,
1684 _ => false,
1685 }
1686}
1687
1688fn find_leading_power(series: &Series) -> u32 {
1689 series
1690 .terms
1691 .iter()
1692 .filter(|t| !t.is_zero())
1693 .map(|t| t.power)
1694 .min()
1695 .unwrap_or(0)
1696}
1697
1698fn divide_series(
1699 num: &Series,
1700 denom: &Series,
1701 neg_order: u32,
1702 pos_order: u32,
1703) -> SeriesResult<Vec<(i32, Expression)>> {
1704 let mut result = Vec::new();
1708 let denom_lead_power = find_leading_power(denom) as i32;
1709 let denom_lead_coeff = denom
1710 .terms
1711 .iter()
1712 .find(|t| t.power as i32 == denom_lead_power)
1713 .map(|t| &t.coefficient);
1714
1715 if denom_lead_coeff.is_none() {
1716 return Err(SeriesError::DivisionByZero);
1717 }
1718
1719 for num_term in &num.terms {
1723 let result_power = num_term.power as i32 - denom_lead_power;
1724
1725 if result_power >= -(neg_order as i32) && result_power <= pos_order as i32 {
1726 let coeff = match denom_lead_coeff {
1728 Some(Expression::Integer(d)) if *d != 0 => match &num_term.coefficient {
1729 Expression::Integer(n) => {
1730 Expression::Rational(num_rational::Rational64::new(*n, *d))
1731 }
1732 Expression::Rational(r) => {
1733 Expression::Rational(*r / num_rational::Rational64::from(*d))
1734 }
1735 other => Expression::Binary(
1736 BinaryOp::Div,
1737 Box::new(other.clone()),
1738 Box::new(Expression::Integer(*d)),
1739 ),
1740 },
1741 _ => num_term.coefficient.clone(),
1742 };
1743
1744 result.push((result_power, coeff));
1745 }
1746 }
1747
1748 Ok(result)
1749}
1750
1751fn find_zeros_of_expression(expr: &Expression, var: &Variable) -> Vec<Expression> {
1752 let mut zeros = Vec::new();
1754
1755 match expr {
1756 Expression::Variable(v) if v.name == var.name => {
1757 zeros.push(Expression::Integer(0));
1758 }
1759 Expression::Binary(BinaryOp::Sub, left, right) => {
1760 if matches!(left.as_ref(), Expression::Variable(v) if v.name == var.name) {
1761 zeros.push(right.as_ref().clone());
1762 }
1763 }
1764 Expression::Binary(BinaryOp::Mul, left, right) => {
1765 zeros.extend(find_zeros_of_expression(left, var));
1766 zeros.extend(find_zeros_of_expression(right, var));
1767 }
1768 Expression::Power(base, _) => {
1769 zeros.extend(find_zeros_of_expression(base, var));
1770 }
1771 _ => {}
1772 }
1773
1774 zeros
1775}
1776
1777fn get_zero_multiplicity(expr: &Expression, var: &Variable, zero: &Expression) -> u32 {
1778 match expr {
1779 Expression::Power(base, exp) => {
1780 let base_zeros = find_zeros_of_expression(base, var);
1781 if base_zeros.iter().any(|z| expressions_equal(z, zero)) {
1782 extract_integer(exp).unwrap_or(1) as u32
1783 } else {
1784 0
1785 }
1786 }
1787 Expression::Binary(BinaryOp::Mul, left, right) => {
1788 get_zero_multiplicity(left, var, zero) + get_zero_multiplicity(right, var, zero)
1789 }
1790 _ => {
1791 let zeros = find_zeros_of_expression(expr, var);
1792 if zeros.iter().any(|z| expressions_equal(z, zero)) {
1793 1
1794 } else {
1795 0
1796 }
1797 }
1798 }
1799}
1800
1801#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1803pub enum AsymptoticDirection {
1804 PosInfinity,
1806 NegInfinity,
1808 Zero,
1810}
1811
1812impl fmt::Display for AsymptoticDirection {
1813 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1814 match self {
1815 AsymptoticDirection::PosInfinity => write!(f, "x→+∞"),
1816 AsymptoticDirection::NegInfinity => write!(f, "x→-∞"),
1817 AsymptoticDirection::Zero => write!(f, "x→0"),
1818 }
1819 }
1820}
1821
1822#[derive(Debug, Clone, PartialEq)]
1825pub struct AsymptoticTerm {
1826 pub coefficient: Expression,
1828 pub exponent: Expression,
1830}
1831
1832impl AsymptoticTerm {
1833 pub fn new(coefficient: Expression, exponent: Expression) -> Self {
1835 AsymptoticTerm {
1836 coefficient,
1837 exponent,
1838 }
1839 }
1840
1841 pub fn is_zero(&self) -> bool {
1843 matches!(&self.coefficient, Expression::Integer(0))
1844 || matches!(&self.coefficient, Expression::Float(x) if x.abs() < 1e-15)
1845 }
1846
1847 pub fn evaluate(&self, var: &Variable, point: f64) -> Option<f64> {
1849 let point_expr = Expression::Float(point);
1851
1852 let coeff_result = evaluate_at(&self.coefficient, var, &point_expr).ok()?;
1853 let exp_result = evaluate_at(&self.exponent, var, &point_expr).ok()?;
1854
1855 let coeff = try_expr_to_f64(&coeff_result)?;
1856 let exp = try_expr_to_f64(&exp_result)?;
1857
1858 Some(coeff * point.powf(exp))
1859 }
1860}
1861
1862impl fmt::Display for AsymptoticTerm {
1863 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1864 match &self.exponent {
1866 Expression::Integer(0) => write!(f, "{}", self.coefficient),
1867 Expression::Integer(1) => write!(f, "{}·x", self.coefficient),
1868 Expression::Integer(n) if *n < 0 => {
1869 write!(f, "{}/x^{}", self.coefficient, -n)
1870 }
1871 Expression::Rational(r) => {
1872 write!(f, "{}·x^({})", self.coefficient, r)
1873 }
1874 _ => write!(f, "{}·x^({})", self.coefficient, self.exponent),
1875 }
1876 }
1877}
1878
1879#[derive(Debug, Clone, PartialEq)]
1881pub struct BigO {
1882 pub order: Expression,
1884 pub variable: Variable,
1886}
1887
1888impl BigO {
1889 pub fn new(order: Expression, variable: Variable) -> Self {
1891 BigO { order, variable }
1892 }
1893
1894 pub fn is_same_order(&self, other: &BigO) -> bool {
1896 self.order == other.order
1898 }
1899
1900 pub fn is_smaller_order(&self, other: &BigO) -> bool {
1903 match (&self.order, &other.order) {
1905 (Expression::Power(_, exp1), Expression::Power(_, exp2)) => {
1906 if let (Some(e1), Some(e2)) = (try_expr_to_f64(exp1), try_expr_to_f64(exp2)) {
1907 return e1 < e2;
1908 }
1909 }
1910 (Expression::Integer(a), Expression::Power(_, _)) => {
1911 return *a == 1;
1913 }
1914 _ => {}
1915 }
1916 false
1917 }
1918
1919 pub fn is_bounded_by(&self, expr: &Expression) -> bool {
1921 expr == &self.order
1923 }
1924}
1925
1926impl fmt::Display for BigO {
1927 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1928 write!(f, "O({})", self.order)
1929 }
1930}
1931
1932#[derive(Debug, Clone, PartialEq)]
1934pub struct AsymptoticSeries {
1935 pub terms: Vec<AsymptoticTerm>,
1937 pub variable: Variable,
1939 pub direction: AsymptoticDirection,
1941}
1942
1943impl AsymptoticSeries {
1944 pub fn new(variable: Variable, direction: AsymptoticDirection) -> Self {
1946 AsymptoticSeries {
1947 terms: Vec::new(),
1948 variable,
1949 direction,
1950 }
1951 }
1952
1953 pub fn add_term(&mut self, term: AsymptoticTerm) {
1955 if !term.is_zero() {
1956 self.terms.push(term);
1957 }
1958 }
1959
1960 pub fn dominant_term(&self) -> Option<&AsymptoticTerm> {
1962 self.terms.first()
1963 }
1964
1965 pub fn order_of_magnitude(&self) -> Option<Expression> {
1967 self.dominant_term().map(|t| t.exponent.clone())
1968 }
1969
1970 pub fn with_error_term(&self) -> (Self, BigO) {
1972 let series = self.clone();
1973
1974 let error_order = if let Some(last_term) = self.terms.last() {
1976 match self.direction {
1979 AsymptoticDirection::PosInfinity | AsymptoticDirection::NegInfinity => {
1980 Expression::Binary(
1981 BinaryOp::Sub,
1982 Box::new(last_term.exponent.clone()),
1983 Box::new(Expression::Integer(1)),
1984 )
1985 }
1986 AsymptoticDirection::Zero => Expression::Binary(
1987 BinaryOp::Add,
1988 Box::new(last_term.exponent.clone()),
1989 Box::new(Expression::Integer(1)),
1990 ),
1991 }
1992 } else {
1993 Expression::Integer(0)
1994 };
1995
1996 let var_expr = Expression::Variable(self.variable.clone());
1997 let big_o = BigO::new(
1998 Expression::Power(Box::new(var_expr), Box::new(error_order)),
1999 self.variable.clone(),
2000 );
2001
2002 (series, big_o)
2003 }
2004
2005 pub fn evaluate(&self, point: f64) -> Option<f64> {
2007 let mut sum = 0.0;
2008 for term in &self.terms {
2009 sum += term.evaluate(&self.variable, point)?;
2010 }
2011 Some(sum)
2012 }
2013
2014 pub fn to_expression(&self) -> Expression {
2016 if self.terms.is_empty() {
2017 return Expression::Integer(0);
2018 }
2019
2020 let var_expr = Expression::Variable(self.variable.clone());
2021 let mut result: Option<Expression> = None;
2022
2023 for term in &self.terms {
2024 let term_expr = Expression::Binary(
2025 BinaryOp::Mul,
2026 Box::new(term.coefficient.clone()),
2027 Box::new(Expression::Power(
2028 Box::new(var_expr.clone()),
2029 Box::new(term.exponent.clone()),
2030 )),
2031 );
2032
2033 result = Some(match result {
2034 None => term_expr,
2035 Some(acc) => Expression::Binary(BinaryOp::Add, Box::new(acc), Box::new(term_expr)),
2036 });
2037 }
2038
2039 result.unwrap_or(Expression::Integer(0)).simplify()
2040 }
2041}
2042
2043impl fmt::Display for AsymptoticSeries {
2044 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2045 if self.terms.is_empty() {
2046 return write!(f, "0");
2047 }
2048
2049 write!(f, "As {}: ", self.direction)?;
2050
2051 for (i, term) in self.terms.iter().enumerate() {
2052 if i > 0 {
2053 write!(f, " + ")?;
2054 }
2055 write!(f, "{}", term)?;
2056 }
2057
2058 Ok(())
2059 }
2060}
2061
2062pub fn asymptotic(
2091 expr: &Expression,
2092 var: impl AsRef<str>,
2093 direction: AsymptoticDirection,
2094 num_terms: u32,
2095) -> SeriesResult<AsymptoticSeries> {
2096 let var_name = var.as_ref();
2097 let variable = Variable::new(var_name);
2098
2099 let mut series = AsymptoticSeries::new(variable.clone(), direction);
2100
2101 extract_asymptotic_terms(expr, &variable, &mut series, num_terms)?;
2104
2105 sort_by_dominance(&mut series.terms, direction);
2107
2108 Ok(series)
2109}
2110
2111fn substitute_for_infinity(expr: &Expression, var: &Variable) -> Expression {
2113 match expr {
2114 Expression::Variable(v) if v == var => {
2115 let t = Variable::new("__t");
2117 Expression::Binary(
2118 BinaryOp::Div,
2119 Box::new(Expression::Integer(1)),
2120 Box::new(Expression::Variable(t)),
2121 )
2122 }
2123 Expression::Binary(op, left, right) => Expression::Binary(
2124 *op,
2125 Box::new(substitute_for_infinity(left, var)),
2126 Box::new(substitute_for_infinity(right, var)),
2127 ),
2128 Expression::Unary(op, inner) => {
2129 Expression::Unary(*op, Box::new(substitute_for_infinity(inner, var)))
2130 }
2131 Expression::Power(base, exp) => Expression::Power(
2132 Box::new(substitute_for_infinity(base, var)),
2133 Box::new(substitute_for_infinity(exp, var)),
2134 ),
2135 Expression::Function(f, args) => Expression::Function(
2136 f.clone(),
2137 args.iter()
2138 .map(|a| substitute_for_infinity(a, var))
2139 .collect(),
2140 ),
2141 _ => expr.clone(),
2142 }
2143}
2144
2145fn extract_asymptotic_terms(
2147 expr: &Expression,
2148 var: &Variable,
2149 series: &mut AsymptoticSeries,
2150 num_terms: u32,
2151) -> SeriesResult<()> {
2152 match expr {
2154 Expression::Binary(BinaryOp::Add, left, right) => {
2155 extract_asymptotic_terms(left, var, series, num_terms)?;
2156 extract_asymptotic_terms(right, var, series, num_terms)?;
2157 }
2158 Expression::Binary(BinaryOp::Div, num, den) => {
2159 let is_one = matches!(num.as_ref(), Expression::Integer(1))
2161 || matches!(num.as_ref(), Expression::Float(f) if (*f - 1.0).abs() < 1e-15);
2162
2163 if is_one {
2164 if let Expression::Variable(v) = den.as_ref() {
2166 if v == var {
2167 series.add_term(AsymptoticTerm::new(
2168 Expression::Integer(1),
2169 Expression::Integer(-1),
2170 ));
2171 }
2172 } else if let Expression::Power(base, exp) = den.as_ref() {
2174 if let Expression::Variable(v) = base.as_ref() {
2175 if v == var {
2176 let neg_exp = Expression::Unary(
2178 crate::ast::UnaryOp::Neg,
2179 Box::new(exp.as_ref().clone()),
2180 )
2181 .simplify();
2182 series.add_term(AsymptoticTerm::new(Expression::Integer(1), neg_exp));
2183 }
2184 }
2185 }
2186 } else {
2187 if let Expression::Variable(v) = den.as_ref() {
2189 if v == var {
2190 series.add_term(AsymptoticTerm::new(
2191 num.as_ref().clone(),
2192 Expression::Integer(-1),
2193 ));
2194 }
2195 } else if let Expression::Power(base, exp) = den.as_ref() {
2196 if let Expression::Variable(v) = base.as_ref() {
2197 if v == var {
2198 let neg_exp = Expression::Unary(
2199 crate::ast::UnaryOp::Neg,
2200 Box::new(exp.as_ref().clone()),
2201 )
2202 .simplify();
2203 series.add_term(AsymptoticTerm::new(num.as_ref().clone(), neg_exp));
2204 }
2205 }
2206 }
2207 }
2208 }
2209 Expression::Power(base, exp) => {
2210 if let Expression::Variable(v) = base.as_ref() {
2211 if v == var {
2212 series.add_term(AsymptoticTerm::new(
2213 Expression::Integer(1),
2214 exp.as_ref().clone(),
2215 ));
2216 }
2217 }
2218 }
2219 Expression::Variable(v) if v == var => {
2220 series.add_term(AsymptoticTerm::new(
2221 Expression::Integer(1),
2222 Expression::Integer(1),
2223 ));
2224 }
2225 Expression::Integer(n) => {
2226 series.add_term(AsymptoticTerm::new(
2227 Expression::Integer(*n),
2228 Expression::Integer(0),
2229 ));
2230 }
2231 Expression::Float(f) => {
2232 series.add_term(AsymptoticTerm::new(
2233 Expression::Float(*f),
2234 Expression::Integer(0),
2235 ));
2236 }
2237 _ => {
2238 return Err(SeriesError::CannotExpand(format!(
2239 "Cannot extract asymptotic terms from: {}",
2240 expr
2241 )));
2242 }
2243 }
2244
2245 Ok(())
2246}
2247
2248fn sort_by_dominance(terms: &mut Vec<AsymptoticTerm>, direction: AsymptoticDirection) {
2250 terms.sort_by(|a, b| {
2251 let exp_a = try_expr_to_f64(&a.exponent).unwrap_or(0.0);
2252 let exp_b = try_expr_to_f64(&b.exponent).unwrap_or(0.0);
2253
2254 match direction {
2255 AsymptoticDirection::PosInfinity | AsymptoticDirection::NegInfinity => {
2256 exp_b.partial_cmp(&exp_a).unwrap()
2258 }
2259 AsymptoticDirection::Zero => {
2260 exp_a.partial_cmp(&exp_b).unwrap()
2262 }
2263 }
2264 });
2265}
2266
2267pub fn limit_via_asymptotic(
2282 expr: &Expression,
2283 var: impl AsRef<str>,
2284 direction: AsymptoticDirection,
2285) -> Result<crate::limits::LimitResult, crate::limits::LimitError> {
2286 use crate::limits::LimitResult;
2287
2288 let series = asymptotic(expr, var.as_ref(), direction, 5)
2289 .map_err(|e| crate::limits::LimitError::EvaluationError(e.to_string()))?;
2290
2291 if let Some(dominant) = series.dominant_term() {
2292 if let Some(exp) = try_expr_to_f64(&dominant.exponent) {
2294 match direction {
2295 AsymptoticDirection::PosInfinity | AsymptoticDirection::NegInfinity => {
2296 if exp > 0.0 {
2297 if let Some(coeff) = try_expr_to_f64(&dominant.coefficient) {
2299 if coeff > 0.0 {
2300 return Ok(LimitResult::PositiveInfinity);
2301 } else if coeff < 0.0 {
2302 return Ok(LimitResult::NegativeInfinity);
2303 }
2304 }
2305 } else if exp < 0.0 {
2306 return Ok(LimitResult::Value(0.0));
2308 } else {
2309 if let Some(coeff) = try_expr_to_f64(&dominant.coefficient) {
2311 return Ok(LimitResult::Value(coeff));
2312 }
2313 }
2314 }
2315 AsymptoticDirection::Zero => {
2316 if exp > 0.0 {
2317 return Ok(LimitResult::Value(0.0));
2319 } else if exp < 0.0 {
2320 if let Some(coeff) = try_expr_to_f64(&dominant.coefficient) {
2322 if coeff > 0.0 {
2323 return Ok(LimitResult::PositiveInfinity);
2324 } else if coeff < 0.0 {
2325 return Ok(LimitResult::NegativeInfinity);
2326 }
2327 }
2328 } else {
2329 if let Some(coeff) = try_expr_to_f64(&dominant.coefficient) {
2331 return Ok(LimitResult::Value(coeff));
2332 }
2333 }
2334 }
2335 }
2336 }
2337 }
2338
2339 Err(crate::limits::LimitError::EvaluationError(
2340 "Cannot determine limit from asymptotic expansion".to_string(),
2341 ))
2342}
2343
2344#[cfg(test)]
2345mod tests {
2346 use super::*;
2347
2348 #[test]
2349 fn test_factorial() {
2350 assert_eq!(factorial(0), 1);
2351 assert_eq!(factorial(1), 1);
2352 assert_eq!(factorial(5), 120);
2353 assert_eq!(factorial(10), 3628800);
2354 }
2355
2356 #[test]
2357 fn test_exp_series() {
2358 let x = Variable::new("x");
2359 let series = exp_series(&x, 4);
2360
2361 assert_eq!(series.term_count(), 5);
2362 assert_eq!(series.order, 4);
2363
2364 let term0 = series.get_term(0).unwrap();
2366 assert!(matches!(&term0.coefficient, Expression::Integer(1)));
2367
2368 let term2 = series.get_term(2).unwrap();
2369 if let Expression::Rational(r) = &term2.coefficient {
2370 assert_eq!(*r.numer(), 1);
2371 assert_eq!(*r.denom(), 2);
2372 } else {
2373 panic!("Expected rational coefficient");
2374 }
2375 }
2376
2377 #[test]
2378 fn test_sin_series() {
2379 let x = Variable::new("x");
2380 let series = sin_series(&x, 7);
2381
2382 assert!(series.get_term(0).is_none());
2384 assert!(series.get_term(1).is_some());
2385 assert!(series.get_term(2).is_none());
2386 assert!(series.get_term(3).is_some());
2387
2388 let term1 = series.get_term(1).unwrap();
2390 if let Expression::Rational(r) = &term1.coefficient {
2391 assert_eq!(*r.numer(), 1);
2392 assert_eq!(*r.denom(), 1);
2393 }
2394
2395 let term3 = series.get_term(3).unwrap();
2397 if let Expression::Rational(r) = &term3.coefficient {
2398 assert_eq!(*r.numer(), -1);
2399 assert_eq!(*r.denom(), 6);
2400 }
2401 }
2402
2403 #[test]
2404 fn test_cos_series() {
2405 let x = Variable::new("x");
2406 let series = cos_series(&x, 6);
2407
2408 assert!(series.get_term(0).is_some());
2410 assert!(series.get_term(1).is_none());
2411 assert!(series.get_term(2).is_some());
2412
2413 let term0 = series.get_term(0).unwrap();
2415 if let Expression::Rational(r) = &term0.coefficient {
2416 assert_eq!(*r.numer(), 1);
2417 }
2418
2419 let term2 = series.get_term(2).unwrap();
2421 if let Expression::Rational(r) = &term2.coefficient {
2422 assert_eq!(*r.numer(), -1);
2423 assert_eq!(*r.denom(), 2);
2424 }
2425 }
2426
2427 #[test]
2428 fn test_series_evaluate() {
2429 let x = Variable::new("x");
2430 let series = exp_series(&x, 10);
2431
2432 let result = series.evaluate(0.1).unwrap();
2434 let expected = 0.1_f64.exp();
2435 assert!((result - expected).abs() < 1e-8);
2436 }
2437
2438 #[test]
2439 fn test_sin_series_evaluate() {
2440 let x = Variable::new("x");
2441 let series = sin_series(&x, 11);
2442
2443 let result = series.evaluate(0.5).unwrap();
2445 let expected = 0.5_f64.sin();
2446 assert!((result - expected).abs() < 1e-8);
2447 }
2448
2449 #[test]
2450 fn test_series_to_expression() {
2451 let x = Variable::new("x");
2452 let series = exp_series(&x, 3);
2453 let expr = series.to_expression();
2454
2455 let mut vars = HashMap::new();
2457 vars.insert("x".to_string(), 0.1);
2458 let result = expr.evaluate(&vars).unwrap();
2459 let expected = 1.0 + 0.1 + 0.01 / 2.0 + 0.001 / 6.0;
2460 assert!((result - expected).abs() < 1e-10);
2461 }
2462
2463 #[test]
2464 fn test_series_to_latex() {
2465 let x = Variable::new("x");
2466 let series = sin_series(&x, 5);
2467 let latex = series.to_latex();
2468
2469 assert!(latex.contains("x"));
2471 assert!(latex.contains("x^{3}") || latex.contains("x³"));
2472 }
2473
2474 #[test]
2475 fn test_ln_1_plus_x_series() {
2476 let x = Variable::new("x");
2477 let series = ln_1_plus_x_series(&x, 5);
2478
2479 assert!(series.get_term(0).is_none());
2481 assert!(series.get_term(1).is_some());
2482
2483 let term1 = series.get_term(1).unwrap();
2485 if let Expression::Rational(r) = &term1.coefficient {
2486 assert_eq!(*r.numer(), 1);
2487 assert_eq!(*r.denom(), 1);
2488 }
2489
2490 let term2 = series.get_term(2).unwrap();
2492 if let Expression::Rational(r) = &term2.coefficient {
2493 assert_eq!(*r.numer(), -1);
2494 assert_eq!(*r.denom(), 2);
2495 }
2496 }
2497
2498 #[test]
2499 fn test_arctan_series() {
2500 let x = Variable::new("x");
2501 let series = arctan_series(&x, 11);
2502
2503 let result = series.evaluate(0.3).unwrap();
2505 let expected = 0.3_f64.atan();
2506 assert!((result - expected).abs() < 1e-4);
2507 }
2508
2509 #[test]
2510 fn test_binomial_series() {
2511 let x = Variable::new("x");
2512
2513 let series = binomial_series(&Expression::Integer(2), &x, 5).unwrap();
2515 assert_eq!(series.term_count(), 3);
2516
2517 let series = binomial_series(&Expression::Float(0.5), &x, 5).unwrap();
2519 let result = series.evaluate(0.21).unwrap(); assert!((result - 1.1).abs() < 0.01);
2521 }
2522
2523 #[test]
2524 fn test_taylor_polynomial() {
2525 let x = Variable::new("x");
2526 let expr = Expression::Power(
2528 Box::new(Expression::Variable(x.clone())),
2529 Box::new(Expression::Integer(2)),
2530 );
2531
2532 let series = taylor(&expr, &x, &Expression::Integer(0), 3).unwrap();
2533
2534 let term2 = series.get_term(2);
2536 assert!(term2.is_some());
2537 }
2538
2539 #[test]
2540 fn test_maclaurin_exp() {
2541 let x = Variable::new("x");
2542 let expr = Expression::Function(Function::Exp, vec![Expression::Variable(x.clone())]);
2543
2544 let series = maclaurin(&expr, &x, 5).unwrap();
2545
2546 let expected = exp_series(&x, 5);
2548 assert_eq!(series.term_count(), expected.term_count());
2549 }
2550
2551 #[test]
2554 fn test_singularity_type_display() {
2555 let pole = SingularityType::Pole(2);
2556 assert_eq!(format!("{}", pole), "pole of order 2");
2557
2558 let removable = SingularityType::Removable;
2559 assert_eq!(format!("{}", removable), "removable singularity");
2560
2561 let essential = SingularityType::Essential;
2562 assert_eq!(format!("{}", essential), "essential singularity");
2563 }
2564
2565 #[test]
2566 fn test_laurent_series_creation() {
2567 let z = Variable::new("z");
2568 let center = Expression::Integer(0);
2569
2570 let mut laurent = LaurentSeries::new(z.clone(), center, 1, 1);
2572
2573 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(1), 0));
2575 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(2), 1));
2576
2577 laurent.add_negative_term(Expression::Integer(3), 1);
2579
2580 assert_eq!(laurent.principal_part_order, 1);
2581 assert_eq!(laurent.analytic_part_order, 1);
2582 assert_eq!(laurent.positive_terms.len(), 2);
2583 assert_eq!(laurent.negative_terms.len(), 1);
2584 }
2585
2586 #[test]
2587 fn test_laurent_series_residue() {
2588 let z = Variable::new("z");
2589
2590 let mut laurent = LaurentSeries::new(z.clone(), Expression::Integer(0), 1, 1);
2592 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(2), 0));
2593 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(3), 1));
2594 laurent.add_negative_term(Expression::Integer(1), 1);
2595
2596 let res = laurent.residue();
2597 if let Expression::Integer(n) = res {
2598 assert_eq!(n, 1);
2599 } else {
2600 panic!("Expected integer residue, got {:?}", res);
2601 }
2602 }
2603
2604 #[test]
2605 fn test_laurent_series_pole_order() {
2606 let z = Variable::new("z");
2607
2608 let mut laurent = LaurentSeries::new(z.clone(), Expression::Integer(0), 3, 0);
2610 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(1), 0));
2611 laurent.add_negative_term(Expression::Integer(1), 3);
2612 laurent.add_negative_term(Expression::Integer(2), 1);
2613
2614 assert_eq!(laurent.principal_part_order, 3);
2616 }
2617
2618 #[test]
2619 fn test_laurent_series_principal_part() {
2620 let z = Variable::new("z");
2621
2622 let mut laurent = LaurentSeries::new(z.clone(), Expression::Integer(0), 2, 1);
2624 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(1), 0));
2625 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(1), 1));
2626 laurent.add_negative_term(Expression::Integer(2), 2);
2627 laurent.add_negative_term(Expression::Integer(3), 1);
2628
2629 let principal = laurent.principal_part();
2630 assert_eq!(principal.negative_terms.len(), 2);
2631 }
2632
2633 #[test]
2634 fn test_laurent_series_analytic_part() {
2635 let z = Variable::new("z");
2636
2637 let mut laurent = LaurentSeries::new(z.clone(), Expression::Integer(0), 1, 2);
2639 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(2), 0));
2640 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(3), 1));
2641 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(4), 2));
2642 laurent.add_negative_term(Expression::Integer(1), 1);
2643
2644 let analytic = laurent.analytic_part();
2645 assert_eq!(analytic.term_count(), 3);
2646 }
2647
2648 #[test]
2649 fn test_laurent_series_evaluate() {
2650 let z = Variable::new("z");
2651
2652 let mut laurent = LaurentSeries::new(z.clone(), Expression::Integer(0), 1, 1);
2655 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(1), 0));
2656 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(1), 1));
2657 laurent.add_negative_term(Expression::Integer(1), 1);
2658
2659 let result = laurent.evaluate(2.0);
2660 assert!(result.is_some());
2661 assert!((result.unwrap() - 3.5).abs() < 1e-10);
2662 }
2663
2664 #[test]
2665 fn test_laurent_series_evaluate_at_singularity() {
2666 let z = Variable::new("z");
2667
2668 let mut laurent = LaurentSeries::new(z.clone(), Expression::Integer(0), 1, 0);
2670 laurent.add_negative_term(Expression::Integer(1), 1);
2671
2672 let result = laurent.evaluate(0.0);
2674 assert!(result.is_none());
2675 }
2676
2677 #[test]
2678 fn test_laurent_series_to_latex() {
2679 let z = Variable::new("z");
2680
2681 let mut laurent = LaurentSeries::new(z.clone(), Expression::Integer(0), 1, 1);
2683 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(1), 0));
2684 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(3), 1));
2685 laurent.add_negative_term(Expression::Integer(2), 1);
2686
2687 let latex = laurent.to_latex();
2688 assert!(latex.contains("z^{-1}"));
2690 }
2691
2692 #[test]
2693 fn test_laurent_is_taylor() {
2694 let z = Variable::new("z");
2695
2696 let mut taylor_like = LaurentSeries::new(z.clone(), Expression::Integer(0), 0, 2);
2698 taylor_like.add_positive_term(SeriesTerm::new(Expression::Integer(1), 0));
2699 taylor_like.add_positive_term(SeriesTerm::new(Expression::Integer(2), 1));
2700
2701 assert!(taylor_like.is_taylor());
2702
2703 let mut laurent = LaurentSeries::new(z.clone(), Expression::Integer(0), 1, 2);
2705 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(1), 0));
2706 laurent.add_negative_term(Expression::Integer(1), 1);
2707
2708 assert!(!laurent.is_taylor());
2709 }
2710
2711 #[test]
2712 fn test_singularity_creation() {
2713 let location = Expression::Integer(0);
2714 let singularity = Singularity {
2715 location: location.clone(),
2716 singularity_type: SingularityType::Pole(2),
2717 };
2718
2719 assert!(matches!(
2720 singularity.singularity_type,
2721 SingularityType::Pole(2)
2722 ));
2723 }
2724
2725 #[test]
2726 fn test_find_singularities_simple_pole() {
2727 let z = Variable::new("z");
2728
2729 let expr = Expression::Binary(
2731 BinaryOp::Div,
2732 Box::new(Expression::Integer(1)),
2733 Box::new(Expression::Variable(z.clone())),
2734 );
2735
2736 let singularities = find_singularities(&expr, &z);
2737
2738 assert!(!singularities.is_empty());
2740 }
2741
2742 #[test]
2743 fn test_pole_order_simple() {
2744 let z = Variable::new("z");
2745
2746 let expr = Expression::Binary(
2748 BinaryOp::Div,
2749 Box::new(Expression::Integer(1)),
2750 Box::new(Expression::Variable(z.clone())),
2751 );
2752
2753 let order = pole_order(&expr, &z, &Expression::Integer(0));
2754 assert!(order.is_ok());
2755 assert_eq!(order.unwrap(), 1);
2756 }
2757
2758 #[test]
2759 fn test_pole_order_double() {
2760 let z = Variable::new("z");
2761
2762 let z_squared = Expression::Power(
2764 Box::new(Expression::Variable(z.clone())),
2765 Box::new(Expression::Integer(2)),
2766 );
2767 let expr = Expression::Binary(
2768 BinaryOp::Div,
2769 Box::new(Expression::Integer(1)),
2770 Box::new(z_squared),
2771 );
2772
2773 let order = pole_order(&expr, &z, &Expression::Integer(0));
2774 assert!(order.is_ok());
2775 assert_eq!(order.unwrap(), 2);
2776 }
2777
2778 #[test]
2779 fn test_residue_simple_pole() {
2780 let z = Variable::new("z");
2781
2782 let expr = Expression::Binary(
2784 BinaryOp::Div,
2785 Box::new(Expression::Integer(1)),
2786 Box::new(Expression::Variable(z.clone())),
2787 );
2788
2789 let res = residue(&expr, &z, &Expression::Integer(0));
2790 assert!(res.is_ok());
2791 let res_val = res.unwrap().simplify();
2793 if let Expression::Integer(n) = res_val {
2794 assert_eq!(n, 1);
2795 } else if let Expression::Float(f) = res_val {
2796 assert!((f - 1.0).abs() < 1e-10);
2797 }
2798 }
2799
2800 #[test]
2801 fn test_laurent_function_simple() {
2802 let z = Variable::new("z");
2803
2804 let expr = Expression::Binary(
2806 BinaryOp::Div,
2807 Box::new(Expression::Integer(1)),
2808 Box::new(Expression::Variable(z.clone())),
2809 );
2810
2811 let result = laurent(&expr, &z, &Expression::Integer(0), 1, 3);
2813 assert!(result.is_ok());
2814
2815 let laurent_series = result.unwrap();
2816 assert!(!laurent_series.negative_terms.is_empty());
2818 assert_eq!(laurent_series.principal_part_order, 1);
2820 }
2821
2822 #[test]
2823 fn test_laurent_to_taylor() {
2824 let z = Variable::new("z");
2825
2826 let mut laurent = LaurentSeries::new(z.clone(), Expression::Integer(0), 0, 2);
2828 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(1), 0));
2829 laurent.add_positive_term(SeriesTerm::new(Expression::Integer(2), 1));
2830
2831 let taylor_opt = laurent.to_taylor();
2832 assert!(taylor_opt.is_some());
2833
2834 let taylor = taylor_opt.unwrap();
2835 assert_eq!(taylor.term_count(), 2);
2836 }
2837
2838 #[test]
2841 fn test_series_addition() {
2842 let x = Variable::new("x");
2843
2844 let mut s1 = Series::new(x.clone(), Expression::Integer(0), 3);
2846 s1.add_term(SeriesTerm::new(Expression::Integer(1), 0));
2847 s1.add_term(SeriesTerm::new(Expression::Integer(1), 1));
2848 s1.add_term(SeriesTerm::new(Expression::Integer(1), 2));
2849
2850 let mut s2 = Series::new(x.clone(), Expression::Integer(0), 3);
2852 s2.add_term(SeriesTerm::new(Expression::Integer(2), 0));
2853 s2.add_term(SeriesTerm::new(Expression::Integer(3), 1));
2854 s2.add_term(SeriesTerm::new(Expression::Integer(1), 2));
2855
2856 let sum = (s1 + s2).unwrap();
2858
2859 let result = sum.evaluate(0.5).unwrap();
2861 assert!((result - 5.5).abs() < 1e-10);
2862 }
2863
2864 #[test]
2865 fn test_series_subtraction() {
2866 let x = Variable::new("x");
2867
2868 let mut s1 = Series::new(x.clone(), Expression::Integer(0), 2);
2870 s1.add_term(SeriesTerm::new(Expression::Integer(3), 0));
2871 s1.add_term(SeriesTerm::new(Expression::Integer(2), 1));
2872 s1.add_term(SeriesTerm::new(Expression::Integer(1), 2));
2873
2874 let mut s2 = Series::new(x.clone(), Expression::Integer(0), 2);
2876 s2.add_term(SeriesTerm::new(Expression::Integer(1), 0));
2877 s2.add_term(SeriesTerm::new(Expression::Integer(1), 1));
2878 s2.add_term(SeriesTerm::new(Expression::Integer(1), 2));
2879
2880 let diff = (s1 - s2).unwrap();
2882
2883 let result = diff.evaluate(1.0).unwrap();
2885 assert!((result - 3.0).abs() < 1e-10);
2886 }
2887
2888 #[test]
2889 fn test_series_multiplication() {
2890 let x = Variable::new("x");
2891
2892 let mut s1 = Series::new(x.clone(), Expression::Integer(0), 4);
2894 s1.add_term(SeriesTerm::new(Expression::Integer(1), 0));
2895 s1.add_term(SeriesTerm::new(Expression::Integer(1), 1));
2896
2897 let mut s2 = Series::new(x.clone(), Expression::Integer(0), 4);
2898 s2.add_term(SeriesTerm::new(Expression::Integer(1), 0));
2899 s2.add_term(SeriesTerm::new(Expression::Integer(-1), 1));
2900
2901 let product = (s1 * s2).unwrap();
2902
2903 let result = product.evaluate(0.5).unwrap();
2905 assert!((result - 0.75).abs() < 1e-10);
2906 }
2907
2908 #[test]
2909 fn test_series_reciprocal() {
2910 let x = Variable::new("x");
2911
2912 let mut s = Series::new(x.clone(), Expression::Integer(0), 5);
2914 s.add_term(SeriesTerm::new(Expression::Integer(1), 0));
2915 s.add_term(SeriesTerm::new(Expression::Integer(-1), 1));
2916
2917 let recip = s.reciprocal().unwrap();
2918
2919 let result = recip.evaluate(0.5).unwrap();
2922 assert!((result - 1.96875).abs() < 1e-10);
2923 }
2924
2925 #[test]
2926 fn test_series_division() {
2927 let x = Variable::new("x");
2928
2929 let mut num = Series::new(x.clone(), Expression::Integer(0), 4);
2931 num.add_term(SeriesTerm::new(Expression::Integer(1), 0));
2932 num.add_term(SeriesTerm::new(Expression::Integer(-1), 2));
2933
2934 let mut denom = Series::new(x.clone(), Expression::Integer(0), 4);
2935 denom.add_term(SeriesTerm::new(Expression::Integer(1), 0));
2936 denom.add_term(SeriesTerm::new(Expression::Integer(-1), 1));
2937
2938 let quotient = (num / denom).unwrap();
2939
2940 let result = quotient.evaluate(0.5).unwrap();
2942 assert!((result - 1.5).abs() < 0.01);
2943 }
2944
2945 #[test]
2946 fn test_series_differentiate() {
2947 let x = Variable::new("x");
2948
2949 let exp_series = exp_series(&x, 4);
2951 let deriv = exp_series.differentiate();
2952
2953 let result = deriv.evaluate(0.1).unwrap();
2955 let expected = 0.1_f64.exp();
2956 assert!((result - expected).abs() < 0.01);
2957 }
2958
2959 #[test]
2960 fn test_series_integrate() {
2961 let x = Variable::new("x");
2962
2963 let exp_series = exp_series(&x, 3);
2966 let integrated = exp_series.integrate(Expression::Integer(0));
2967
2968 let result = integrated.evaluate(0.1).unwrap();
2970 let expected = 0.1_f64.exp() - 1.0; assert!((result - expected).abs() < 0.01);
2972 }
2973
2974 #[test]
2975 fn test_differentiate_then_integrate() {
2976 let x = Variable::new("x");
2977
2978 let sin_s = sin_series(&x, 7);
2980 let deriv = sin_s.differentiate(); let integrated = deriv.integrate(Expression::Integer(0)); let result = integrated.evaluate(0.5).unwrap();
2985 let expected = 0.5_f64.sin();
2986 assert!((result - expected).abs() < 0.01);
2987 }
2988
2989 #[test]
2990 fn test_exp_times_neg_exp() {
2991 let x = Variable::new("x");
2992
2993 let exp_pos = exp_series(&x, 5);
2995
2996 let mut exp_neg = Series::new(x.clone(), Expression::Integer(0), 5);
2998 for n in 0..=5 {
2999 let sign = if n % 2 == 0 { 1.0 } else { -1.0 };
3000 let coeff = sign / factorial(n) as f64;
3001 exp_neg.add_term(SeriesTerm::new(Expression::Float(coeff), n));
3002 }
3003
3004 let product = (exp_pos * exp_neg).unwrap();
3005
3006 let result = product.evaluate(0.5).unwrap();
3008 assert!((result - 1.0).abs() < 0.01);
3009 }
3010
3011 #[test]
3012 fn test_compose_series_exp_sin() {
3013 let x = Variable::new("x");
3014
3015 let exp_s = exp_series(&x, 5);
3017 let sin_s = sin_series(&x, 5);
3018
3019 let composed = compose_series(&exp_s, &sin_s).unwrap();
3021
3022 let result = composed.evaluate(0.1).unwrap();
3024 let expected = (0.1_f64.sin()).exp();
3025 assert!((result - expected).abs() < 0.01);
3026 }
3027
3028 #[test]
3029 fn test_series_reversion() {
3030 let x = Variable::new("x");
3031
3032 let sin_s = sin_series(&x, 7);
3035 let arcsin_s = reversion(&sin_s).unwrap();
3036
3037 let result = arcsin_s.evaluate(0.5).unwrap();
3039 let expected = 0.5_f64.asin();
3040 assert!((result - expected).abs() < 0.05);
3041 }
3042
3043 #[test]
3046 fn test_asymptotic_direction_display() {
3047 assert_eq!(format!("{}", AsymptoticDirection::PosInfinity), "x→+∞");
3048 assert_eq!(format!("{}", AsymptoticDirection::NegInfinity), "x→-∞");
3049 assert_eq!(format!("{}", AsymptoticDirection::Zero), "x→0");
3050 }
3051
3052 #[test]
3053 fn test_asymptotic_term_creation() {
3054 let term = AsymptoticTerm::new(Expression::Integer(2), Expression::Integer(-1));
3055 assert_eq!(term.coefficient, Expression::Integer(2));
3056 assert_eq!(term.exponent, Expression::Integer(-1));
3057 assert!(!term.is_zero());
3058
3059 let zero_term = AsymptoticTerm::new(Expression::Integer(0), Expression::Integer(1));
3060 assert!(zero_term.is_zero());
3061 }
3062
3063 #[test]
3064 fn test_asymptotic_term_evaluate() {
3065 let x = Variable::new("x");
3066
3067 let term = AsymptoticTerm::new(Expression::Integer(2), Expression::Integer(-1));
3069 let result = term.evaluate(&x, 4.0).unwrap();
3070 assert!((result - 0.5).abs() < 1e-10);
3071
3072 let term2 = AsymptoticTerm::new(Expression::Integer(3), Expression::Integer(2));
3074 let result2 = term2.evaluate(&x, 2.0).unwrap();
3075 assert!((result2 - 12.0).abs() < 1e-10);
3076 }
3077
3078 #[test]
3079 fn test_asymptotic_term_display() {
3080 let term1 = AsymptoticTerm::new(Expression::Integer(2), Expression::Integer(0));
3081 assert_eq!(format!("{}", term1), "2");
3082
3083 let term2 = AsymptoticTerm::new(Expression::Integer(3), Expression::Integer(1));
3084 assert_eq!(format!("{}", term2), "3·x");
3085
3086 let term3 = AsymptoticTerm::new(Expression::Integer(1), Expression::Integer(-2));
3087 assert_eq!(format!("{}", term3), "1/x^2");
3088 }
3089
3090 #[test]
3091 fn test_big_o_creation() {
3092 let x = Variable::new("x");
3093 let order = Expression::Power(
3094 Box::new(Expression::Variable(x.clone())),
3095 Box::new(Expression::Integer(2)),
3096 );
3097 let big_o = BigO::new(order.clone(), x.clone());
3098
3099 assert_eq!(big_o.order, order);
3100 assert_eq!(big_o.variable, x);
3101 }
3102
3103 #[test]
3104 fn test_big_o_is_same_order() {
3105 let x = Variable::new("x");
3106 let order1 = Expression::Power(
3107 Box::new(Expression::Variable(x.clone())),
3108 Box::new(Expression::Integer(2)),
3109 );
3110 let order2 = Expression::Power(
3111 Box::new(Expression::Variable(x.clone())),
3112 Box::new(Expression::Integer(2)),
3113 );
3114
3115 let big_o1 = BigO::new(order1, x.clone());
3116 let big_o2 = BigO::new(order2, x.clone());
3117
3118 assert!(big_o1.is_same_order(&big_o2));
3119 }
3120
3121 #[test]
3122 fn test_big_o_display() {
3123 let x = Variable::new("x");
3124 let order = Expression::Power(
3125 Box::new(Expression::Variable(x.clone())),
3126 Box::new(Expression::Integer(3)),
3127 );
3128 let big_o = BigO::new(order, x);
3129
3130 assert!(format!("{}", big_o).contains("O("));
3131 }
3132
3133 #[test]
3134 fn test_asymptotic_series_creation() {
3135 let x = Variable::new("x");
3136 let series = AsymptoticSeries::new(x.clone(), AsymptoticDirection::PosInfinity);
3137
3138 assert_eq!(series.variable, x);
3139 assert_eq!(series.direction, AsymptoticDirection::PosInfinity);
3140 assert_eq!(series.terms.len(), 0);
3141 }
3142
3143 #[test]
3144 fn test_asymptotic_series_add_term() {
3145 let x = Variable::new("x");
3146 let mut series = AsymptoticSeries::new(x.clone(), AsymptoticDirection::PosInfinity);
3147
3148 series.add_term(AsymptoticTerm::new(
3149 Expression::Integer(1),
3150 Expression::Integer(-1),
3151 ));
3152 series.add_term(AsymptoticTerm::new(
3153 Expression::Integer(1),
3154 Expression::Integer(-2),
3155 ));
3156
3157 assert_eq!(series.terms.len(), 2);
3158 }
3159
3160 #[test]
3161 fn test_asymptotic_series_dominant_term() {
3162 let x = Variable::new("x");
3163 let mut series = AsymptoticSeries::new(x.clone(), AsymptoticDirection::PosInfinity);
3164
3165 series.add_term(AsymptoticTerm::new(
3166 Expression::Integer(1),
3167 Expression::Integer(-1),
3168 ));
3169 series.add_term(AsymptoticTerm::new(
3170 Expression::Integer(1),
3171 Expression::Integer(-2),
3172 ));
3173
3174 let dominant = series.dominant_term().unwrap();
3175 assert_eq!(dominant.exponent, Expression::Integer(-1));
3176 }
3177
3178 #[test]
3179 fn test_asymptotic_series_order_of_magnitude() {
3180 let x = Variable::new("x");
3181 let mut series = AsymptoticSeries::new(x.clone(), AsymptoticDirection::PosInfinity);
3182
3183 series.add_term(AsymptoticTerm::new(
3184 Expression::Integer(2),
3185 Expression::Integer(-1),
3186 ));
3187
3188 let order = series.order_of_magnitude().unwrap();
3189 assert_eq!(order, Expression::Integer(-1));
3190 }
3191
3192 #[test]
3193 fn test_asymptotic_series_with_error_term() {
3194 let x = Variable::new("x");
3195 let mut series = AsymptoticSeries::new(x.clone(), AsymptoticDirection::PosInfinity);
3196
3197 series.add_term(AsymptoticTerm::new(
3198 Expression::Integer(1),
3199 Expression::Integer(-1),
3200 ));
3201 series.add_term(AsymptoticTerm::new(
3202 Expression::Integer(1),
3203 Expression::Integer(-2),
3204 ));
3205
3206 let (_, big_o) = series.with_error_term();
3207 assert_eq!(big_o.variable, x);
3209 }
3210
3211 #[test]
3212 fn test_asymptotic_series_evaluate() {
3213 let x = Variable::new("x");
3214 let mut series = AsymptoticSeries::new(x.clone(), AsymptoticDirection::PosInfinity);
3215
3216 series.add_term(AsymptoticTerm::new(
3218 Expression::Integer(1),
3219 Expression::Integer(-1),
3220 ));
3221 series.add_term(AsymptoticTerm::new(
3222 Expression::Integer(1),
3223 Expression::Integer(-2),
3224 ));
3225
3226 let result = series.evaluate(2.0).unwrap();
3227 assert!((result - 0.75).abs() < 1e-10);
3228 }
3229
3230 #[test]
3231 fn test_asymptotic_1_over_x() {
3232 use crate::parser::parse_expression;
3233
3234 let expr = parse_expression("1/x").unwrap();
3236 let series = asymptotic(&expr, "x", AsymptoticDirection::PosInfinity, 3).unwrap();
3237
3238 assert_eq!(series.terms.len(), 1);
3239 assert_eq!(series.terms[0].coefficient, Expression::Integer(1));
3240 assert_eq!(series.terms[0].exponent, Expression::Integer(-1));
3241 }
3242
3243 #[test]
3244 fn test_asymptotic_1_over_x_plus_1_over_x2() {
3245 use crate::parser::parse_expression;
3246
3247 let expr = parse_expression("1/x + 1/x^2").unwrap();
3249 let series = asymptotic(&expr, "x", AsymptoticDirection::PosInfinity, 3).unwrap();
3250
3251 assert_eq!(series.terms.len(), 2);
3252 assert_eq!(series.terms[0].exponent, Expression::Integer(-1));
3254
3255 let exp1 = &series.terms[1].exponent;
3258 let exp1_val = try_expr_to_f64(exp1).unwrap();
3259 assert!((exp1_val - (-2.0)).abs() < 1e-10);
3260 }
3261
3262 #[test]
3263 fn test_asymptotic_x_squared_plus_x() {
3264 use crate::parser::parse_expression;
3265
3266 let expr = parse_expression("x^2 + x").unwrap();
3268 let series = asymptotic(&expr, "x", AsymptoticDirection::PosInfinity, 3).unwrap();
3269
3270 let dominant = series.dominant_term().unwrap();
3271 let exp_val = try_expr_to_f64(&dominant.exponent).unwrap();
3273 assert!((exp_val - 2.0).abs() < 1e-10);
3274 }
3275
3276 #[test]
3277 fn test_asymptotic_evaluate_at_point() {
3278 use crate::parser::parse_expression;
3279
3280 let expr = parse_expression("1/x + 1/x^2").unwrap();
3282 let series = asymptotic(&expr, "x", AsymptoticDirection::PosInfinity, 3).unwrap();
3283
3284 let result = series.evaluate(10.0).unwrap();
3285 assert!((result - 0.11).abs() < 1e-10);
3286 }
3287
3288 #[test]
3289 fn test_sort_by_dominance_pos_infinity() {
3290 let _x = Variable::new("x");
3291 let mut terms = vec![
3292 AsymptoticTerm::new(Expression::Integer(1), Expression::Integer(-2)),
3293 AsymptoticTerm::new(Expression::Integer(1), Expression::Integer(-1)),
3294 AsymptoticTerm::new(Expression::Integer(1), Expression::Integer(0)),
3295 ];
3296
3297 sort_by_dominance(&mut terms, AsymptoticDirection::PosInfinity);
3298
3299 assert_eq!(terms[0].exponent, Expression::Integer(0));
3301 assert_eq!(terms[1].exponent, Expression::Integer(-1));
3302 assert_eq!(terms[2].exponent, Expression::Integer(-2));
3303 }
3304
3305 #[test]
3306 fn test_sort_by_dominance_zero() {
3307 let _x = Variable::new("x");
3308 let mut terms = vec![
3309 AsymptoticTerm::new(Expression::Integer(1), Expression::Integer(2)),
3310 AsymptoticTerm::new(Expression::Integer(1), Expression::Integer(1)),
3311 AsymptoticTerm::new(Expression::Integer(1), Expression::Integer(0)),
3312 ];
3313
3314 sort_by_dominance(&mut terms, AsymptoticDirection::Zero);
3315
3316 assert_eq!(terms[0].exponent, Expression::Integer(0));
3318 assert_eq!(terms[1].exponent, Expression::Integer(1));
3319 assert_eq!(terms[2].exponent, Expression::Integer(2));
3320 }
3321
3322 #[test]
3323 fn test_limit_via_asymptotic_to_zero() {
3324 use crate::limits::LimitResult;
3325 use crate::parser::parse_expression;
3326
3327 let expr = parse_expression("1/x").unwrap();
3329 let result = limit_via_asymptotic(&expr, "x", AsymptoticDirection::PosInfinity).unwrap();
3330
3331 assert_eq!(result, LimitResult::Value(0.0));
3332 }
3333
3334 #[test]
3335 fn test_limit_via_asymptotic_to_infinity() {
3336 use crate::limits::LimitResult;
3337 use crate::parser::parse_expression;
3338
3339 let expr = parse_expression("x^2").unwrap();
3341 let result = limit_via_asymptotic(&expr, "x", AsymptoticDirection::PosInfinity).unwrap();
3342
3343 assert_eq!(result, LimitResult::PositiveInfinity);
3344 }
3345
3346 #[test]
3347 fn test_limit_via_asymptotic_constant() {
3348 use crate::limits::LimitResult;
3349
3350 let expr = Expression::Integer(5);
3352 let result = limit_via_asymptotic(&expr, "x", AsymptoticDirection::PosInfinity).unwrap();
3353
3354 assert_eq!(result, LimitResult::Value(5.0));
3355 }
3356
3357 #[test]
3358 fn test_asymptotic_series_to_expression() {
3359 let x = Variable::new("x");
3360 let mut series = AsymptoticSeries::new(x.clone(), AsymptoticDirection::PosInfinity);
3361
3362 series.add_term(AsymptoticTerm::new(
3363 Expression::Integer(1),
3364 Expression::Integer(-1),
3365 ));
3366 series.add_term(AsymptoticTerm::new(
3367 Expression::Integer(2),
3368 Expression::Integer(-2),
3369 ));
3370
3371 let expr = series.to_expression();
3372 assert!(!matches!(expr, Expression::Integer(0)));
3374 }
3375
3376 #[test]
3377 fn test_asymptotic_series_display() {
3378 let x = Variable::new("x");
3379 let mut series = AsymptoticSeries::new(x.clone(), AsymptoticDirection::PosInfinity);
3380
3381 series.add_term(AsymptoticTerm::new(
3382 Expression::Integer(1),
3383 Expression::Integer(-1),
3384 ));
3385
3386 let display_str = format!("{}", series);
3387 assert!(display_str.contains("x→+∞"));
3388 }
3389}