///|
pub(all) enum NumberSymbolKind {
ImaginaryUnit
Pi
Exp1
EulerGamma
GoldenRatio
Catalan
Infinity
NegativeInfinity
ComplexInfinity
NaN
}
///|
pub(all) enum RelOp {
Eq
Ne
Lt
Le
Gt
Ge
}
///|
pub(all) enum WildProperty {
Symbol
Integer
Rational
Real
Positive
Negative
Finite
Nonzero
}
///|
pub(all) enum ExactNumberKind {
Zero
One
NegativeOne
Half
Integer
Rational
}
///|
pub(all) enum Kind {
NumberKind
BooleanKind
TupleKind(Array[Kind])
UndefinedKind
}
///|
pub enum ExprForm {
Number(@symnum.BigRational)
Float(Float)
ComplexFloat(ComplexFloat)
NumberSymbol(NumberSymbolKind)
Symbol(String)
Dummy(String, Int)
Wild(String, Array[Expr], Array[WildProperty])
WildFunction(String, Array[Int])
IdentityFunction
FunctionHead(String)
UndefinedFunction(String)
Apply(Expr, Array[Expr])
Boolean(Bool)
Add(Array[Expr])
Mul(Array[Expr])
Pow(Expr, Expr)
Mod(Expr, Expr)
Tuple(Array[Expr])
Dict(Array[(Expr, Expr)])
Relational(RelOp, Expr, Expr)
Derivative(Expr, Array[Expr])
Subs(Expr, Expr, Expr)
Lambda(Expr, Expr)
}
///|
pub fn number_symbol_name(kind : NumberSymbolKind) -> String {
match kind {
NumberSymbolKind::ImaginaryUnit => "I"
NumberSymbolKind::Pi => "pi"
NumberSymbolKind::Exp1 => "E"
NumberSymbolKind::EulerGamma => "EulerGamma"
NumberSymbolKind::GoldenRatio => "GoldenRatio"
NumberSymbolKind::Catalan => "Catalan"
NumberSymbolKind::Infinity => "oo"
NumberSymbolKind::NegativeInfinity => "-oo"
NumberSymbolKind::ComplexInfinity => "zoo"
NumberSymbolKind::NaN => "nan"
}
}
///|
pub fn dummy_display_name(name : String) -> String {
if name.has_prefix("_") {
name
} else {
"_\{name}"
}
}
///|
pub fn wild_display_name(name : String) -> String {
if name.has_suffix("_") {
name
} else {
"\{name}_"
}
}
///|
pub fn wild_property_name(property : WildProperty) -> String {
match property {
WildProperty::Symbol => "symbol"
WildProperty::Integer => "integer"
WildProperty::Rational => "rational"
WildProperty::Real => "real"
WildProperty::Positive => "positive"
WildProperty::Negative => "negative"
WildProperty::Finite => "finite"
WildProperty::Nonzero => "nonzero"
}
}
///|
pub fn kind_name(kind : Kind) -> String {
match kind {
Kind::NumberKind => "NumberKind"
Kind::BooleanKind => "BooleanKind"
Kind::TupleKind(items) =>
"TupleKind(" + items.map(kind_name).join(", ") + ")"
Kind::UndefinedKind => "UndefinedKind"
}
}
///|
fn kind_is_undefined(kind : Kind) -> Bool {
match kind {
Kind::UndefinedKind => true
_ => false
}
}
///|
fn combine_kinds(lhs : Kind, rhs : Kind) -> Kind {
match (lhs, rhs) {
(Kind::UndefinedKind, _) | (_, Kind::UndefinedKind) => Kind::UndefinedKind
(Kind::NumberKind, Kind::NumberKind) => Kind::NumberKind
(Kind::BooleanKind, Kind::BooleanKind) => Kind::BooleanKind
(Kind::TupleKind(items_lhs), Kind::TupleKind(items_rhs)) =>
if items_lhs.length() != items_rhs.length() {
Kind::UndefinedKind
} else {
let out : Array[Kind] = []
for i in 0.. Kind::UndefinedKind
}
}
///|
pub fn common_kind(kinds : Array[Kind]) -> Kind {
if kinds.is_empty() {
return Kind::UndefinedKind
}
let mut current = kinds[0]
for i in 1.. NumberSymbolKind? {
match name {
"I" => Some(NumberSymbolKind::ImaginaryUnit)
"pi" | "Pi" => Some(NumberSymbolKind::Pi)
"E" => Some(NumberSymbolKind::Exp1)
"EulerGamma" => Some(NumberSymbolKind::EulerGamma)
"GoldenRatio" => Some(NumberSymbolKind::GoldenRatio)
"Catalan" => Some(NumberSymbolKind::Catalan)
"oo" => Some(NumberSymbolKind::Infinity)
"-oo" => Some(NumberSymbolKind::NegativeInfinity)
"zoo" => Some(NumberSymbolKind::ComplexInfinity)
"nan" | "NaN" => Some(NumberSymbolKind::NaN)
_ => None
}
}
///|
pub fn standalone_function_head_display_name(name : String) -> String {
match name {
"Eq" => ""
"Ne" => ""
"Lt" => ""
"Le" => ""
"Gt" => ""
"Ge" => ""
"Tuple" => ""
"Dict" => ""
"Derivative" => ""
"Lambda" => ""
"IdentityFunction" =>
""
"Subs" => ""
"Zero" => ""
"One" => ""
"NegativeOne" => ""
"Half" => ""
"Integer" => ""
"Rational" => ""
"Pi" => ""
"Exp1" => ""
"ImaginaryUnit" => ""
"Infinity" => ""
"NegativeInfinity" => ""
"ComplexInfinity" => ""
"NaN" => ""
"BooleanTrue" => ""
"BooleanFalse" => ""
"Symbol" => ""
"Dummy" => ""
"Wild" => ""
_ =>
match builtin_head_display_override(name) {
Some(display) => display
None => name
}
}
}
///|
/// Build a derivative expression over a variable and optional order.
///
/// - Does: Constructs a core derivative node.
/// - Input: An expression, a differentiation variable, and an optional order.
/// - Returns: `Expr::Derivative`.
/// - Limits: This front door builds the expression object only; analytical
/// calculus routines live in higher-level packages.
pub fn derivative(expr : Expr, wrt : Expr, order? : Expr = int(1)) -> Expr {
Expr::Derivative(expr, [wrt, order])
}
///|
fn expr_can_diff_wrt(expr : Expr) -> Bool {
match normalize_legacy_expr(expr) {
Expr::Symbol(_) | Expr::Dummy(_, _) => true
Expr::Apply(Expr::UndefinedFunction(_), _) => true
Expr::Derivative(inner, deriv_args) =>
match normalize_legacy_expr(inner) {
Expr::Apply(Expr::UndefinedFunction(_), inner_args) => {
let mut all_symbol_args = true
for arg in inner_args {
match normalize_legacy_expr(arg) {
Expr::Symbol(_) | Expr::Dummy(_, _) => ()
_ => {
all_symbol_args = false
break
}
}
}
if !all_symbol_args {
return false
}
for pair in derivative_constructor_pairs(deriv_args[:]) {
let (wrt, order) = pair
if !expr_can_diff_wrt(wrt) ||
!derivative_order_is_positive_integer(order) {
return false
}
}
true
}
_ => false
}
_ => false
}
}
///|
fn derivative_order_is_zero(expr : Expr) -> Bool {
match normalize_legacy_expr(expr) {
Expr::Number(n) => n.is_integral() && n.is_zero()
_ => false
}
}
///|
fn derivative_order_is_positive_integer(expr : Expr) -> Bool {
match normalize_legacy_expr(expr) {
Expr::Number(n) => n.is_integral() && n.numerator().compare(0N) > 0
_ => false
}
}
///|
fn derivative_order_is_negative_integer(expr : Expr) -> Bool {
match normalize_legacy_expr(expr) {
Expr::Number(n) => n.is_integral() && n.numerator().compare(0N) < 0
_ => false
}
}
///|
fn is_explicit_derivative_order(expr : Expr) -> Bool {
match expr {
Expr::Number(n) => n.is_integral()
_ => false
}
}
///|
pub fn canonical_derivative_args(deriv_args : ArrayView[Expr]) -> Array[Expr] {
if deriv_args.length() == 0 {
return []
}
if deriv_args.length() % 2 == 0 {
let out : Array[Expr] = []
for arg in deriv_args {
out.push(arg)
}
return out
}
let out : Array[Expr] = []
for arg in deriv_args {
out.push(arg)
out.push(int(1))
}
out
}
///|
fn raw_derivative_args(deriv_args : ArrayView[Expr]) -> Array[Expr] {
let out : Array[Expr] = []
let mut i = 0
while i < deriv_args.length() {
let wrt = deriv_args[i]
if i + 1 < deriv_args.length() &&
is_explicit_derivative_order(deriv_args[i + 1]) {
out.push(wrt)
out.push(deriv_args[i + 1])
i += 2
} else {
out.push(wrt)
out.push(int(1))
i += 1
}
}
out
}
///|
pub fn raw_derivative(expr : Expr, deriv_args : ArrayView[Expr]) -> Expr {
Expr::Derivative(expr, raw_derivative_args(deriv_args))
}
///|
fn derivative_constructor_pairs(
deriv_args : ArrayView[Expr],
) -> Array[(Expr, Expr)] {
let out : Array[(Expr, Expr)] = []
let mut i = 0
while i < deriv_args.length() {
let current = normalize_legacy_expr(deriv_args[i])
match current {
Expr::Tuple([wrt, order]) => {
out.push((wrt, order))
i += 1
}
_ =>
if i + 1 < deriv_args.length() &&
is_explicit_derivative_order(normalize_legacy_expr(deriv_args[i + 1])) {
out.push((current, normalize_legacy_expr(deriv_args[i + 1])))
i += 2
} else {
out.push((current, int(1)))
i += 1
}
}
}
out
}
///|
pub fn derivative_signature_error(deriv_args : ArrayView[Expr]) -> String? {
for pair in derivative_constructor_pairs(deriv_args) {
let (wrt, order) = pair
if !expr_can_diff_wrt(wrt) {
return Some("Can't calculate derivative wrt " + expr_display(wrt) + ".")
}
if derivative_order_is_negative_integer(order) {
return Some("order of differentiation must be nonnegative")
}
}
None
}
///|
pub fn derivative_constructor_expr(
expr : Expr,
deriv_args : ArrayView[Expr],
) -> Expr {
let expr = normalize_legacy_expr(expr)
match derivative_signature_error(deriv_args) {
Some(_) => {
let legacy_args : Array[Expr] = [expr]
for arg in deriv_args {
legacy_args.push(normalize_legacy_expr(arg))
}
Expr::Apply(Expr::FunctionHead("Derivative"), legacy_args)
}
None => {
let out : Array[Expr] = []
for pair in derivative_constructor_pairs(deriv_args) {
let (wrt, order) = pair
if derivative_order_is_zero(order) {
()
} else {
out.push(wrt)
out.push(order)
}
}
if out.is_empty() {
expr
} else {
Expr::Derivative(expr, out)
}
}
}
}
///|
fn subs_items(expr : Expr) -> Array[Expr] {
match normalize_legacy_expr(expr) {
Expr::Tuple(items) => items
other => [other]
}
}
///|
fn subs_items_expr(items : Array[Expr]) -> Expr {
match items {
[] => Expr::Tuple([])
[item] => item
_ => Expr::Tuple(items)
}
}
///|
pub fn subs_signature_error(variable : Expr, value : Expr) -> String? {
let variables = subs_items(variable)
let values = subs_items(value)
let repeated : Array[String] = []
for variable in variables {
let count = variables.filter(current => current == variable).length()
if count > 1 {
let name = variable.to_string()
if !repeated.contains(name) {
repeated.push(name)
}
}
}
if !repeated.is_empty() {
Some(
"The following expressions appear more than once: " + repeated.join(", "),
)
} else if variables.length() != values.length() {
Some("Number of point values must be the same as the number of variables.")
} else {
None
}
}
///|
/// Build or extend a structural substitution expression.
///
/// - Does: Replaces one exact subexpression with another exact subexpression.
/// - Input: The target expression, the variable expression, and the replacement
/// expression.
/// - Returns: A rewritten `Expr`, often a `Subs` node.
/// - Limits: Matching is structural; it does not perform algebraic equivalence
/// matching.
///
/// ```mbt check
/// test "symcore subs_expr performs structural replacement" {
/// let x = Expr::Symbol("x")
/// let expr = add([x, int(1)])
/// inspect(
/// @symprint.pretty_string(subs_expr(expr, x, int(3))),
/// content="Subs(x + 1, x, 3)",
/// )
/// }
/// ```
pub fn subs_expr(expr : Expr, variable : Expr, value : Expr) -> Expr {
let expr = normalize_legacy_expr(expr)
let variable = normalize_legacy_expr(variable)
let value = normalize_legacy_expr(value)
match subs_signature_error(variable, value) {
Some(_) => Expr::Apply(Expr::FunctionHead("Subs"), [expr, variable, value])
None => {
let variables = subs_items(variable)
let values = subs_items(value)
if values.is_empty() {
expr
} else {
match expr {
Expr::Subs(inner, inner_variable, inner_value) =>
Expr::Subs(
inner,
subs_items_expr(subs_items(inner_variable) + variables),
subs_items_expr(subs_items(inner_value) + values),
)
_ => Expr::Subs(expr, variable, value)
}
}
}
}
}
///|
fn lambda_signature_items(vars : Expr) -> Array[Expr] {
match normalize_legacy_expr(vars) {
Expr::Tuple(items) => items
other => [other]
}
}
///|
fn is_lambda_signature_atom(expr : Expr) -> Bool {
match normalize_legacy_expr(expr) {
Expr::Symbol(_) | Expr::Dummy(_, _) | Expr::Wild(_, _, _) => true
_ => false
}
}
///|
fn lambda_identity_expr() -> Expr {
Expr::IdentityFunction
}
///|
pub fn lambda_arity(vars : Expr) -> Int? {
let items = lambda_signature_items(vars)
for item in items {
match normalize_legacy_expr(item) {
Expr::Tuple(_)
| Expr::Symbol(_)
| Expr::Dummy(_, _)
| Expr::Wild(_, _, _) => ()
_ => return None
}
}
Some(items.length())
}
///|
pub fn lambda_signature_error(vars : Expr) -> String? {
let seen : Array[Expr] = []
letrec check = (expr : Expr) => {
let normalized = normalize_legacy_expr(expr)
if is_lambda_signature_atom(normalized) {
if seen.contains(normalized) {
return Some("Duplicate symbol " + normalized.to_string())
}
seen.push(normalized)
return None
}
match normalized {
Expr::Tuple(items) => {
for item in items {
match check(item) {
Some(err) => return Some(err)
None => ()
}
}
None
}
_ =>
Some(
"Lambda signature should be only tuples and symbols, not " +
normalized.to_string(),
)
}
}
let top_level = lambda_signature_items(vars)
for item in top_level {
match check(item) {
Some(err) => return Some(err)
None => ()
}
}
None
}
///|
pub fn lambda_expr(vars : Expr, body : Expr) -> Expr {
let vars = normalize_legacy_expr(vars)
let body = normalize_legacy_expr(body)
match lambda_signature_error(vars) {
Some(_) => Expr::Apply(Expr::FunctionHead("Lambda"), [vars, body])
None => {
let items = lambda_signature_items(vars)
if items.length() == 1 && items[0] == body {
lambda_identity_expr()
} else {
Expr::Lambda(vars, body)
}
}
}
}
///|
pub fn expr_display(expr : Expr) -> String {
expr.to_string()
}
///|
pub fn sorted_dict_entries(items : Array[(Expr, Expr)]) -> Array[(Expr, Expr)] {
let sorted = items.copy()
sorted.sort_by((lhs, rhs) => {
let (lhs_key, lhs_value) = lhs
let (rhs_key, rhs_value) = rhs
let key_cmp = if is_number_atom(lhs_key) && !is_number_atom(rhs_key) {
-1
} else if !is_number_atom(lhs_key) && is_number_atom(rhs_key) {
1
} else {
compare_expr(lhs_key, rhs_key)
}
if key_cmp != 0 {
key_cmp
} else {
compare_expr(lhs_value, rhs_value)
}
})
sorted
}
///|
fn normalize_legacy_function(name : String, args : Array[Expr]) -> Expr {
let normalized_args = args.map(normalize_legacy_expr)
match name {
"True"
| "False"
| "Tuple"
| "S"
| "Integer"
| "Rational"
| "Float"
| "Eq"
| "Ne"
| "Lt"
| "Le"
| "Gt"
| "Ge"
| "Mod"
| "Derivative"
| "Subs"
| "Lambda"
| "Dict" =>
with_evaluate(true, fn() {
with_distribute(true, fn() {
with_exp_is_pow(false, fn() { function(name, normalized_args) })
})
})
_ => Expr::Apply(Expr::FunctionHead(name), normalized_args)
}
}
///|
/// Expose the structural view of an expression.
///
/// - Does: Converts `Expr` into its public structural discriminator.
/// - Input: Any `Expr`.
/// - Returns: An `ExprForm`.
/// - Limits: This is for branching and inspection, not a stable serialization
/// format.
pub fn expr_form(expr : Expr) -> ExprForm {
match expr {
Expr::Number(n) => ExprForm::Number(n)
Expr::Float(f) => ExprForm::Float(f)
Expr::ComplexFloat(z) => ExprForm::ComplexFloat(z)
Expr::NumberSymbol(kind) => ExprForm::NumberSymbol(kind)
Expr::Boolean(value) => ExprForm::Boolean(value)
Expr::IdentityFunction => ExprForm::IdentityFunction
Expr::Dummy(name, id) => ExprForm::Dummy(name, id)
Expr::Wild(name, exclude, properties) =>
ExprForm::Wild(name, exclude, properties)
Expr::WildFunction(name, nargs) => ExprForm::WildFunction(name, nargs)
Expr::FunctionHead(name) => ExprForm::FunctionHead(name)
Expr::UndefinedFunction(name) => ExprForm::UndefinedFunction(name)
Expr::Apply(head, args) => ExprForm::Apply(head, args)
Expr::Symbol(name) => ExprForm::Symbol(name)
Expr::Add(args) => ExprForm::Add(args)
Expr::Mul(args) => ExprForm::Mul(args)
Expr::Pow(base, exp) => ExprForm::Pow(base, exp)
Expr::Mod(lhs, rhs) => ExprForm::Mod(lhs, rhs)
Expr::Tuple(args) => ExprForm::Tuple(args)
Expr::Dict(items) => ExprForm::Dict(items)
Expr::Relational(op, lhs, rhs) => ExprForm::Relational(op, lhs, rhs)
Expr::Derivative(inner, deriv_args) =>
ExprForm::Derivative(inner, canonical_derivative_args(deriv_args))
Expr::Subs(inner, variable, value) => ExprForm::Subs(inner, variable, value)
Expr::Lambda(vars, body) => ExprForm::Lambda(vars, body)
Expr::Function(name, args) =>
expr_form(normalize_legacy_function(name, args))
}
}
///|
pub fn normalize_legacy_expr(expr : Expr) -> Expr {
match expr {
Expr::Add(args) => Expr::Add(args.map(normalize_legacy_expr))
Expr::Mul(args) => Expr::Mul(args.map(normalize_legacy_expr))
Expr::Pow(base, exp) =>
Expr::Pow(normalize_legacy_expr(base), normalize_legacy_expr(exp))
Expr::Mod(lhs, rhs) =>
Expr::Mod(normalize_legacy_expr(lhs), normalize_legacy_expr(rhs))
Expr::Tuple(args) => Expr::Tuple(args.map(normalize_legacy_expr))
Expr::Dict(items) => {
let out : Array[(Expr, Expr)] = []
for item in items {
let (key, value) = item
out.push((normalize_legacy_expr(key), normalize_legacy_expr(value)))
}
Expr::Dict(out)
}
Expr::Relational(op, lhs, rhs) =>
Expr::Relational(
op,
normalize_legacy_expr(lhs),
normalize_legacy_expr(rhs),
)
Expr::Derivative(inner, deriv_args) =>
raw_derivative(
normalize_legacy_expr(inner),
deriv_args.map(normalize_legacy_expr)[:],
)
Expr::Subs(inner, variable, value) =>
subs_expr(
normalize_legacy_expr(inner),
normalize_legacy_expr(variable),
normalize_legacy_expr(value),
)
Expr::Lambda(vars, body) =>
lambda_expr(normalize_legacy_expr(vars), normalize_legacy_expr(body))
Expr::Apply(head, args) => {
let head = normalize_legacy_expr(head)
let args = args.map(normalize_legacy_expr)
match raw_apply(head, args) {
Some(applied) => applied
None => Expr::Apply(head, args)
}
}
Expr::Function(name, args) => normalize_legacy_function(name, args)
_ => expr
}
}
///|
pub fn is_true(expr : Expr) -> Bool {
match expr_form(expr) {
ExprForm::Boolean(value) => value
_ => false
}
}
///|
pub fn bool_value(expr : Expr) -> Bool? {
match expr_form(expr) {
ExprForm::Boolean(value) => Some(value)
_ => None
}
}
///|
pub fn is_false(expr : Expr) -> Bool {
match expr_form(expr) {
ExprForm::Boolean(value) => !value
_ => false
}
}
///|
pub fn is_number_atom(expr : Expr) -> Bool {
match expr_form(expr) {
ExprForm::Number(_)
| ExprForm::Float(_)
| ExprForm::ComplexFloat(_)
| ExprForm::NumberSymbol(_) => true
_ => false
}
}
///|
pub fn number_symbol_kind(expr : Expr) -> NumberSymbolKind? {
match expr_form(expr) {
ExprForm::NumberSymbol(kind) => Some(kind)
_ => None
}
}
///|
pub fn is_number_symbol(expr : Expr) -> Bool {
match number_symbol_kind(expr) {
Some(_) => true
None => false
}
}
///|
pub fn is_infinite(expr : Expr) -> Bool {
match expr_form(expr) {
ExprForm::NumberSymbol(NumberSymbolKind::Infinity)
| ExprForm::NumberSymbol(NumberSymbolKind::NegativeInfinity)
| ExprForm::NumberSymbol(NumberSymbolKind::ComplexInfinity) => true
_ => false
}
}
///|
pub fn is_nan(expr : Expr) -> Bool {
match expr_form(expr) {
ExprForm::NumberSymbol(NumberSymbolKind::NaN) => true
_ => false
}
}
///|
pub fn is_zero(expr : Expr) -> Bool {
match expr_form(expr) {
ExprForm::Number(n) => n.is_zero()
ExprForm::Float(f) => @symnum.is_zero(f.to_mpf())
ExprForm::ComplexFloat(z) =>
@symnum.is_zero(z.to_mpc().real) && @symnum.is_zero(z.to_mpc().imag)
_ => false
}
}
///|
pub fn is_one(expr : Expr) -> Bool {
match expr_form(expr) {
ExprForm::Number(n) => n.is_one()
ExprForm::Float(f) =>
@symnum.mpf_cmp(
f.to_mpf(),
Float::from_int(1, prec=f.precision()).to_mpf(),
) ==
0
ExprForm::ComplexFloat(z) =>
@symnum.mpf_cmp(
z.to_mpc().real,
Float::from_int(1, prec=z.precision()).to_mpf(),
) ==
0 &&
@symnum.is_zero(z.to_mpc().imag)
_ => false
}
}
///|
pub fn is_finite_number_atom(expr : Expr) -> Bool {
match expr_form(expr) {
ExprForm::Number(_) => true
ExprForm::Float(f) => f.is_finite()
ExprForm::ComplexFloat(z) => z.is_finite()
ExprForm::NumberSymbol(_) => !is_infinite(expr) && !is_nan(expr)
_ => false
}
}
///|
pub fn is_real_number_atom(expr : Expr) -> Bool {
match expr_form(expr) {
ExprForm::Number(_) | ExprForm::Float(_) => true
ExprForm::NumberSymbol(kind) =>
match kind {
NumberSymbolKind::Pi
| NumberSymbolKind::Exp1
| NumberSymbolKind::EulerGamma
| NumberSymbolKind::GoldenRatio
| NumberSymbolKind::Catalan => true
_ => false
}
ExprForm::ComplexFloat(z) => @symnum.is_zero(z.to_mpc().imag)
_ => false
}
}
///|
pub fn is_complex_number_atom(expr : Expr) -> Bool {
match expr_form(expr) {
ExprForm::ComplexFloat(_) => true
_ => false
}
}
///|
pub fn is_atomic(expr : Expr) -> Bool {
match expr_form(expr) {
ExprForm::Number(_)
| ExprForm::Float(_)
| ExprForm::ComplexFloat(_)
| ExprForm::NumberSymbol(_)
| ExprForm::Symbol(_)
| ExprForm::Dummy(_, _)
| ExprForm::Wild(_, _, _)
| ExprForm::WildFunction(_, _)
| ExprForm::IdentityFunction
| ExprForm::FunctionHead(_)
| ExprForm::UndefinedFunction(_)
| ExprForm::Boolean(_) => true
ExprForm::Apply(_, args) => args.is_empty()
_ => false
}
}
///|
pub fn head_name(expr : Expr) -> String {
match expr_form(expr) {
ExprForm::Number(_) => "Number"
ExprForm::Float(_) => "Float"
ExprForm::ComplexFloat(_) => "ComplexFloat"
ExprForm::NumberSymbol(kind) => number_symbol_name(kind)
ExprForm::Symbol(name) => name
ExprForm::Dummy(name, _) => dummy_display_name(name)
ExprForm::Wild(name, _, _) => wild_display_name(name)
ExprForm::WildFunction(name, _) => wild_display_name(name)
ExprForm::IdentityFunction => "IdentityFunction"
ExprForm::FunctionHead(name) => name
ExprForm::UndefinedFunction(name) => name
ExprForm::Boolean(true) => "True"
ExprForm::Boolean(false) => "False"
ExprForm::Add(_) => "Add"
ExprForm::Mul(_) => "Mul"
ExprForm::Pow(_, _) => "Pow"
ExprForm::Mod(_, _) => "Mod"
ExprForm::Tuple(_) => "Tuple"
ExprForm::Dict(_) => "Dict"
ExprForm::Relational(op, _, _) =>
match op {
RelOp::Eq => "Eq"
RelOp::Ne => "Ne"
RelOp::Lt => "Lt"
RelOp::Le => "Le"
RelOp::Gt => "Gt"
RelOp::Ge => "Ge"
}
ExprForm::Derivative(_, _) => "Derivative"
ExprForm::Subs(_, _, _) => "Subs"
ExprForm::Lambda(_, _) => "Lambda"
ExprForm::Apply(head, _) =>
match function_head_name(head) {
Some(name) => name
None => "Apply"
}
}
}
///|
pub fn tuple_items(expr : Expr) -> Array[Expr]? {
let expr = normalize_legacy_expr(expr)
match expr_form(expr) {
ExprForm::Tuple(items) => Some(items)
_ => None
}
}
///|
pub fn dict_items(expr : Expr) -> Array[(Expr, Expr)]? {
let expr = normalize_legacy_expr(expr)
match expr_form(expr) {
ExprForm::Dict(items) => Some(items)
_ => None
}
}
///|
pub fn dummy_parts(expr : Expr) -> (String, Int)? {
match expr {
Expr::Dummy(name, id) => Some((name, id))
_ => None
}
}
///|
pub fn wild_name(expr : Expr) -> String? {
match expr {
Expr::Wild(name, _, _) | Expr::WildFunction(name, _) => Some(name)
_ => None
}
}
///|
pub fn wild_parts(expr : Expr) -> (String, Array[Expr], Array[WildProperty])? {
match expr {
Expr::Wild(name, exclude, properties) =>
Some((name, exclude.copy(), properties.copy()))
_ => None
}
}
///|
pub fn wild_function_parts(expr : Expr) -> (String, Array[Int])? {
match expr {
Expr::WildFunction(name, nargs) => Some((name, nargs.copy()))
_ => None
}
}
///|
pub fn undefined_application_parts(expr : Expr) -> (String, Array[Expr])? {
match application_parts(expr) {
Some((Expr::UndefinedFunction(name), args)) => Some((name, args))
_ => None
}
}
///|
pub fn function_head_name(expr : Expr) -> String? {
match expr_form(expr) {
ExprForm::FunctionHead(name) => Some(name)
ExprForm::UndefinedFunction(name) => Some(name)
ExprForm::WildFunction(name, _) => Some(name)
_ => None
}
}
///|
pub fn exact_number_kind(expr : Expr) -> ExactNumberKind? {
match expr_form(expr) {
ExprForm::Number(value) => Some(exact_number_kind_from_rational(value))
_ => None
}
}
///|
fn exact_number_kind_from_rational(
value : @symnum.BigRational,
) -> ExactNumberKind {
let half = @symnum.BigRational::from_ints(1, 2) catch {
_ => abort("invalid half rational")
}
if value.is_zero() {
ExactNumberKind::Zero
} else if value.is_one() {
ExactNumberKind::One
} else if value.compare(@symnum.BigRational::from_int(-1)) == 0 {
ExactNumberKind::NegativeOne
} else if value.compare(half) == 0 {
ExactNumberKind::Half
} else if value.is_integral() {
ExactNumberKind::Integer
} else {
ExactNumberKind::Rational
}
}
///|
pub fn exact_number_head_name(kind : ExactNumberKind) -> String {
match kind {
ExactNumberKind::Zero => "Zero"
ExactNumberKind::One => "One"
ExactNumberKind::NegativeOne => "NegativeOne"
ExactNumberKind::Half => "Half"
ExactNumberKind::Integer => "Integer"
ExactNumberKind::Rational => "Rational"
}
}
///|
pub fn singleton_head(expr : Expr) -> Expr? {
match expr_form(expr) {
ExprForm::Number(value) =>
Some(
Expr::FunctionHead(
exact_number_head_name(exact_number_kind_from_rational(value)),
),
)
ExprForm::NumberSymbol(kind) =>
Some(Expr::FunctionHead(number_symbol_func_name(kind)))
ExprForm::Boolean(value) =>
Some(Expr::FunctionHead(boolean_func_name(value)))
_ => None
}
}
///|
pub fn exact_number_num_den(expr : Expr) -> (BigInt, BigInt)? {
match expr_form(expr) {
ExprForm::Number(value) => Some((value.numerator(), value.denominator()))
_ => None
}
}
///|
fn is_boolean_application_head(name : String) -> Bool {
match name {
"And"
| "Or"
| "Not"
| "Xor"
| "Nand"
| "Nor"
| "Implies"
| "Equivalent"
| "ITE" => true
_ => false
}
}
///|
fn application_kind(head : Expr, args : Array[Expr]) -> Kind {
match function_head_name(head) {
Some(name) =>
if is_boolean_application_head(name) {
Kind::BooleanKind
} else {
match name {
"Tuple" => Kind::TupleKind(args.map(expr_kind))
"Dict" | "Lambda" => Kind::UndefinedKind
"Subs" | "Derivative" =>
if args.is_empty() {
Kind::UndefinedKind
} else {
expr_kind(args[0])
}
_ => Kind::NumberKind
}
}
None => Kind::UndefinedKind
}
}
///|
pub fn expr_kind(expr : Expr) -> Kind {
let expr = normalize_legacy_expr(expr)
match expr_form(expr) {
ExprForm::Boolean(_) | ExprForm::Relational(_, _, _) => Kind::BooleanKind
ExprForm::Tuple(items) => Kind::TupleKind(items.map(expr_kind))
ExprForm::Dict(_) => Kind::UndefinedKind
ExprForm::FunctionHead(_)
| ExprForm::UndefinedFunction(_)
| ExprForm::IdentityFunction
| ExprForm::Lambda(_, _) => Kind::UndefinedKind
ExprForm::Number(_)
| ExprForm::Float(_)
| ExprForm::ComplexFloat(_)
| ExprForm::NumberSymbol(_)
| ExprForm::Symbol(_)
| ExprForm::Dummy(_, _)
| ExprForm::Wild(_, _, _) => Kind::NumberKind
ExprForm::WildFunction(_, _) => Kind::UndefinedKind
ExprForm::Apply(head, args) => application_kind(head, args)
ExprForm::Add(items) | ExprForm::Mul(items) =>
common_kind(items.map(expr_kind))
ExprForm::Pow(base, exp) =>
match expr_kind(exp) {
Kind::NumberKind => expr_kind(base)
_ => Kind::UndefinedKind
}
ExprForm::Mod(lhs, rhs) => common_kind([expr_kind(lhs), expr_kind(rhs)])
ExprForm::Derivative(inner, _) | ExprForm::Subs(inner, _, _) =>
expr_kind(inner)
}
}
///|
fn number_symbol_func_name(kind : NumberSymbolKind) -> String {
match kind {
NumberSymbolKind::ImaginaryUnit => "ImaginaryUnit"
NumberSymbolKind::Pi => "Pi"
NumberSymbolKind::Exp1 => "Exp1"
NumberSymbolKind::EulerGamma => "EulerGamma"
NumberSymbolKind::GoldenRatio => "GoldenRatio"
NumberSymbolKind::Catalan => "Catalan"
NumberSymbolKind::Infinity => "Infinity"
NumberSymbolKind::NegativeInfinity => "NegativeInfinity"
NumberSymbolKind::ComplexInfinity => "ComplexInfinity"
NumberSymbolKind::NaN => "NaN"
}
}
///|
fn relational_func_name(op : RelOp) -> String {
match op {
RelOp::Eq => "Equality"
RelOp::Ne => "Unequality"
RelOp::Lt => "StrictLessThan"
RelOp::Le => "LessThan"
RelOp::Gt => "StrictGreaterThan"
RelOp::Ge => "GreaterThan"
}
}
///|
fn boolean_func_name(value : Bool) -> String {
if value {
"BooleanTrue"
} else {
"BooleanFalse"
}
}
///|
pub fn is_function_head(expr : Expr) -> Bool {
match function_head_name(expr) {
Some(_) => true
None => false
}
}
///|
pub fn relational_parts(expr : Expr) -> (RelOp, Expr, Expr)? {
let expr = normalize_legacy_expr(expr)
match expr_form(expr) {
ExprForm::Relational(op, lhs, rhs) => Some((op, lhs, rhs))
_ => None
}
}
///|
pub fn derivative_parts(expr : Expr) -> (Expr, Array[Expr])? {
let expr = normalize_legacy_expr(expr)
match expr_form(expr) {
ExprForm::Derivative(inner, deriv_args) => Some((inner, deriv_args))
_ => None
}
}
///|
pub fn subs_parts(expr : Expr) -> (Expr, Expr, Expr)? {
let expr = normalize_legacy_expr(expr)
match expr_form(expr) {
ExprForm::Subs(inner, variable, value) => Some((inner, variable, value))
_ => None
}
}
///|
pub fn lambda_parts(expr : Expr) -> (Expr, Expr)? {
let expr = normalize_legacy_expr(expr)
match expr_form(expr) {
ExprForm::Lambda(vars, body) => Some((vars, body))
_ => None
}
}
///|
pub fn applied_parts(expr : Expr) -> (String, Array[Expr])? {
let expr = normalize_legacy_expr(expr)
match expr_form(expr) {
ExprForm::Tuple(items) => Some(("Tuple", items))
ExprForm::Dict(items) => {
let args : Array[Expr] = []
for item in items {
let (key, value) = item
args.push(Expr::Tuple([key, value]))
}
Some(("Dict", args))
}
ExprForm::Relational(op, lhs, rhs) => {
let name = match op {
RelOp::Eq => "Eq"
RelOp::Ne => "Ne"
RelOp::Lt => "Lt"
RelOp::Le => "Le"
RelOp::Gt => "Gt"
RelOp::Ge => "Ge"
}
Some((name, [lhs, rhs]))
}
ExprForm::Derivative(inner, deriv_args) => {
let args : Array[Expr] = [inner]
for arg in deriv_args {
args.push(arg)
}
Some(("Derivative", args))
}
ExprForm::Subs(inner, variable, value) =>
Some(("Subs", [inner, variable, value]))
ExprForm::Lambda(vars, body) => Some(("Lambda", [vars, body]))
ExprForm::Mod(lhs, rhs) => Some(("Mod", [lhs, rhs]))
ExprForm::Apply(head, args) =>
match function_head_name(head) {
Some(name) => Some((name, args))
None => None
}
_ => None
}
}
///|
pub fn application_parts(expr : Expr) -> (Expr, Array[Expr])? {
let expr = normalize_legacy_expr(expr)
match expr {
Expr::Apply(head, args) => return Some((head, args.map(child => child)))
_ => ()
}
None
}
///|
pub fn is_application(expr : Expr) -> Bool {
application_parts(expr) is Some(_)
}
///|
pub fn func(expr : Expr) -> Expr? {
let expr = normalize_legacy_expr(expr)
match application_parts(expr) {
Some((head, _)) => Some(head)
None =>
match expr_form(expr) {
ExprForm::Number(value) =>
Some(
Expr::FunctionHead(
exact_number_head_name(exact_number_kind_from_rational(value)),
),
)
ExprForm::Float(_) => Some(Expr::FunctionHead("Float"))
ExprForm::ComplexFloat(_) => Some(Expr::FunctionHead("ComplexFloat"))
ExprForm::NumberSymbol(kind) =>
Some(Expr::FunctionHead(number_symbol_func_name(kind)))
ExprForm::Symbol(_) => Some(Expr::FunctionHead("Symbol"))
ExprForm::Dummy(_, _) => Some(Expr::FunctionHead("Dummy"))
ExprForm::Wild(_, _, _) => Some(Expr::FunctionHead("Wild"))
ExprForm::WildFunction(_, _) => Some(Expr::FunctionHead("WildFunction"))
ExprForm::IdentityFunction =>
Some(Expr::FunctionHead("IdentityFunction"))
ExprForm::Boolean(value) =>
Some(Expr::FunctionHead(boolean_func_name(value)))
ExprForm::FunctionHead(_) | ExprForm::UndefinedFunction(_) => None
ExprForm::Add(_) => Some(Expr::FunctionHead("Add"))
ExprForm::Mul(_) => Some(Expr::FunctionHead("Mul"))
ExprForm::Pow(_, _) => Some(Expr::FunctionHead("Pow"))
ExprForm::Mod(_, _) => Some(Expr::FunctionHead("Mod"))
ExprForm::Tuple(_) => Some(Expr::FunctionHead("Tuple"))
ExprForm::Dict(_) => Some(Expr::FunctionHead("Dict"))
ExprForm::Relational(op, _, _) =>
Some(Expr::FunctionHead(relational_func_name(op)))
ExprForm::Derivative(_, _) => Some(Expr::FunctionHead("Derivative"))
ExprForm::Subs(_, _, _) => Some(Expr::FunctionHead("Subs"))
ExprForm::Lambda(_, _) => Some(Expr::FunctionHead("Lambda"))
ExprForm::Apply(_, _) => None
}
}
}
///|
pub fn rebuild_application(expr : Expr, args : Array[Expr]) -> Expr? {
match application_parts(expr) {
Some((head, _)) => raw_apply(head, args)
None => None
}
}
///|
pub fn application_name(expr : Expr) -> String? {
match application_parts(expr) {
Some((head, _)) => function_head_name(head)
None => None
}
}
///|
pub fn application_args(expr : Expr) -> Array[Expr]? {
match application_parts(expr) {
Some((_, args)) => Some(args)
None => None
}
}
///|
pub fn application_has_name(
expr : Expr,
name : String,
arity? : Int = -1,
) -> Bool {
match application_parts(expr) {
Some((head, args)) =>
match function_head_name(head) {
Some(head_name) =>
head_name == name && (arity < 0 || args.length() == arity)
None => false
}
None => false
}
}
///|
pub fn args(expr : Expr) -> Array[Expr] {
let expr = normalize_legacy_expr(expr)
match expr_form(expr) {
ExprForm::Number(_)
| ExprForm::Float(_)
| ExprForm::ComplexFloat(_)
| ExprForm::NumberSymbol(_)
| ExprForm::Symbol(_)
| ExprForm::Dummy(_, _)
| ExprForm::Wild(_, _, _)
| ExprForm::WildFunction(_, _)
| ExprForm::IdentityFunction
| ExprForm::FunctionHead(_)
| ExprForm::UndefinedFunction(_)
| ExprForm::Boolean(_) => []
ExprForm::Add(items)
| ExprForm::Mul(items)
| ExprForm::Tuple(items)
| ExprForm::Apply(_, items) => items.copy()
ExprForm::Dict(items) => {
let sorted = sorted_dict_entries(items)
let out : Array[Expr] = []
for item in sorted {
let (key, value) = item
out.push(Expr::Tuple([key, value]))
}
out
}
ExprForm::Pow(base, exp) => [base, exp]
ExprForm::Mod(lhs, rhs) => [lhs, rhs]
ExprForm::Relational(_, lhs, rhs) => [lhs, rhs]
ExprForm::Derivative(inner, deriv_args) => {
let out : Array[Expr] = [inner]
let mut i = 0
while i + 1 < deriv_args.length() {
out.push(Expr::Tuple([deriv_args[i], deriv_args[i + 1]]))
i += 2
}
out
}
ExprForm::Subs(inner, variable, value) =>
[
inner,
match variable {
Expr::Tuple(_) => variable
_ => Expr::Tuple([variable])
},
match value {
Expr::Tuple(_) => value
_ => Expr::Tuple([value])
},
]
ExprForm::Lambda(vars, body) =>
[
match vars {
Expr::Tuple(_) => vars
_ => Expr::Tuple([vars])
},
body,
]
}
}
///|
fn remove_bound_symbol(out : Map[String, Expr], expr : Expr) -> Unit {
match expr {
Expr::Symbol(name) => ignore(out.remove("sym:\{name}"))
Expr::Dummy(_, id) => ignore(out.remove("dummy:\{id}"))
Expr::Wild(name, _, _) => ignore(out.remove("wild:\{name}"))
Expr::WildFunction(name, _) => ignore(out.remove("wildf:\{name}"))
Expr::Tuple(items) =>
for item in items {
remove_bound_symbol(out, item)
}
Expr::Dict(items) =>
for item in items {
let (key, value) = item
remove_bound_symbol(out, key)
remove_bound_symbol(out, value)
}
_ =>
match expr_form(expr) {
ExprForm::Tuple(items) =>
for item in items {
remove_bound_symbol(out, item)
}
ExprForm::Dict(items) =>
for item in items {
let (key, value) = item
remove_bound_symbol(out, key)
remove_bound_symbol(out, value)
}
_ => ()
}
}
}
///|
fn free_symbol_map(expr : Expr, out : Map[String, Expr]) -> Unit {
match expr {
Expr::Symbol(name) => {
out.set("sym:\{name}", expr)
return
}
Expr::Dummy(_, id) => {
out.set("dummy:\{id}", expr)
return
}
Expr::Wild(name, _, _) => {
out.set("wild:\{name}", expr)
return
}
Expr::WildFunction(name, _) => {
out.set("wildf:\{name}", expr)
return
}
Expr::FunctionHead(_) | Expr::UndefinedFunction(_) | Expr::Boolean(_) =>
return
_ => ()
}
match expr_form(expr) {
ExprForm::Number(_)
| ExprForm::Float(_)
| ExprForm::ComplexFloat(_)
| ExprForm::NumberSymbol(_)
| ExprForm::Dummy(_, _)
| ExprForm::Wild(_, _, _)
| ExprForm::WildFunction(_, _)
| ExprForm::IdentityFunction
| ExprForm::FunctionHead(_)
| ExprForm::UndefinedFunction(_)
| ExprForm::Boolean(_) => ()
ExprForm::Symbol(name) => out.set("sym:\{name}", expr)
ExprForm::Add(items) | ExprForm::Mul(items) | ExprForm::Tuple(items) =>
for item in items {
free_symbol_map(item, out)
}
ExprForm::Apply(head, items) => {
free_symbol_map(head, out)
for item in items {
free_symbol_map(item, out)
}
}
ExprForm::Dict(items) =>
for item in items {
let (key, value) = item
free_symbol_map(key, out)
free_symbol_map(value, out)
}
ExprForm::Pow(base, exp) => {
free_symbol_map(base, out)
free_symbol_map(exp, out)
}
ExprForm::Mod(lhs, rhs) => {
free_symbol_map(lhs, out)
free_symbol_map(rhs, out)
}
ExprForm::Relational(_, lhs, rhs) => {
free_symbol_map(lhs, out)
free_symbol_map(rhs, out)
}
ExprForm::Derivative(inner, deriv_args) => {
free_symbol_map(inner, out)
let pair_count = deriv_args.length() / 2
for i in 0.. {
let inner_symbols : Map[String, Expr] = {}
free_symbol_map(inner, inner_symbols)
remove_bound_symbol(inner_symbols, variable)
for key, value0 in inner_symbols {
out.set(key, value0)
}
free_symbol_map(value, out)
}
ExprForm::Lambda(vars, body) => {
free_symbol_map(body, out)
remove_bound_symbol(out, vars)
}
}
}
///|
fn exact_number_matches_head(
kind : ExactNumberKind,
needle_name : String,
) -> Bool {
match kind {
ExactNumberKind::Zero =>
match needle_name {
"Zero"
| "Integer"
| "Rational"
| "Number"
| "AtomicExpr"
| "Atom"
| "Expr"
| "Basic" => true
_ => false
}
ExactNumberKind::One =>
match needle_name {
"One"
| "Integer"
| "Rational"
| "Number"
| "AtomicExpr"
| "Atom"
| "Expr"
| "Basic" => true
_ => false
}
ExactNumberKind::NegativeOne =>
match needle_name {
"NegativeOne"
| "Integer"
| "Rational"
| "Number"
| "AtomicExpr"
| "Atom"
| "Expr"
| "Basic" => true
_ => false
}
ExactNumberKind::Half =>
match needle_name {
"Half"
| "Rational"
| "Number"
| "AtomicExpr"
| "Atom"
| "Expr"
| "Basic" => true
_ => false
}
ExactNumberKind::Integer =>
match needle_name {
"Integer"
| "Rational"
| "Number"
| "AtomicExpr"
| "Atom"
| "Expr"
| "Basic" => true
_ => false
}
ExactNumberKind::Rational =>
match needle_name {
"Rational" | "Number" | "AtomicExpr" | "Atom" | "Expr" | "Basic" => true
_ => false
}
}
}
///|
fn expr_matches_class_head(expr : Expr, needle_name : String) -> Bool {
let expr = normalize_legacy_expr(expr)
match expr_form(expr) {
ExprForm::Number(value) =>
exact_number_matches_head(
exact_number_kind_from_rational(value),
needle_name,
)
ExprForm::Float(_) =>
match needle_name {
"Float" | "Number" | "AtomicExpr" | "Atom" | "Expr" | "Basic" => true
_ => false
}
ExprForm::ComplexFloat(_) =>
match needle_name {
"ComplexFloat" | "Number" | "AtomicExpr" | "Atom" | "Expr" | "Basic" =>
true
_ => false
}
ExprForm::NumberSymbol(_) =>
match needle_name {
"NumberSymbol" | "Number" | "AtomicExpr" | "Atom" | "Expr" | "Basic" =>
true
_ => false
}
ExprForm::Symbol(_) =>
match needle_name {
"Symbol" | "AtomicExpr" | "Atom" | "Expr" | "Basic" => true
_ => false
}
ExprForm::Dummy(_, _) =>
match needle_name {
"Dummy" | "Symbol" | "AtomicExpr" | "Atom" | "Expr" | "Basic" => true
_ => false
}
ExprForm::Wild(_, _, _) =>
match needle_name {
"Wild" | "Symbol" | "AtomicExpr" | "Atom" | "Expr" | "Basic" => true
_ => false
}
ExprForm::WildFunction(_, _) =>
match needle_name {
"WildFunction" | "Basic" => true
_ => false
}
ExprForm::FunctionHead(_) =>
match needle_name {
"FunctionClass" | "Basic" => true
_ => false
}
ExprForm::UndefinedFunction(_) =>
match needle_name {
"UndefinedFunction" | "FunctionClass" | "Basic" => true
_ => false
}
ExprForm::IdentityFunction =>
match needle_name {
"IdentityFunction" | "FunctionClass" | "Basic" => true
_ => false
}
ExprForm::Boolean(_) =>
match needle_name {
"Boolean" | "Basic" => true
_ => false
}
ExprForm::Add(_) =>
match needle_name {
"Add" | "Expr" | "Basic" => true
_ => false
}
ExprForm::Mul(_) =>
match needle_name {
"Mul" | "Expr" | "Basic" => true
_ => false
}
ExprForm::Pow(_, _) =>
match needle_name {
"Pow" | "Expr" | "Basic" => true
_ => false
}
ExprForm::Mod(_, _) =>
match needle_name {
"Mod" | "Function" | "Application" | "Expr" | "Basic" => true
_ => false
}
ExprForm::Tuple(_) =>
match needle_name {
"Tuple" | "Basic" => true
_ => false
}
ExprForm::Dict(_) =>
match needle_name {
"Dict" | "Basic" => true
_ => false
}
ExprForm::Relational(_, _, _) =>
match needle_name {
"Relational" | "Boolean" | "Basic" => true
_ => false
}
ExprForm::Derivative(_, _) =>
match needle_name {
"Derivative" | "Expr" | "Basic" => true
_ => false
}
ExprForm::Subs(_, _, _) =>
match needle_name {
"Subs" | "Expr" | "Basic" => true
_ => false
}
ExprForm::Lambda(_, _) =>
match needle_name {
"Lambda" | "Expr" | "Basic" => true
_ => false
}
ExprForm::Apply(_, _) =>
match needle_name {
"Function" | "Application" | "Expr" | "Basic" => true
_ => false
}
}
}
///|
fn contains_commutative_subexpr(
expr_items : Array[Expr],
needle_items : Array[Expr],
) -> Bool {
let remaining = expr_items.copy()
for needle_item in needle_items {
let mut found = false
for i in 0.. Bool {
let expr = normalize_legacy_expr(expr)
let needle = normalize_legacy_expr(needle)
match (expr_form(expr), expr_form(needle)) {
(ExprForm::Add(expr_items), ExprForm::Add(needle_items)) =>
contains_commutative_subexpr(expr_items, needle_items)
(ExprForm::Mul(expr_items), ExprForm::Mul(needle_items)) =>
contains_commutative_subexpr(expr_items, needle_items)
_ => false
}
}
///|
/// Collect the free symbolic variables occurring in an expression.
///
/// - Does: Walks an expression and extracts its free symbols.
/// - Input: Any `Expr`.
/// - Returns: `Array[Expr]`, typically containing `Expr::Symbol` items.
/// - Limits: The order is canonicalized rather than source-order oriented.
pub fn free_symbols(expr : Expr) -> Array[Expr] {
let out : Map[String, Expr] = {}
free_symbol_map(expr, out)
let names : Array[String] = []
for name in out.keys() {
names.push(name)
}
names.sort()
let symbols : Array[Expr] = []
for name in names {
match out.get(name) {
Some(value) => symbols.push(value)
None => ()
}
}
symbols
}
///|
pub fn has(expr : Expr, needle : Expr) -> Bool {
let expr = normalize_legacy_expr(expr)
let needle = normalize_legacy_expr(needle)
if expr == needle {
return true
}
match needle {
Expr::FunctionHead(name) if expr_matches_class_head(expr, name) =>
return true
_ => ()
}
match func(expr) {
Some(head) if head == needle => return true
_ => ()
}
if has_matcher(expr, needle) {
return true
}
match application_parts(expr) {
Some((head, app_args)) => {
if has(head, needle) {
return true
}
for child in app_args {
if has(child, needle) {
return true
}
}
return false
}
None => ()
}
for child in args(expr) {
if has(child, needle) {
return true
}
}
false
}
///|
fn push_unique_sorted(out : Array[Expr], expr : Expr) -> Unit {
for item in out {
if compare_expr(item, expr) == 0 {
return
}
}
out.push(expr)
out.mut_view().sort_by((a, b) => compare_expr(a, b))
}
///|
fn collect_atoms(expr : Expr, out : Array[Expr]) -> Unit {
if is_atomic(expr) {
push_unique_sorted(out, expr)
return
}
for child in args(expr) {
collect_atoms(child, out)
}
}
///|
pub fn atoms(expr : Expr) -> Array[Expr] {
let out : Array[Expr] = []
collect_atoms(expr, out)
out
}