#![allow(deprecated)]
use std::collections::HashMap;
use std::convert::AsRef;
use std::convert::TryFrom;
use std::fmt;
use std::fmt::Debug;
use std::hash::Hasher;
use std::ops::Add;
use std::ops::Div;
use std::ops::Mul;
use std::ops::Neg;
use std::ops::Rem;
use std::ops::Sub;
use std::sync::LazyLock;
use std::sync::RwLock;
use num_bigint::BigInt;
use num_bigint::ToBigInt as _;
use num_rational::BigRational;
use num_rational::Ratio;
use num_traits::One;
use num_traits::Signed;
use num_traits::ToPrimitive;
use num_traits::Zero;
use ordered_float::OrderedFloat;
use super::dag_mgr::DAG_MANAGER;
use super::dag_mgr::DagOp;
use super::expr::Expr;
use super::expr::SparsePolynomial;
impl AsRef<Self> for Expr {
fn as_ref(&self) -> &Self {
self
}
}
macro_rules! unary_constructor {
($name:ident, $op:ident) => {
#[doc = stringify!($op)]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn $name<A>(a: A) -> Expr
where
A: AsRef<Expr>,
{
let dag_a = DAG_MANAGER
.get_or_create(a.as_ref())
.expect("DAG manager get_or_create failed");
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::$op, vec![dag_a])
.expect("DAG manager get_or_create_normalized failed");
Expr::Dag(node)
}
};
}
macro_rules! binary_constructor {
($name:ident, $op:ident) => {
#[doc = stringify!($op)]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn $name<A, B>(
a: A,
b: B,
) -> Expr
where
A: AsRef<Expr>,
B: AsRef<Expr>,
{
let dag_a = DAG_MANAGER
.get_or_create(a.as_ref())
.expect("DAG manager get_or_create failed");
let dag_b = DAG_MANAGER
.get_or_create(b.as_ref())
.expect("DAG manager get_or_create failed");
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::$op, vec![dag_a, dag_b])
.expect("DAG manager get_or_create_normalized failed");
Expr::Dag(node)
}
};
}
macro_rules! n_ary_constructor {
($name:ident, $op:ident) => {
#[doc = stringify!($op)]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn $name<I, T>(elements: I) -> Expr
where
I: IntoIterator<Item = T>,
T: AsRef<Expr>,
{
let children_nodes = elements
.into_iter()
.map(|child| {
DAG_MANAGER
.get_or_create(child.as_ref())
.expect("DAG manager get_or_create failed")
})
.collect::<Vec<_>>();
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::$op, children_nodes)
.expect("DAG manager get_or_create_normalized failed");
Expr::Dag(node)
}
};
}
#[deprecated(
since = "0.1.18",
note = "Please use the \
'UnaryList' variant \
instead."
)]
macro_rules! unary_constructor_deprecated {
($name:ident, $op:ident) => {
#[doc = stringify!($op)]
#[deprecated(since = "0.1.18", note = "Please use the 'UnaryList' variant instead.")]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn $name<A>(a: A) -> Expr
where
A: AsRef<Expr>,
{
let dag_a = DAG_MANAGER
.get_or_create(a.as_ref())
.expect("DAG manager get_or_create failed");
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::$op, vec![dag_a])
.expect("DAG manager get_or_create_normalized failed");
Expr::Dag(node)
}
};
}
#[deprecated(
since = "0.1.18",
note = "Please use the \
'BinaryList' variant \
instead."
)]
macro_rules! binary_constructor_deprecated {
($name:ident, $op:ident) => {
#[doc = stringify!($op)]
#[deprecated(
since = "0.1.18",
note = "Please use the 'BinaryList' variant instead."
)]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn $name<A, B>(
a: A,
b: B,
) -> Expr
where
A: AsRef<Expr>,
B: AsRef<Expr>,
{
let dag_a = DAG_MANAGER
.get_or_create(a.as_ref())
.expect("DAG manager get_or_create failed");
let dag_b = DAG_MANAGER
.get_or_create(b.as_ref())
.expect("DAG manager get_or_create failed");
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::$op, vec![dag_a, dag_b])
.expect("DAG manager get_or_create_normalized failed");
Expr::Dag(node)
}
};
}
#[deprecated(
since = "0.1.18",
note = "Please use the 'NaryList' \
variant instead."
)]
macro_rules! n_ary_constructor_deprecated {
($name:ident, $op:ident) => {
#[doc = stringify!($op)]
#[deprecated(since = "0.1.18", note = "Please use the 'NaryList' variant instead.")]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn $name<I, T>(elements: I) -> Expr
where
I: IntoIterator<Item = T>,
T: AsRef<Expr>,
{
let children_nodes = elements
.into_iter()
.map(|child| {
DAG_MANAGER
.get_or_create(child.as_ref())
.expect("DAG manager get_or_create failed")
})
.collect::<Vec<_>>();
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::$op, children_nodes)
.expect("DAG manager get_or_create_normalized failed");
Expr::Dag(node)
}
};
}
impl Expr {
unary_constructor!(new_sin, Sin);
unary_constructor!(new_cos, Cos);
unary_constructor!(new_tan, Tan);
unary_constructor!(new_exp, Exp);
unary_constructor!(new_log, Log);
unary_constructor!(new_neg, Neg);
unary_constructor!(new_abs, Abs);
unary_constructor!(new_sqrt, Sqrt);
unary_constructor!(new_transpose, Transpose);
unary_constructor!(new_inverse, Inverse);
unary_constructor!(new_sec, Sec);
unary_constructor!(new_csc, Csc);
unary_constructor!(new_cot, Cot);
unary_constructor!(new_arcsin, ArcSin);
unary_constructor!(new_arccos, ArcCos);
unary_constructor!(new_arctan, ArcTan);
unary_constructor!(new_arcsec, ArcSec);
unary_constructor!(new_arccsc, ArcCsc);
unary_constructor!(new_arccot, ArcCot);
unary_constructor!(new_sinh, Sinh);
unary_constructor!(new_cosh, Cosh);
unary_constructor!(new_tanh, Tanh);
unary_constructor!(new_sech, Sech);
unary_constructor!(new_csch, Csch);
unary_constructor!(new_coth, Coth);
unary_constructor!(new_arcsinh, ArcSinh);
unary_constructor!(new_arccosh, ArcCosh);
unary_constructor!(new_arctanh, ArcTanh);
unary_constructor!(new_arcsech, ArcSech);
unary_constructor!(new_arccsch, ArcCsch);
unary_constructor!(new_arccoth, ArcCoth);
unary_constructor!(new_not, Not);
unary_constructor!(new_floor, Floor);
unary_constructor!(new_gamma, Gamma);
unary_constructor!(new_erf, Erf);
unary_constructor!(new_erfc, Erfc);
unary_constructor!(new_erfi, Erfi);
unary_constructor!(new_zeta, Zeta);
unary_constructor!(new_digamma, Digamma);
binary_constructor!(new_add, Add);
binary_constructor!(new_sub, Sub);
binary_constructor!(new_mul, Mul);
binary_constructor!(new_div, Div);
binary_constructor!(new_pow, Power);
binary_constructor!(new_complex, Complex);
binary_constructor!(new_matrix_mul, MatrixMul);
binary_constructor!(new_matrix_vec_mul, MatrixVecMul);
binary_constructor!(new_log_base, LogBase);
binary_constructor!(new_atan2, Atan2);
binary_constructor!(new_xor, Xor);
binary_constructor!(new_implies, Implies);
binary_constructor!(new_equivalent, Equivalent);
binary_constructor!(new_beta, Beta);
binary_constructor!(new_bessel_j, BesselJ);
binary_constructor!(new_bessel_y, BesselY);
binary_constructor!(new_legendre_p, LegendreP);
binary_constructor!(new_laguerre_l, LaguerreL);
binary_constructor!(new_hermite_h, HermiteH);
binary_constructor!(new_kronecker_delta, KroneckerDelta);
binary_constructor!(new_apply, Apply);
n_ary_constructor!(new_vector, Vector);
n_ary_constructor!(new_and, And);
n_ary_constructor!(new_or, Or);
n_ary_constructor!(new_union, Union);
n_ary_constructor!(new_polynomial, Polynomial);
n_ary_constructor!(new_tuple, Tuple);
unary_constructor_deprecated!(new_custom_arc_one, CustomArcOne);
binary_constructor_deprecated!(new_custom_arc_two, CustomArcTwo);
n_ary_constructor_deprecated!(new_custom_vec_one, CustomVecOne);
n_ary_constructor_deprecated!(new_custom_vec_two, CustomVecTwo);
n_ary_constructor_deprecated!(new_custom_vec_three, CustomVecThree);
n_ary_constructor_deprecated!(new_custom_vec_four, CustomVecFour);
n_ary_constructor_deprecated!(new_custom_vec_five, CustomVecFive);
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_constant(c: f64) -> Self {
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::Constant(OrderedFloat(c)), vec![])
.expect("Value is valid");
Self::Dag(node)
}
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_variable(name: &str) -> Self {
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::Variable(name.to_string()), vec![])
.expect("Value is valid");
Self::Dag(node)
}
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_bigint(i: BigInt) -> Self {
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::BigInt(i), vec![])
.expect("Value is valid");
Self::Dag(node)
}
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_rational(r: BigRational) -> Self {
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::Rational(r), vec![])
.expect("Value is valid");
Self::Dag(node)
}
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_pi() -> Self {
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::Pi, vec![])
.expect("Value is valid");
Self::Dag(node)
}
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_e() -> Self {
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::E, vec![])
.expect("Value is valid");
Self::Dag(node)
}
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_infinity() -> Self {
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::Infinity, vec![])
.expect("Value is valid");
Self::Dag(node)
}
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_negative_infinity() -> Self {
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::NegativeInfinity, vec![])
.expect("Value is valid");
Self::Dag(node)
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_matrix<I, J, T>(elements: I) -> Self
where
I: IntoIterator<Item = J>,
J: IntoIterator<Item = T>,
T: AsRef<Self>,
{
let mut flat_children_nodes = Vec::new();
let mut rows = 0;
let mut cols = 0;
for row_iter in elements {
rows += 1;
let mut current_cols = 0;
for element in row_iter {
let node = DAG_MANAGER
.get_or_create(element.as_ref())
.expect("Value is valid");
flat_children_nodes.push(node);
current_cols += 1;
}
if cols == 0 {
cols = current_cols;
} else if current_cols != cols {
panic!(
"Matrix rows must \
have consistent \
length"
);
}
}
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::Matrix { rows, cols }, flat_children_nodes)
.expect("Value is valid");
Self::Dag(node)
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_predicate<I, T>(
name: &str,
args: I,
) -> Self
where
I: IntoIterator<Item = T>,
T: AsRef<Self>,
{
let children_nodes = args
.into_iter()
.map(|child| {
DAG_MANAGER
.get_or_create(child.as_ref())
.expect("Value is valid")
})
.collect::<Vec<_>>();
let node = DAG_MANAGER
.get_or_create_normalized(
DagOp::Predicate {
name: name.to_string(),
},
children_nodes,
)
.expect("Value is valid");
Self::Dag(node)
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_forall<A>(
var: &str,
expr: A,
) -> Self
where
A: AsRef<Self>,
{
let child_node = DAG_MANAGER
.get_or_create(expr.as_ref())
.expect("Value is valid");
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::ForAll(var.to_string()), vec![child_node])
.expect("Value is valid");
Self::Dag(node)
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_exists<A>(
var: &str,
expr: A,
) -> Self
where
A: AsRef<Self>,
{
let child_node = DAG_MANAGER
.get_or_create(expr.as_ref())
.expect("Value is valid");
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::Exists(var.to_string()), vec![child_node])
.expect("Value is valid");
Self::Dag(node)
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_interval<A, B>(
lower: A,
upper: B,
incl_lower: bool,
incl_upper: bool,
) -> Self
where
A: AsRef<Self>,
B: AsRef<Self>,
{
let dag_lower = DAG_MANAGER
.get_or_create(lower.as_ref())
.expect("Value is valid");
let dag_upper = DAG_MANAGER
.get_or_create(upper.as_ref())
.expect("Value is valid");
let node = DAG_MANAGER
.get_or_create_normalized(
DagOp::Interval(incl_lower, incl_upper),
vec![dag_lower, dag_upper],
)
.expect("Value is valid");
Self::Dag(node)
}
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_derivative<A>(
function: A,
variable: String,
) -> Self
where
A: AsRef<Self>,
{
let dag_function = DAG_MANAGER
.get_or_create(function.as_ref())
.expect("Value is valid");
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::Derivative(variable), vec![dag_function])
.expect("Value is valid");
Self::Dag(node)
}
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_derivativen<A, B>(
function: A,
variable: String,
grades: B,
) -> Self
where
A: AsRef<Self>,
B: AsRef<Self>,
{
let dag_function = DAG_MANAGER
.get_or_create(function.as_ref())
.expect("Value is valid");
let dag_grades = DAG_MANAGER
.get_or_create(grades.as_ref())
.expect("Value is valid");
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::DerivativeN(variable), vec![dag_function, dag_grades])
.expect("Value is valid");
Self::Dag(node)
}
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_sparse_polynomial(p: SparsePolynomial) -> Self {
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::SparsePolynomial(p), vec![])
.expect("Value is valid");
Self::Dag(node)
}
#[deprecated(
since = "0.1.18",
note = "Please use the \
'UnaryList' variant \
instead."
)]
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_custom_zero() -> Self {
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::CustomZero, vec![])
.expect("Value is valid");
Self::Dag(node)
}
#[deprecated(
since = "0.1.18",
note = "Please use the \
'UnaryList' variant \
instead."
)]
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_custom_string(s: &str) -> Self {
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::CustomString(s.to_string()), vec![])
.expect("Value is valid");
Self::Dag(node)
}
#[deprecated(
since = "0.1.18",
note = "Please use the \
'NaryList' variant \
instead."
)]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_custom_arc_three<A, B, C>(
a: A,
b: B,
c: C,
) -> Self
where
A: AsRef<Self>,
B: AsRef<Self>,
C: AsRef<Self>,
{
let dag_a = DAG_MANAGER
.get_or_create(a.as_ref())
.expect("Value is valid");
let dag_b = DAG_MANAGER
.get_or_create(b.as_ref())
.expect("Value is valid");
let dag_c = DAG_MANAGER
.get_or_create(c.as_ref())
.expect("Value is valid");
let children = vec![dag_a, dag_b, dag_c];
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::CustomArcThree, children)
.expect("Value is valid");
Self::Dag(node)
}
#[deprecated(
since = "0.1.18",
note = "Please use the \
'NaryList' variant \
instead."
)]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_custom_arc_four<A, B, C, D>(
a: A,
b: B,
c: C,
d: D,
) -> Self
where
A: AsRef<Self>,
B: AsRef<Self>,
C: AsRef<Self>,
D: AsRef<Self>,
{
let dag_a = DAG_MANAGER
.get_or_create(a.as_ref())
.expect("Value is valid");
let dag_b = DAG_MANAGER
.get_or_create(b.as_ref())
.expect("Value is valid");
let dag_c = DAG_MANAGER
.get_or_create(c.as_ref())
.expect("Value is valid");
let dag_d = DAG_MANAGER
.get_or_create(d.as_ref())
.expect("Value is valid");
let children = vec![dag_a, dag_b, dag_c, dag_d];
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::CustomArcFour, children)
.expect("Value is valid");
Self::Dag(node)
}
#[deprecated(
since = "0.1.18",
note = "Please use the \
'NaryList' variant \
instead."
)]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn new_custom_arc_five<A, B, C, D, E>(
a: A,
b: B,
c: C,
d: D,
e: E,
) -> Self
where
A: AsRef<Self>,
B: AsRef<Self>,
C: AsRef<Self>,
D: AsRef<Self>,
E: AsRef<Self>,
{
let dag_a = DAG_MANAGER
.get_or_create(a.as_ref())
.expect("Value is valid");
let dag_b = DAG_MANAGER
.get_or_create(b.as_ref())
.expect("Value is valid");
let dag_c = DAG_MANAGER
.get_or_create(c.as_ref())
.expect("Value is valid");
let dag_d = DAG_MANAGER
.get_or_create(d.as_ref())
.expect("Value is valid");
let dag_e = DAG_MANAGER
.get_or_create(e.as_ref())
.expect("Value is valid");
let children = vec![dag_a, dag_b, dag_c, dag_d, dag_e];
let node = DAG_MANAGER
.get_or_create_normalized(DagOp::CustomArcFive, children)
.expect("Value is valid");
Self::Dag(node)
}
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub const fn is_dag(&self) -> bool {
matches!(self, Self::Dag(_))
}
#[inline]
pub fn to_dag(&self) -> Result<Self, String> {
match self {
| Self::Dag(_) => Ok(self.clone()),
| _ => {
let dag_node = DAG_MANAGER.get_or_create(self)?;
Ok(Self::Dag(dag_node))
},
}
}
#[inline]
pub fn to_dag_form(&mut self) {
if let Ok(dag) = self.to_dag() {
*self = dag;
}
}
#[inline]
pub fn to_ast(&self) -> Result<Self, String> {
match self {
| Self::Dag(node) => node.to_expr(),
| _ => Ok(self.clone()),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct DynamicOpProperties {
pub name: String,
pub description: String,
pub is_associative: bool,
pub is_commutative: bool,
}
pub static DYNAMIC_OP_REGISTRY: LazyLock<RwLock<HashMap<String, DynamicOpProperties>>> =
LazyLock::new(|| RwLock::new(HashMap::new()));
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn register_dynamic_op(
name: &str,
props: DynamicOpProperties,
) {
let mut registry = DYNAMIC_OP_REGISTRY.write().unwrap();
registry.insert(name.to_string(), props);
}
#[must_use]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn get_dynamic_op_properties(name: &str) -> Option<DynamicOpProperties> {
let registry = DYNAMIC_OP_REGISTRY.read().unwrap();
registry.get(name).cloned()
}
impl Expr {
#[must_use]
pub fn sin(&self) -> Self {
Self::new_sin(self)
}
#[must_use]
pub fn cos(&self) -> Self {
Self::new_cos(self)
}
#[must_use]
pub fn tan(&self) -> Self {
Self::new_tan(self)
}
#[must_use]
pub fn exp(&self) -> Self {
Self::new_exp(self)
}
#[must_use]
pub fn ln(&self) -> Self {
Self::new_log(self)
}
#[must_use]
pub fn log<B: AsRef<Self>>(
&self,
base: B,
) -> Self {
Self::new_log_base(base, self)
}
#[must_use]
pub fn abs(&self) -> Self {
Self::new_abs(self)
}
#[must_use]
pub fn sqrt(&self) -> Self {
Self::new_sqrt(self)
}
#[must_use]
pub fn pow<E: AsRef<Self>>(
&self,
exponent: E,
) -> Self {
Self::new_pow(self, exponent)
}
#[must_use]
pub fn asin(&self) -> Self {
Self::new_arcsin(self)
}
#[must_use]
pub fn acos(&self) -> Self {
Self::new_arccos(self)
}
#[must_use]
pub fn atan(&self) -> Self {
Self::new_arctan(self)
}
#[must_use]
pub fn sinh(&self) -> Self {
Self::new_sinh(self)
}
#[must_use]
pub fn cosh(&self) -> Self {
Self::new_cosh(self)
}
#[must_use]
pub fn tanh(&self) -> Self {
Self::new_tanh(self)
}
}
macro_rules! impl_binary_op {
(
$trait:ident,
$func:ident,
$constructor:ident
) => {
impl $trait<Expr> for Expr {
type Output = Expr;
fn $func(
self,
rhs: Expr,
) -> Self::Output {
Expr::$constructor(&self, &rhs)
}
}
impl $trait<&Expr> for Expr {
type Output = Expr;
fn $func(
self,
rhs: &Expr,
) -> Self::Output {
Expr::$constructor(&self, rhs)
}
}
impl $trait<Expr> for &Expr {
type Output = Expr;
fn $func(
self,
rhs: Expr,
) -> Self::Output {
Expr::$constructor(self, &rhs)
}
}
impl $trait<&Expr> for &Expr {
type Output = Expr;
fn $func(
self,
rhs: &Expr,
) -> Self::Output {
Expr::$constructor(self, rhs)
}
}
impl $trait<f64> for Expr {
type Output = Expr;
fn $func(
self,
rhs: f64,
) -> Self::Output {
Expr::$constructor(&self, &Expr::new_constant(rhs))
}
}
impl $trait<f64> for &Expr {
type Output = Expr;
fn $func(
self,
rhs: f64,
) -> Self::Output {
Expr::$constructor(self, &Expr::new_constant(rhs))
}
}
impl $trait<Expr> for f64 {
type Output = Expr;
fn $func(
self,
rhs: Expr,
) -> Self::Output {
Expr::$constructor(&Expr::new_constant(self), &rhs)
}
}
impl $trait<&Expr> for f64 {
type Output = Expr;
fn $func(
self,
rhs: &Expr,
) -> Self::Output {
Expr::$constructor(&Expr::new_constant(self), rhs)
}
}
};
}
impl_binary_op!(Add, add, new_add);
impl_binary_op!(Sub, sub, new_sub);
impl_binary_op!(Mul, mul, new_mul);
impl_binary_op!(Div, div, new_div);
impl Neg for Expr {
type Output = Self;
fn neg(self) -> Self::Output {
Self::new_neg(&self)
}
}
impl Neg for &Expr {
type Output = Expr;
fn neg(self) -> Self::Output {
Expr::new_neg(self)
}
}
pub trait ToConstant {
fn constant(&self) -> Expr;
}
pub trait ToBigInt {
fn bigint(&self) -> Expr;
}
pub trait ToRational {
fn rational(&self) -> Expr;
}
impl ToConstant for f64 {
fn constant(&self) -> Expr {
Expr::new_constant(*self)
}
}
impl ToConstant for f32 {
fn constant(&self) -> Expr {
Expr::new_constant(f64::from(*self))
}
}
impl ToConstant for i32 {
fn constant(&self) -> Expr {
Expr::new_constant(f64::from(*self))
}
}
impl ToConstant for i64 {
fn constant(&self) -> Expr {
Expr::new_constant(*self as f64)
}
}
#[derive(Debug, Clone)]
pub enum Number {
BigInteger(BigInt),
Rational(BigRational),
Float(OrderedFloat<f64>),
}
impl Number {
#[must_use]
pub fn abs(&self) -> Self {
match self {
| Self::BigInteger(i) => Self::BigInteger(i.abs()),
| Self::Rational(r) => Self::Rational(r.abs()),
| Self::Float(f) => Self::Float(OrderedFloat(f.0.abs())),
}
}
}
impl Number {
#[must_use]
pub fn to_f64(&self) -> f64 {
match self {
| Self::BigInteger(i) => i.to_f64().unwrap_or(f64::INFINITY),
| Self::Rational(r) => r.to_f64().unwrap_or(f64::INFINITY),
| Self::Float(f) => f.0,
}
}
#[must_use]
pub fn sqrt(&self) -> Option<Self> {
if self.is_negative() {
return None;
}
match self {
| Self::BigInteger(i) => {
if let Some(root) = i.sqrt().to_bigint() {
if &root * &root == *i {
return Some(Self::BigInteger(root));
}
}
Some(Self::Float(OrderedFloat(self.to_f64().sqrt())))
},
| _ => Some(Self::Float(OrderedFloat(self.to_f64().sqrt()))),
}
}
}
impl Number {
#[must_use]
pub fn is_positive(&self) -> bool {
match self {
| Self::BigInteger(i) => i.is_positive(),
| Self::Rational(r) => r.is_positive(),
| Self::Float(f) => f.0 > 0.0,
}
}
#[must_use]
pub fn is_negative(&self) -> bool {
match self {
| Self::BigInteger(i) => i.is_negative(),
| Self::Rational(r) => r.is_negative(),
| Self::Float(f) => f.0.is_sign_negative() && f.0 != 0.0,
}
}
#[must_use]
pub fn is_fractional(&self) -> bool {
match self {
| Self::BigInteger(_) => false,
| Self::Rational(r) => !r.is_integer(),
| Self::Float(f) => f.0.fract() != 0.0,
}
}
}
impl Number {
#[must_use]
pub const fn is_integer(&self) -> bool {
matches!(self, Self::BigInteger(_))
}
#[must_use]
pub const fn is_float(&self) -> bool {
matches!(self, Self::Float(_))
}
#[must_use]
pub fn is_zero(&self) -> bool {
match self {
| Self::BigInteger(i) => i.is_zero(),
| Self::Rational(r) => r.is_zero(),
| Self::Float(f) => f.0.abs() < f64::EPSILON,
}
}
#[must_use]
pub fn is_one(&self) -> bool {
match self {
| Self::BigInteger(i) => i.is_one(),
| Self::Rational(r) => r.is_one(),
| Self::Float(f) => (f.0 - 1.0).abs() < f64::EPSILON,
}
}
#[must_use]
pub fn as_f64(&self) -> Option<f64> {
match self {
| Self::BigInteger(i) => i.to_f64(),
| Self::Rational(r) => r.to_f64(),
| Self::Float(f) => Some(f.0),
}
}
#[must_use]
pub fn simplify(self) -> Self {
match self {
| Self::Rational(r) if r.is_integer() => Self::BigInteger(r.to_integer()),
| _ => self,
}
}
fn into_rational(self) -> Option<BigRational> {
match self {
| Self::BigInteger(i) => Some(BigRational::from_integer(i)),
| Self::Rational(r) => Some(r),
| Self::Float(_) => None,
}
}
}
impl PartialEq for Number {
fn eq(
&self,
other: &Self,
) -> bool {
match (self, other) {
| (Self::BigInteger(a), Self::BigInteger(b)) => a == b,
| (Self::Rational(a), Self::Rational(b)) => a == b,
| (Self::Float(a), Self::Float(b)) => (a.0 - b.0).abs() < f64::EPSILON,
| (Self::BigInteger(a), Self::Rational(b))
| (Self::Rational(b), Self::BigInteger(a)) => b.is_integer() && b.numer() == a,
| _ => {
match (self.as_f64(), other.as_f64()) {
| (Some(a), Some(b)) => (a - b).abs() < f64::EPSILON,
| _ => false,
}
},
}
}
}
impl Eq for Number {}
impl PartialOrd for Number {
fn partial_cmp(
&self,
other: &Self,
) -> Option<std::cmp::Ordering> {
match (self, other) {
| (Self::BigInteger(a), Self::BigInteger(b)) => a.partial_cmp(b),
| (Self::Rational(a), Self::Rational(b)) => a.partial_cmp(b),
| (Self::Float(a), Self::Float(b)) => a.partial_cmp(b),
| (Self::BigInteger(a), Self::Rational(b)) => {
BigRational::from_integer(a.clone()).partial_cmp(b)
},
| (Self::Rational(a), Self::BigInteger(b)) => {
a.partial_cmp(&BigRational::from_integer(b.clone()))
},
| _ => {
match (self.as_f64(), other.as_f64()) {
| (Some(a), Some(b)) => OrderedFloat(a).partial_cmp(&OrderedFloat(b)),
| _ => None,
}
},
}
}
}
impl fmt::Display for Number {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
match self {
| Self::BigInteger(i) => {
write!(f, "{i}")
},
| Self::Rational(r) => {
write!(f, "{r}")
},
| Self::Float(fl) => {
write!(f, "{}", fl.0)
},
}
}
}
macro_rules! impl_from_int {
($($t:ty),*) => {
$(
impl From<$t> for Number {
fn from(i: $t) -> Self {
Number::BigInteger(BigInt::from(i))
}
}
)*
};
}
impl_from_int!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);
impl From<BigInt> for Number {
fn from(i: BigInt) -> Self {
Self::BigInteger(i)
}
}
impl From<BigRational> for Number {
fn from(r: BigRational) -> Self {
Self::Rational(r).simplify()
}
}
impl From<f32> for Number {
fn from(f: f32) -> Self {
Self::Float(OrderedFloat(f64::from(f)))
}
}
impl From<f64> for Number {
fn from(f: f64) -> Self {
Self::Float(OrderedFloat(f))
}
}
impl ToConstant for Number {
fn constant(&self) -> Expr {
match self {
| Self::BigInteger(n) => Expr::new_constant(n.to_f64().unwrap_or(f64::NAN)),
| Self::Rational(n) => Expr::new_constant(n.to_f64().unwrap_or(f64::NAN)),
| Self::Float(n) => Expr::new_constant(n.0),
}
}
}
impl ToBigInt for Number {
fn bigint(&self) -> Expr {
match self {
| Self::BigInteger(n) => Expr::new_bigint(n.clone()),
| Self::Rational(n) => Expr::new_bigint(n.to_integer()),
| Self::Float(n) => Expr::new_bigint(n.to_bigint().unwrap_or_else(BigInt::zero)),
}
}
}
impl ToRational for Number {
fn rational(&self) -> Expr {
match self {
| Self::BigInteger(n) => Expr::new_rational(BigRational::from_integer(n.clone())),
| Self::Rational(n) => Expr::new_rational(n.clone()),
| Self::Float(n) => {
Expr::new_rational(BigRational::from_float(n.0).unwrap_or_else(BigRational::zero))
},
}
}
}
impl Add for Number {
type Output = Self;
fn add(
self,
other: Self,
) -> Self {
match (self, other) {
| (Self::BigInteger(a), Self::BigInteger(b)) => Self::BigInteger(a + b),
| (Self::Float(a), b) | (b, Self::Float(a)) => {
Self::Float(a + OrderedFloat(b.as_f64().unwrap_or(f64::NAN)))
},
| (a, b) => {
let ra = a.into_rational().unwrap();
let rb = b.into_rational().unwrap();
Self::from(ra + rb)
},
}
}
}
impl Sub for Number {
type Output = Self;
fn sub(
self,
other: Self,
) -> Self {
match (self, other) {
| (Self::BigInteger(a), Self::BigInteger(b)) => Self::BigInteger(a - b),
| (Self::Float(a), b) => Self::Float(a - OrderedFloat(b.as_f64().unwrap_or(f64::NAN))),
| (a, Self::Float(b)) => Self::Float(OrderedFloat(a.as_f64().unwrap_or(f64::NAN)) - b),
| (a, b) => {
let ra = a.into_rational().unwrap();
let rb = b.into_rational().unwrap();
Self::from(ra - rb)
},
}
}
}
impl Mul for Number {
type Output = Self;
fn mul(
self,
other: Self,
) -> Self {
match (self, other) {
| (Self::BigInteger(a), Self::BigInteger(b)) => Self::BigInteger(a * b),
| (Self::Float(a), b) | (b, Self::Float(a)) => {
Self::Float(a * OrderedFloat(b.as_f64().unwrap_or(f64::NAN)))
},
| (a, b) => {
let ra = a.into_rational().unwrap();
let rb = b.into_rational().unwrap();
Self::from(ra * rb)
},
}
}
}
impl Div for Number {
type Output = Self;
fn div(
self,
other: Self,
) -> Self {
if other.is_zero() {
return Self::Float(OrderedFloat(f64::NAN));
}
match (self, other) {
| (Self::Float(a), b) => Self::Float(a / OrderedFloat(b.as_f64().unwrap_or(f64::NAN))),
| (a, Self::Float(b)) => Self::Float(OrderedFloat(a.as_f64().unwrap_or(f64::NAN)) / b),
| (a, b) => {
let ra = a.into_rational().unwrap();
let rb = b.into_rational().unwrap();
Self::from(ra / rb)
},
}
}
}
impl Rem for Number {
type Output = Self;
fn rem(
self,
other: Self,
) -> Self {
if other.is_zero() {
return Self::Float(OrderedFloat(f64::NAN));
}
match (self, other) {
| (Self::BigInteger(a), Self::BigInteger(b)) => Self::BigInteger(a % b),
| (a, b) => {
let val_a = a.as_f64().unwrap_or(f64::NAN);
let val_b = b.as_f64().unwrap_or(f64::NAN);
Self::Float(OrderedFloat(val_a % val_b))
},
}
}
}
impl Neg for Number {
type Output = Self;
fn neg(self) -> Self {
match self {
| Self::BigInteger(i) => Self::BigInteger(-i),
| Self::Rational(r) => Self::Rational(-r),
| Self::Float(f) => Self::Float(-f),
}
}
}