use crate::base::arena::Arena;
use crate::base::node::ExprId;
fn integral_over_period(arena: &mut Arena, g: ExprId, var: ExprId) -> ExprId {
let pi = arena.pi;
let neg_pi = arena.neg(pi);
let anti = crate::transforms::integrate::integrate(arena, g, var);
if !crate::base::walk::has_unevaluated(arena, anti) {
let at_pi = crate::transforms::subs::subs(arena, anti, var, pi);
let at_neg_pi = crate::transforms::subs::subs(arena, anti, var, neg_pi);
let diff = arena.sub(at_pi, at_neg_pi);
let diff = crate::transforms::eval::eval(arena, diff);
if !has_non_finite(arena, diff) {
return diff;
}
}
match crate::calculus::definite::integrate_definite(arena, g, var, neg_pi, pi) {
Ok(v) if !has_non_finite(arena, v) => v,
_ => crate::calculus::definite::unevaluated_definite(arena, g, var, neg_pi, pi),
}
}
fn has_non_finite(arena: &Arena, e: ExprId) -> bool {
use crate::base::node::ExprNode;
crate::base::walk::post_order_ids(arena, e)
.into_iter()
.any(|id| {
matches!(
arena.node(id),
ExprNode::NaN
| ExprNode::ComplexInfinity
| ExprNode::Infinity
| ExprNode::NegInfinity
)
})
}
pub(crate) fn fourier_series(arena: &mut Arena, expr: ExprId, var: ExprId, n_terms: u32) -> ExprId {
let pi = arena.pi;
let two = arena.int(2);
let definite = integral_over_period(arena, expr, var);
let a0 = arena.div(definite, pi);
let a0_half = arena.div(a0, two);
let mut terms = vec![a0_half];
for n in 1..=n_terms {
let n_expr = arena.int(n as i64);
let nx = arena.mul(&[n_expr, var]);
let cos_nx = arena.cos(nx);
let f_cos = arena.mul(&[expr, cos_nx]);
let an_num = integral_over_period(arena, f_cos, var);
let an = arena.div(an_num, pi);
let sin_nx = arena.sin(nx);
let f_sin = arena.mul(&[expr, sin_nx]);
let bn_num = integral_over_period(arena, f_sin, var);
let bn = arena.div(bn_num, pi);
let an_cos = arena.mul(&[an, cos_nx]);
let bn_sin = arena.mul(&[bn, sin_nx]);
let term = arena.add(&[an_cos, bn_sin]);
terms.push(term);
}
let result = arena.add(&terms);
crate::transforms::eval::eval(arena, result)
}