use std::cell::{Ref, RefCell};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt;
use std::rc::Rc;
mod assume;
mod calculus;
mod canonical;
mod eval;
mod factor;
mod hash;
mod node;
mod order;
mod poly_bridge;
#[cfg(feature = "test-gen")]
pub mod test_gen;
mod transform;
use hash::FxBuild;
pub(crate) use node::Node;
pub use crate::assume::{Assumptions, Predicate, Trinary};
pub use cas_domain::{Integer, Rational};
pub struct Inner {
pub(crate) nodes: Vec<Node>,
pub(crate) hashes: Vec<u64>,
pub(crate) intern: HashMap<u64, Vec<u32>, FxBuild>,
pub(crate) args_slab: Vec<u32>,
pub(crate) syms: HashMap<Box<str>, u32, FxBuild>,
pub(crate) sym_names: Vec<Box<str>>,
pub(crate) fn_heads: HashMap<Box<str>, u32, FxBuild>,
pub(crate) fn_names: Vec<Box<str>>,
pub(crate) sym_assumptions: HashMap<u32, assume::Assumptions>,
}
impl Inner {
fn new() -> Self {
Inner {
nodes: vec![Node::Int(Integer::zero())], hashes: vec![0],
intern: HashMap::with_hasher(FxBuild::default()),
args_slab: Vec::new(),
syms: HashMap::with_hasher(FxBuild::default()),
sym_names: Vec::new(),
fn_heads: HashMap::with_hasher(FxBuild::default()),
fn_names: Vec::new(),
sym_assumptions: HashMap::new(),
}
}
}
#[derive(Clone)]
pub struct Context {
inner: Rc<RefCell<Inner>>,
}
impl Context {
pub fn new() -> Self {
Context {
inner: Rc::new(RefCell::new(Inner::new())),
}
}
fn wrap(&self, id: u32) -> Expr {
Expr {
ctx: Rc::clone(&self.inner),
id,
}
}
fn with(&self, f: impl FnOnce(&mut Inner) -> u32) -> Expr {
let id = f(&mut self.inner.borrow_mut());
self.wrap(id)
}
pub fn sym(&self, name: &str) -> Expr {
validate_name(name, "符号");
self.with(|c| c.sym_node(name))
}
pub fn int(&self, v: i64) -> Expr {
self.with(|c| c.lit_int(v))
}
pub fn integer(&self, v: &Integer) -> Expr {
self.with(|c| c.lit_integer(v))
}
pub fn sym_with(&self, name: &str, preds: &[assume::Predicate]) -> Result<Expr, String> {
validate_name(name, "符号");
let a = assume::Assumptions::union(preds);
let e = self.sym(name);
let sid = self.inner.borrow().syms.get(name).copied();
if let Some(sid) = sid {
self.inner
.borrow_mut()
.sym_assumptions
.entry(sid)
.and_modify(|old| {
assert!(
*old == a,
"符号 {name} 的假设只能一次性设定(当前 {:?},重设 {:?})",
old,
a
);
})
.or_insert(a);
}
Ok(e)
}
pub fn query(&self, e: &Expr, p: assume::Predicate) -> assume::Trinary {
let inner = self.inner.borrow();
inner.query_at(e.id, p, 0)
}
pub fn float(&self, v: f64) -> Expr {
assert!(!v.is_nan(), "Float 字面量禁止 NaN");
if v < 0.0 {
self.with(|c| {
let f = c.lit_float(-v);
let neg = c.lit_int(-1);
c.make_mul(&[neg, f])
})
} else {
self.with(|c| c.lit_float(v))
}
}
pub fn rational(&self, n: i64, d: i64) -> Option<Expr> {
let r = Rational::from_ints(&Integer::from_i64(n), &Integer::from_i64(d))?;
Some(self.with(|c| c.lit_rational(&r)))
}
pub fn add(&self, args: &[Expr]) -> Expr {
let ids: Vec<u32> = args.iter().map(|e| e.id).collect();
self.with(|c| c.make_add(&ids))
}
pub fn mul(&self, args: &[Expr]) -> Expr {
let ids: Vec<u32> = args.iter().map(|e| e.id).collect();
self.with(|c| c.make_mul(&ids))
}
pub fn pow(&self, base: &Expr, exp: &Expr) -> Expr {
self.with(|c| c.make_pow(base.id, exp.id))
}
pub fn call(&self, head: &str, args: &[Expr]) -> Expr {
validate_name(head, "函数");
let ids: Vec<u32> = args.iter().map(|e| e.id).collect();
self.with(|c| c.fn_node(head, &ids))
}
pub fn cmp_expr(&self, a: &Expr, b: &Expr) -> Ordering {
let inner = self.inner.borrow();
inner.cmp_ids(a.id, b.id)
}
pub fn eq_expr(&self, a: &Expr, b: &Expr) -> bool {
self.cmp_expr(a, b) == Ordering::Equal
}
pub fn node_count(&self) -> usize {
self.inner.borrow().nodes.len()
}
pub fn inspect<R>(&self, f: impl FnOnce(&Inspector<'_>) -> R) -> R {
f(&Inspector {
inner: self.inner.borrow(),
})
}
pub fn expand(&self, e: &Expr) -> Expr {
self.with(|c| c.expand_at(e.id, 0))
}
pub fn cancel(&self, e: &Expr) -> Expr {
self.with(|c| c.cancel_at(e.id))
}
pub fn factor(&self, e: &Expr) -> Expr {
self.with(|c| c.factor_at(e.id))
}
pub fn diff(&self, e: &Expr, x: &Expr) -> Expr {
match x_name_of(self, x) {
Some(name) => {
let sid = self.inner.borrow().syms.get(name.as_str()).copied();
match sid {
Some(s) => self.with(|c| c.diff_at(e.id, s, 0)),
None => self.with(|c| c.lit_int(0)),
}
}
None => self.with(|c| c.lit_int(0)),
}
}
pub fn taylor(&self, e: &Expr, x: &Expr, at: i64, order: u32) -> Expr {
match x_name_of(self, x) {
Some(name) => {
let sid = self.inner.borrow().syms.get(name.as_str()).copied();
match sid {
Some(s) => self.with(|c| c.taylor_at(e.id, s, at, order)),
None => e.clone(),
}
}
None => e.clone(),
}
}
pub fn simplify(&self, e: &Expr) -> Expr {
self.with(|c| c.simplify_at(e.id, 0))
}
pub fn subst(&self, e: &Expr, map: &[(&str, Expr)]) -> Expr {
let m = {
let inner = self.inner.borrow();
map.iter()
.filter_map(|(name, val)| inner.syms.get(*name).map(|&s| (s, val.id)))
.collect::<std::collections::HashMap<u32, u32>>()
};
self.with(|c| c.subst_at(e.id, &m, 0))
}
pub fn eval_rational(&self, e: &Expr, vals: &[(&str, Rational)]) -> Option<Rational> {
let inner = self.inner.borrow();
let m = vals
.iter()
.filter_map(|(n, v)| inner.syms.get(*n).map(|&s| (s, v.clone())))
.collect::<std::collections::HashMap<u32, Rational>>();
eval::eval_rational_at(&inner, e.id, &m, 0)
}
pub fn eval_float(&self, e: &Expr, vals: &[(&str, f64)]) -> Option<f64> {
let inner = self.inner.borrow();
let m = vals
.iter()
.filter_map(|(n, v)| inner.syms.get(*n).map(|&s| (s, *v)))
.collect::<std::collections::HashMap<u32, f64>>();
eval::eval_float_at(&inner, e.id, &m, 0)
}
pub fn monomial_coeffs(
&self,
e: &Expr,
vars: &[&str],
) -> Result<Vec<(Vec<u32>, Expr)>, CasError> {
let var_ids: Vec<u32> = {
let inner = self.inner.borrow();
let mut ids = Vec::with_capacity(vars.len());
for name in vars {
match inner.syms.get(*name) {
Some(&s) => ids.push(s),
None => {
return Err(CasError::new(
CasErrorKind::UnknownSymbol,
format!("变量 {name} 不在该 Context 中"),
));
}
}
}
ids
};
let monos = {
let inner = self.inner.borrow();
transform::monomials_at(&inner, e.id, &var_ids, 0)?
};
let mut groups: HashMap<Vec<u32>, Vec<Vec<u32>>> = HashMap::new();
let mut order: Vec<Vec<u32>> = Vec::new();
for m in monos {
match groups.get_mut(&m.exps) {
Some(list) => list.push(m.coef),
None => {
order.push(m.exps.clone());
groups.insert(m.exps, vec![m.coef]);
}
}
}
let mut out = Vec::with_capacity(order.len());
for exps in order {
let terms = groups.remove(&exps).unwrap_or_default();
let coef = self.with(|c| {
let ids: Vec<u32> = terms
.iter()
.map(|factors| {
if factors.is_empty() {
c.lit_int(1)
} else {
c.make_mul(factors)
}
})
.collect();
if ids.len() == 1 {
ids[0]
} else {
c.make_add(&ids)
}
});
out.push((exps, coef));
}
Ok(out)
}
}
fn x_name_of(ctx: &Context, x: &Expr) -> Option<String> {
ctx.inspect(|i| match i.kind(x.raw_id()) {
Kind::Sym(name) => Some(name.to_string()),
_ => None,
})
}
impl Default for Context {
fn default() -> Self {
Self::new()
}
}
fn validate_name(name: &str, what: &str) {
let mut chars = name.chars();
let valid = matches!(chars.next(), Some(c) if c.is_ascii_alphabetic() || c == '_')
&& chars.all(|c| c.is_ascii_alphanumeric() || c == '_');
assert!(
valid && !name.is_empty(),
"{what}名非法: {name:?},须匹配 [A-Za-z_][A-Za-z0-9_]*"
);
}
#[derive(Clone)]
pub struct Expr {
ctx: Rc<RefCell<Inner>>,
id: u32,
}
impl Expr {
pub fn raw_id(&self) -> u32 {
self.id
}
fn bin(self, other: Expr, f: impl FnOnce(&mut Inner, u32, u32) -> u32) -> Expr {
let ctx = Rc::clone(&self.ctx);
let id = f(&mut ctx.borrow_mut(), self.id, other.id);
Expr { ctx, id }
}
pub fn pow<E: IntoExpr>(self, exp: E) -> Expr {
let ctx = Rc::clone(&self.ctx);
let mut inner = ctx.borrow_mut();
let eid = exp.into_id(&mut inner);
let id = inner.make_pow(self.id, eid);
drop(inner);
Expr { ctx, id }
}
}
macro_rules! forward_binop {
($tr:ident, $method:ident, $inner:expr) => {
impl std::ops::$tr<Expr> for Expr {
type Output = Expr;
fn $method(self, rhs: Expr) -> Expr {
self.bin(rhs, $inner)
}
}
impl std::ops::$tr<&Expr> for Expr {
type Output = Expr;
fn $method(self, rhs: &Expr) -> Expr {
self.bin(rhs.clone(), $inner)
}
}
impl std::ops::$tr<Expr> for &Expr {
type Output = Expr;
fn $method(self, rhs: Expr) -> Expr {
self.clone().bin(rhs, $inner)
}
}
impl std::ops::$tr<&Expr> for &Expr {
type Output = Expr;
fn $method(self, rhs: &Expr) -> Expr {
self.clone().bin(rhs.clone(), $inner)
}
}
};
}
forward_binop!(Add, add, |c, a, b| c.make_add(&[a, b]));
forward_binop!(Sub, sub, |c, a, b| {
let neg1 = c.lit_int(-1);
let neg = c.make_mul(&[neg1, b]);
c.make_add(&[a, neg])
});
forward_binop!(Mul, mul, |c, a, b| c.make_mul(&[a, b]));
forward_binop!(Div, div, |c, a, b| {
let neg1 = c.lit_int(-1);
let inv = c.make_pow(b, neg1);
c.make_mul(&[a, inv])
});
impl std::ops::Neg for Expr {
type Output = Expr;
fn neg(self) -> Expr {
let ctx = Rc::clone(&self.ctx);
let id = {
let mut c = ctx.borrow_mut();
let neg1 = c.lit_int(-1);
c.make_mul(&[neg1, self.id])
};
Expr { ctx, id }
}
}
impl std::ops::Neg for &Expr {
type Output = Expr;
fn neg(self) -> Expr {
(*self).clone().neg()
}
}
impl PartialEq for Expr {
fn eq(&self, other: &Self) -> bool {
if Rc::ptr_eq(&self.ctx, &other.ctx) {
return self.id == other.id;
}
let x = self.ctx.borrow();
let y = other.ctx.borrow();
order::deep_eq(&x, self.id, &y, other.id)
}
}
impl Eq for Expr {}
impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(
&Inspector {
inner: self.ctx.borrow(),
}
.dump(self.id),
)
}
}
pub trait IntoExpr {
fn into_id(self, c: &mut Inner) -> u32;
}
impl IntoExpr for Expr {
fn into_id(self, _c: &mut Inner) -> u32 {
self.id
}
}
impl IntoExpr for &Expr {
fn into_id(self, _c: &mut Inner) -> u32 {
self.id
}
}
impl IntoExpr for i64 {
fn into_id(self, c: &mut Inner) -> u32 {
c.lit_int(self)
}
}
impl IntoExpr for i32 {
fn into_id(self, c: &mut Inner) -> u32 {
c.lit_int(self as i64)
}
}
impl IntoExpr for u32 {
fn into_id(self, c: &mut Inner) -> u32 {
c.lit_int(self as i64)
}
}
impl IntoExpr for f64 {
fn into_id(self, c: &mut Inner) -> u32 {
assert!(self >= 0.0, "pow 的浮点指数须非负(负浮点请用 Expr 形态)");
c.lit_float(self)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CasErrorKind {
NonPolynomial,
NegativeExponent,
UnknownSymbol,
}
#[derive(Clone, Debug)]
pub struct CasError {
pub kind: CasErrorKind,
pub msg: String,
}
impl CasError {
pub fn new(kind: CasErrorKind, msg: impl Into<String>) -> Self {
CasError {
kind,
msg: msg.into(),
}
}
}
impl std::fmt::Display for CasError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}: {}", self.kind, self.msg)
}
}
impl std::error::Error for CasError {}
pub struct Inspector<'a> {
inner: Ref<'a, Inner>,
}
#[derive(Debug)]
pub enum Kind<'a> {
Int(&'a Integer),
Rat(&'a Rational),
Float(f64),
Sym(&'a str),
Fn { head: &'a str, args: &'a [u32] },
Pow { base: u32, exp: u32 },
Mul(&'a [u32]),
Add(&'a [u32]),
}
impl Inspector<'_> {
pub fn kind(&self, id: u32) -> Kind<'_> {
match &self.inner.nodes[id as usize] {
Node::Int(v) => Kind::Int(v),
Node::Rat(v) => Kind::Rat(v),
Node::Float { bits, .. } => Kind::Float(f64::from_bits(*bits)),
Node::Sym(s) => Kind::Sym(&self.inner.sym_names[*s as usize]),
Node::Fn { head, args } => Kind::Fn {
head: &self.inner.fn_names[*head as usize],
args: self.inner.node_args(*args),
},
Node::Pow { base, exp } => Kind::Pow {
base: *base,
exp: *exp,
},
Node::Mul { args } => Kind::Mul(self.inner.node_args(*args)),
Node::Add { args } => Kind::Add(self.inner.node_args(*args)),
}
}
pub fn cmp(&self, a: u32, b: u32) -> Ordering {
self.inner.cmp_ids(a, b)
}
pub fn dump(&self, id: u32) -> String {
let mut s = String::new();
self.dump_at(id, &mut s, 0);
s
}
fn dump_at(&self, id: u32, out: &mut String, depth: u32) {
assert!(depth <= 10_000, "表达式嵌套过深");
let sub = |c: &Self, cid: u32, o: &mut String, d: u32| c.dump_at(cid, o, d);
match self.kind(id) {
Kind::Int(v) => out.push_str(&format!("int({v})")),
Kind::Rat(r) => out.push_str(&format!("rat({r})")),
Kind::Float(v) => out.push_str(&format!("flt({v})")),
Kind::Sym(n) => out.push_str(&format!("sym({n})")),
Kind::Fn { head, args } => {
out.push_str(&format!("fn({head}, ["));
for (i, &a) in args.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
sub(self, a, out, depth + 1);
}
out.push_str("])");
}
Kind::Pow { base, exp } => {
out.push_str("pow(");
sub(self, base, out, depth + 1);
out.push_str(", ");
sub(self, exp, out, depth + 1);
out.push(')');
}
Kind::Mul(args) => {
out.push_str("mul[");
for (i, &a) in args.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
sub(self, a, out, depth + 1);
}
out.push(']');
}
Kind::Add(args) => {
out.push_str("add[");
for (i, &a) in args.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
sub(self, a, out, depth + 1);
}
out.push(']');
}
}
}
}
#[macro_export]
macro_rules! sym {
($ctx:expr, $name:ident) => {
$ctx.sym(stringify!($name))
};
($ctx:expr, $($name:ident),+ $(,)?) => {
($($ctx.sym(stringify!($name))),+)
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn 规范形_同类合并与数值折叠() {
let ctx = Context::new();
let x = ctx.sym("x");
let y = ctx.sym("y");
let a = x.clone() + x.clone();
let b = ctx.int(2) * x.clone();
assert!(a == b && a.raw_id() == b.raw_id());
let c = ctx.sym("c");
let p = (x.clone() + y.clone()) + c.clone();
let q = c + (y + x);
assert_eq!(p.raw_id(), q.raw_id());
let n = ctx.int(2) + ctx.int(3);
assert!(
ctx.inspect(
|i| matches!(i.kind(n.raw_id()), Kind::Int(v) if *v == Integer::from_i64(5))
)
);
}
#[test]
fn 规范形_同底幂合并() {
let ctx = Context::new();
let x = ctx.sym("x");
let a = ctx.sym("a");
let b = ctx.sym("b");
let p = x.clone() * x.clone();
let q = x.clone().pow(2);
assert_eq!(p.raw_id(), q.raw_id());
let p = x.clone().pow(2) * x.clone().pow(3);
let q = x.clone().pow(5);
assert_eq!(p.raw_id(), q.raw_id());
let p = x.clone().pow(&a) * x.clone().pow(&b);
let q = x.clone().pow(&a + &b);
assert_eq!(p.raw_id(), q.raw_id());
let p = x.clone().pow(&a).pow(2);
let q = x.clone().pow(&ctx.int(2) * &a);
assert_eq!(p.raw_id(), q.raw_id());
let nested = x.clone().pow(&a).pow(&b);
assert!(ctx.inspect(|i| matches!(i.kind(nested.raw_id()), Kind::Pow { .. })));
}
#[test]
fn 规范形_数值幂折叠与守卫() {
let ctx = Context::new();
let p = ctx.int(2).pow(100);
assert!(ctx.inspect(|i| matches!(i.kind(p.raw_id()), Kind::Int(v)
if *v == Integer::parse("1267650600228229401496703205376").unwrap())));
let p = ctx.int(2).pow(-2);
assert!(ctx.inspect(|i| matches!(i.kind(p.raw_id()), Kind::Rat(_))));
assert_eq!(ctx.int(0).pow(0).raw_id(), ctx.int(1).raw_id());
assert_eq!(ctx.int(0).pow(3).raw_id(), ctx.int(0).raw_id());
let zero_neg_pow = ctx.int(0).pow(-1);
assert!(ctx.inspect(|i| matches!(i.kind(zero_neg_pow.raw_id()), Kind::Pow { .. })));
let huge = ctx.int(2).pow(1 << 30);
assert!(ctx.inspect(|i| matches!(i.kind(huge.raw_id()), Kind::Pow { .. })));
}
#[test]
fn 规范形_除法与负浮点() {
let ctx = Context::new();
let x = ctx.sym("x");
let p = x.clone() / ctx.int(2);
let q = ctx.mul(&[ctx.rational(1, 2).unwrap(), x.clone()]);
assert_eq!(p.raw_id(), q.raw_id());
let p = ctx.float(-2.5);
let q = -ctx.float(2.5);
assert_eq!(p.raw_id(), q.raw_id());
assert_eq!((x.clone() / x.clone()).raw_id(), ctx.int(1).raw_id());
}
#[test]
fn 全序_数值与符号() {
let ctx = Context::new();
let x = ctx.sym("x");
let y = ctx.sym("y");
let two = ctx.int(2);
let half = ctx.rational(1, 2).unwrap();
let three = ctx.int(3);
let f1 = ctx.float(1.5);
assert_eq!(ctx.cmp_expr(&half, &two), Ordering::Less);
assert_eq!(ctx.cmp_expr(&two, &three), Ordering::Less);
assert_eq!(ctx.cmp_expr(&three, &f1), Ordering::Less);
let big = ctx.sym("zz");
assert_eq!(ctx.cmp_expr(&x, &y), Ordering::Less);
assert_eq!(ctx.cmp_expr(&y, &big), Ordering::Less);
let m = ctx.int(3) * x.clone() * y.clone();
let first = ctx.inspect(|i| match i.kind(m.raw_id()) {
Kind::Mul(args) => args[0],
k => panic!("期望 Mul: {k:?}"),
});
assert!(ctx.cmp_expr(&ctx.wrap(first), &x) == Ordering::Less);
}
#[test]
fn 全序_复合节点字典序() {
let ctx = Context::new();
let x = ctx.sym("x");
let y = ctx.sym("y");
let x2 = x.clone().pow(2);
let xy = x.clone() * y.clone();
let sum = x.clone() + y.clone();
assert_eq!(ctx.cmp_expr(&x, &x2), Ordering::Less);
assert_eq!(ctx.cmp_expr(&x2, &xy), Ordering::Less);
assert_eq!(ctx.cmp_expr(&xy, &sum), Ordering::Less);
}
#[test]
fn 展开_黄金快照() {
let ctx = Context::new();
let x = ctx.sym("x");
let y = ctx.sym("y");
let e = (x.clone() + y.clone()) * (x.clone() - y.clone());
let g = ctx.expand(&e);
let expect = x.clone().pow(2) - y.clone().pow(2);
assert_eq!(g.raw_id(), expect.raw_id());
let e = (x.clone() + y.clone()).pow(2);
let g = ctx.expand(&e);
let expect = x.clone().pow(2) + y.clone().pow(2) + ctx.int(2) * x.clone() * y.clone();
assert_eq!(g.raw_id(), expect.raw_id());
let e = (x.clone() + y.clone()).pow(3);
let g = ctx.expand(&e);
let expect = x.clone().pow(3)
+ y.clone().pow(3)
+ ctx.int(3) * x.clone() * y.clone().pow(2)
+ ctx.int(3) * x.clone().pow(2) * y.clone();
assert_eq!(g.raw_id(), expect.raw_id());
}
#[test]
fn 展开_语义一致与幂等() {
let ctx = Context::new();
let x = ctx.sym("x");
let y = ctx.sym("y");
let z = ctx.sym("z");
let e = ((x.clone() + y.clone()).pow(3) - z.clone() * (x.clone() + y.clone()))
* (x.clone() - z.clone());
let g = ctx.expand(&e);
let g2 = ctx.expand(&g);
assert_eq!(g.raw_id(), g2.raw_id());
for (xv, yv, zv) in [(2, -3, 5), (-1, 4, 7), (0, 9, -11)] {
let pts = [
("x", Rational::from_integer(&Integer::from_i64(xv))),
("y", Rational::from_integer(&Integer::from_i64(yv))),
("z", Rational::from_integer(&Integer::from_i64(zv))),
];
let a = ctx.eval_rational(&e, &pts);
let b = ctx.eval_rational(&g, &pts);
assert_eq!(a, b, "展开改变语义");
}
let huge = (x.clone() + y.clone()).pow(20_000);
assert!(ctx.inspect(|i| matches!(i.kind(huge.raw_id()), Kind::Pow { .. })));
}
#[test]
fn 展开_四元二十次幂() {
let ctx = Context::new();
let (x, y, z, w) = crate::sym!(&ctx, x, y, z, w);
let base = ctx.int(1) + x + y + z + w;
let g = ctx.expand(&base.pow(20));
let n = ctx.inspect(|i| match i.kind(g.raw_id()) {
Kind::Add(args) => args.len(),
_ => 0,
});
assert_eq!(n, 10_626); }
#[test]
fn 代换() {
let ctx = Context::new();
let x = ctx.sym("x");
let y = ctx.sym("y");
let e = x.clone() + y.clone();
let g = ctx.subst(&e, &[("x", x.clone().pow(2))]);
let expect = x.clone().pow(2) + y.clone();
assert_eq!(g.raw_id(), expect.raw_id());
let same = ctx.subst(&e, &[("x", x.clone())]);
assert_eq!(same.raw_id(), e.raw_id());
let p = x.clone() * y.clone();
let q = ctx.subst(&p, &[("x", y.clone()), ("y", x.clone())]);
assert_eq!(q.raw_id(), p.raw_id()); }
#[test]
fn 展开快慢路径同构() {
let ctx = Context::new();
let (x, y, z, w) = crate::sym!(&ctx, x, y, z, w);
let syms = [x.clone(), y.clone(), z.clone(), w.clone()];
let mut xs = 777u64;
let mut nxt = move || {
xs ^= xs << 13;
xs ^= xs >> 7;
xs ^= xs << 17;
xs
};
fn gen_poly(
ctx: &Context,
syms: &[Expr],
nxt: &mut impl FnMut() -> u64,
depth: u32,
) -> Expr {
if depth == 0 || nxt() % 3 == 0 {
match nxt() % 3 {
0 => ctx.int((nxt() % 19) as i64 - 9),
1 => ctx
.rational((nxt() % 15) as i64 - 7, (nxt() % 8) as i64 + 2)
.unwrap(),
_ => syms[(nxt() % syms.len() as u64) as usize].clone(),
}
} else {
match nxt() % 3 {
0 => gen_poly(ctx, syms, nxt, depth - 1) + gen_poly(ctx, syms, nxt, depth - 1),
1 => gen_poly(ctx, syms, nxt, depth - 1) * gen_poly(ctx, syms, nxt, depth - 1),
_ => gen_poly(ctx, syms, nxt, depth - 1).pow((nxt() % 5) as i64),
}
}
}
let slow_of = |ctx: &Context, e: &Expr| -> u32 {
let mut inner = ctx.inner.borrow_mut();
inner.expand_impl(e.raw_id(), 0, false)
};
for _ in 0..60 {
let e = gen_poly(&ctx, &syms, &mut nxt, 4);
let fast = ctx.expand(&e);
let slow = slow_of(&ctx, &e);
assert_eq!(fast.raw_id(), slow, "快慢路径不同构: {e:?}");
}
let e = ctx.call("sin", std::slice::from_ref(&x)) * (y.clone() + z.clone()).pow(5)
+ ctx.float(1.5) * (x.clone() + w.clone()).pow(3);
assert_eq!(ctx.expand(&e).raw_id(), slow_of(&ctx, &e));
}
#[test]
fn 约化() {
let ctx = Context::new();
let x = ctx.sym("x");
let y = ctx.sym("y");
let z = ctx.sym("z");
let e = (x.clone().pow(2) - y.clone().pow(2)) / (x.clone() - y.clone());
let expect = x.clone() + y.clone();
assert_eq!(ctx.cancel(&e).raw_id(), expect.raw_id());
let e = (x.clone() * y.clone()) / (x.clone() * z.clone());
let expect = y.clone() / z.clone();
assert_eq!(ctx.cancel(&e).raw_id(), expect.raw_id());
let e = (x.clone() * y.clone() + x.clone() * z.clone()) / x.clone();
let expect = y.clone() + z.clone();
assert_eq!(ctx.cancel(&e).raw_id(), expect.raw_id());
let e = (x.clone() + y.clone()) / x.clone();
assert_eq!(ctx.cancel(&e).raw_id(), e.raw_id());
let e = (x.clone().pow(2) * y.clone() - y.clone().pow(3))
/ (x.clone() * y.clone() + y.clone().pow(2));
let g = ctx.cancel(&e);
for (xv, yv) in [(2, 3), (-4, 5), (7, -2)] {
let pts = [
("x", Rational::from_integer(&Integer::from_i64(xv))),
("y", Rational::from_integer(&Integer::from_i64(yv))),
];
assert_eq!(ctx.eval_rational(&e, &pts), ctx.eval_rational(&g, &pts));
}
let e = ctx.float(1.5) * ((x.clone().pow(2) - ctx.int(1)) / (x.clone() - ctx.int(1)));
let g = ctx.cancel(&e);
let v = ctx.eval_float(&g, &[("x", 3.0)]).unwrap();
assert!((v - 1.5 * 4.0).abs() < 1e-12);
}
#[test]
fn 因式分解() {
let ctx = Context::new();
let x = ctx.sym("x");
let e = x.clone().pow(2) - ctx.int(1);
let g = ctx.factor(&e);
let expect = (x.clone() - ctx.int(1)) * (x.clone() + ctx.int(1));
assert_eq!(g.raw_id(), expect.raw_id());
let e = x.clone().pow(2) - ctx.int(1);
let e = e.pow(2);
let g = ctx.factor(&e);
let expect = (x.clone() - ctx.int(1)).pow(2) * (x.clone() + ctx.int(1)).pow(2);
assert_eq!(g.raw_id(), expect.raw_id());
let e = x.clone().pow(4) + ctx.int(4);
let g = ctx.factor(&e);
let expect = (x.clone().pow(2) - ctx.int(2) * x.clone() + ctx.int(2))
* (x.clone().pow(2) + ctx.int(2) * x.clone() + ctx.int(2));
assert_eq!(g.raw_id(), expect.raw_id());
let e = x.clone().pow(4) + ctx.int(1);
let g = ctx.factor(&e);
let expect = x.clone().pow(4) + ctx.int(1);
assert_eq!(g.raw_id(), expect.raw_id());
let y = ctx.sym("y");
let e = x.clone().pow(2) - y.clone().pow(2);
let g = ctx.factor(&e);
let s = ctx.inspect(|i| i.dump(g.raw_id()));
assert_eq!(
s,
"mul[int(-1), add[sym(x), sym(y)], add[sym(y), mul[int(-1), sym(x)]]]"
);
for (xv, yv) in [(3, 2), (-5, 7)] {
let pts = [
("x", Rational::from_integer(&Integer::from_i64(xv))),
("y", Rational::from_integer(&Integer::from_i64(yv))),
];
assert_eq!(ctx.eval_rational(&e, &pts), ctx.eval_rational(&g, &pts));
}
}
#[test]
fn 精确与数值求值() {
let ctx = Context::new();
let x = ctx.sym("x");
let e = ctx.rational(3, 2).unwrap() * x.clone() + ctx.int(2);
let v = ctx.eval_rational(&e, &[("x", Rational::from_integer(&Integer::from_i64(4)))]);
assert_eq!(v.map(|r| r.to_string()), Some("8".to_string()));
let e = x.clone().pow(-2);
let v = ctx.eval_rational(&e, &[("x", Rational::from_integer(&Integer::from_i64(2)))]);
assert_eq!(v.map(|r| r.to_string()), Some("1/4".to_string()));
assert_eq!(ctx.eval_rational(&ctx.float(1.5), &[]), None);
let s = ctx.call("sin", std::slice::from_ref(&x));
assert_eq!(ctx.eval_rational(&s, &[("x", Rational::zero())]), None);
let sv = ctx.eval_float(&s, &[("x", 0.5)]);
assert!((sv.unwrap() - 0.5f64.sin()).abs() < 1e-15);
let e = (x.clone() + ctx.int(1)).pow(10);
let g = ctx.expand(&e);
let pts = [("x", 1.7f64)];
let a = ctx.eval_float(&e, &pts).unwrap();
let b = ctx.eval_float(&g, &pts).unwrap();
assert!((a - b).abs() < 1e-9 * a.abs().max(1.0));
let lg = ctx.call("log", std::slice::from_ref(&x));
assert_eq!(ctx.eval_float(&lg, &[("x", -1.0)]), None);
}
}