#![allow(clippy::suspicious_arithmetic_impl)]
use std::fmt;
use std::marker::PhantomData;
use std::ops;
use typenum::operator_aliases::{Diff, Sum};
use crate::prelude::Ex;
use super::dim::{Dim, DimName};
pub trait IntoEx {
fn into_ex(self) -> Ex;
}
impl IntoEx for Ex {
fn into_ex(self) -> Ex {
self
}
}
impl IntoEx for &Ex {
fn into_ex(self) -> Ex {
self.clone()
}
}
#[derive(Clone)]
pub struct Qty<D> {
pub(crate) inner: Ex,
pub(crate) _dim: PhantomData<D>,
}
impl<D> Qty<D> {
#[inline]
pub fn from_ex(ex: impl IntoEx) -> Self {
Qty {
inner: ex.into_ex(),
_dim: PhantomData,
}
}
#[inline]
pub fn into_inner(self) -> Ex {
self.inner
}
#[inline]
pub fn inner(&self) -> &Ex {
&self.inner
}
#[inline]
pub fn map(self, f: impl FnOnce(Ex) -> Ex) -> Self {
Qty::from_ex(f(self.inner))
}
}
impl<D> Qty<D> {
pub fn simplify(&self) -> Self {
Self::from_ex(self.inner.simplify())
}
pub fn expand(&self) -> Self {
Self::from_ex(self.inner.expand())
}
pub fn eval(&self) -> Self {
Self::from_ex(self.inner.eval())
}
pub fn subs(&self, var: &impl AsRef<Ex>, val: &impl AsRef<Ex>) -> Self {
Self::from_ex(self.inner.subs(var.as_ref(), val.as_ref()))
}
pub fn simplify_trig(&self) -> Self {
Self::from_ex(self.inner.simplify_trig())
}
pub fn simplify_powers(&self) -> Self {
Self::from_ex(self.inner.simplify_powers())
}
pub fn simplify_rational(&self) -> Self {
Self::from_ex(self.inner.simplify_rational())
}
pub fn expand_trig(&self) -> Self {
Self::from_ex(self.inner.expand_trig())
}
pub fn expand_log(&self) -> Self {
Self::from_ex(self.inner.expand_log())
}
pub fn log_combine(&self) -> Self {
Self::from_ex(self.inner.log_combine())
}
pub fn trig_combine(&self) -> Self {
Self::from_ex(self.inner.trig_combine())
}
pub fn factor(&self, var: &impl AsRef<Ex>) -> Self {
Self::from_ex(self.inner.factor(var.as_ref()))
}
pub fn collect(&self, var: &impl AsRef<Ex>) -> Self {
Self::from_ex(self.inner.collect(var.as_ref()))
}
pub fn cancel(&self, var: &Ex) -> Self {
Self::from_ex(self.inner.cancel(var))
}
pub fn together(&self) -> Self {
Self::from_ex(self.inner.together())
}
pub fn partial_fractions(&self, var: &impl AsRef<Ex>) -> Self {
Self::from_ex(self.inner.partial_fractions(var.as_ref()))
}
pub fn rationalize_denom(&self) -> Self {
Self::from_ex(self.inner.rationalize_denom())
}
pub fn diff(&self, var: &impl AsRef<Ex>) -> Ex {
self.inner.diff(var.as_ref())
}
pub fn integrate(&self, var: &impl AsRef<Ex>) -> Ex {
self.inner.integrate(var.as_ref())
}
pub fn to_latex(&self) -> String {
self.inner.to_latex()
}
pub fn free_symbols(&self) -> Vec<Ex> {
self.inner.free_symbols()
}
pub fn contains(&self, other: &impl AsRef<Ex>) -> bool {
self.inner.contains(other.as_ref())
}
pub fn term_count(&self) -> usize {
self.inner.term_count()
}
pub fn count_ops(&self) -> usize {
self.inner.count_ops()
}
pub fn is_zero(&self) -> Option<bool> {
self.inner.is_zero()
}
pub fn eval_f64(&self) -> Result<f64, crate::base::errors::SymplexError> {
self.inner.eval_f64()
}
pub fn eval_f64_with(
&self,
subs: &[(&Ex, i64)],
) -> Result<f64, crate::base::errors::SymplexError> {
self.inner.eval_f64_with(subs)
}
pub fn eval_decimal(&self, digits: u32) -> Result<String, crate::base::errors::SymplexError> {
self.inner.eval_decimal(digits)
}
}
impl<D> AsRef<Ex> for Qty<D> {
fn as_ref(&self) -> &Ex {
&self.inner
}
}
impl<D: DimName> Qty<D> {
#[inline]
pub fn dim_name(&self) -> &'static str {
D::dim_name()
}
#[inline]
pub fn dim_symbol(&self) -> &'static str {
D::dim_symbol()
}
}
impl<D> fmt::Display for Qty<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl<D> fmt::Debug for Qty<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Qty({})", self.inner)
}
}
#[diagnostic::on_unimplemented(
message = "cannot add or subtract quantities with different physical dimensions",
label = "incompatible physical dimension",
note = "addition and subtraction require both sides to have the same dimension",
note = "use .into_inner() to drop dimension tracking"
)]
pub trait SameDim<Rhs> {}
impl<L, M, T, I, Th, N, J> SameDim<Dim<L, M, T, I, Th, N, J>> for Dim<L, M, T, I, Th, N, J> {}
#[diagnostic::on_unimplemented(
message = "dimension mismatch: expression does not produce `{Self}`",
label = "wrong physical dimension",
note = "the arithmetic in your dim!(ctx, ) expression produces a different dimension than `{Self}`",
note = "check that your factors multiply/divide to the correct physical dimension"
)]
pub trait FromDimExpr<D> {
fn from_dim_expr(qty: Qty<D>) -> Self;
}
impl<A, B> ops::Add<Qty<B>> for Qty<A>
where
A: SameDim<B>,
{
type Output = Qty<A>;
#[inline]
fn add(self, rhs: Qty<B>) -> Qty<A> {
Qty::from_ex(&self.inner + &rhs.inner)
}
}
impl<A, B> ops::Add<&Qty<B>> for Qty<A>
where
A: SameDim<B>,
{
type Output = Qty<A>;
#[inline]
fn add(self, rhs: &Qty<B>) -> Qty<A> {
Qty::from_ex(&self.inner + &rhs.inner)
}
}
impl<A, B> ops::Add<Qty<B>> for &Qty<A>
where
A: SameDim<B>,
{
type Output = Qty<A>;
#[inline]
fn add(self, rhs: Qty<B>) -> Qty<A> {
Qty::from_ex(&self.inner + &rhs.inner)
}
}
impl<A, B> ops::Add<&Qty<B>> for &Qty<A>
where
A: SameDim<B>,
{
type Output = Qty<A>;
#[inline]
fn add(self, rhs: &Qty<B>) -> Qty<A> {
Qty::from_ex(&self.inner + &rhs.inner)
}
}
impl<A, B> ops::Sub<Qty<B>> for Qty<A>
where
A: SameDim<B>,
{
type Output = Qty<A>;
#[inline]
fn sub(self, rhs: Qty<B>) -> Qty<A> {
Qty::from_ex(&self.inner - &rhs.inner)
}
}
impl<A, B> ops::Sub<&Qty<B>> for Qty<A>
where
A: SameDim<B>,
{
type Output = Qty<A>;
#[inline]
fn sub(self, rhs: &Qty<B>) -> Qty<A> {
Qty::from_ex(&self.inner - &rhs.inner)
}
}
impl<A, B> ops::Sub<Qty<B>> for &Qty<A>
where
A: SameDim<B>,
{
type Output = Qty<A>;
#[inline]
fn sub(self, rhs: Qty<B>) -> Qty<A> {
Qty::from_ex(&self.inner - &rhs.inner)
}
}
impl<A, B> ops::Sub<&Qty<B>> for &Qty<A>
where
A: SameDim<B>,
{
type Output = Qty<A>;
#[inline]
fn sub(self, rhs: &Qty<B>) -> Qty<A> {
Qty::from_ex(&self.inner - &rhs.inner)
}
}
impl<D> ops::Neg for Qty<D> {
type Output = Qty<D>;
#[inline]
fn neg(self) -> Qty<D> {
Qty::from_ex(-&self.inner)
}
}
impl<D> ops::Neg for &Qty<D> {
type Output = Qty<D>;
#[inline]
fn neg(self) -> Qty<D> {
Qty::from_ex(-&self.inner)
}
}
impl<L1, M1, T1, I1, Th1, N1x, J1, L2, M2, T2, I2, Th2, N2x, J2>
ops::Mul<Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>> for Qty<Dim<L1, M1, T1, I1, Th1, N1x, J1>>
where
L1: ops::Add<L2>,
M1: ops::Add<M2>,
T1: ops::Add<T2>,
I1: ops::Add<I2>,
Th1: ops::Add<Th2>,
N1x: ops::Add<N2x>,
J1: ops::Add<J2>,
{
type Output = Qty<
Dim<
Sum<L1, L2>,
Sum<M1, M2>,
Sum<T1, T2>,
Sum<I1, I2>,
Sum<Th1, Th2>,
Sum<N1x, N2x>,
Sum<J1, J2>,
>,
>;
#[inline]
fn mul(self, rhs: Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>) -> Self::Output {
Qty::from_ex(&self.inner * &rhs.inner)
}
}
impl<L1, M1, T1, I1, Th1, N1x, J1, L2, M2, T2, I2, Th2, N2x, J2>
ops::Mul<&Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>> for Qty<Dim<L1, M1, T1, I1, Th1, N1x, J1>>
where
L1: ops::Add<L2>,
M1: ops::Add<M2>,
T1: ops::Add<T2>,
I1: ops::Add<I2>,
Th1: ops::Add<Th2>,
N1x: ops::Add<N2x>,
J1: ops::Add<J2>,
{
type Output = Qty<
Dim<
Sum<L1, L2>,
Sum<M1, M2>,
Sum<T1, T2>,
Sum<I1, I2>,
Sum<Th1, Th2>,
Sum<N1x, N2x>,
Sum<J1, J2>,
>,
>;
#[inline]
fn mul(self, rhs: &Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>) -> Self::Output {
Qty::from_ex(&self.inner * &rhs.inner)
}
}
impl<L1, M1, T1, I1, Th1, N1x, J1, L2, M2, T2, I2, Th2, N2x, J2>
ops::Mul<Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>> for &Qty<Dim<L1, M1, T1, I1, Th1, N1x, J1>>
where
L1: ops::Add<L2>,
M1: ops::Add<M2>,
T1: ops::Add<T2>,
I1: ops::Add<I2>,
Th1: ops::Add<Th2>,
N1x: ops::Add<N2x>,
J1: ops::Add<J2>,
{
type Output = Qty<
Dim<
Sum<L1, L2>,
Sum<M1, M2>,
Sum<T1, T2>,
Sum<I1, I2>,
Sum<Th1, Th2>,
Sum<N1x, N2x>,
Sum<J1, J2>,
>,
>;
#[inline]
fn mul(self, rhs: Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>) -> Self::Output {
Qty::from_ex(&self.inner * &rhs.inner)
}
}
impl<L1, M1, T1, I1, Th1, N1x, J1, L2, M2, T2, I2, Th2, N2x, J2>
ops::Mul<&Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>> for &Qty<Dim<L1, M1, T1, I1, Th1, N1x, J1>>
where
L1: ops::Add<L2>,
M1: ops::Add<M2>,
T1: ops::Add<T2>,
I1: ops::Add<I2>,
Th1: ops::Add<Th2>,
N1x: ops::Add<N2x>,
J1: ops::Add<J2>,
{
type Output = Qty<
Dim<
Sum<L1, L2>,
Sum<M1, M2>,
Sum<T1, T2>,
Sum<I1, I2>,
Sum<Th1, Th2>,
Sum<N1x, N2x>,
Sum<J1, J2>,
>,
>;
#[inline]
fn mul(self, rhs: &Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>) -> Self::Output {
Qty::from_ex(&self.inner * &rhs.inner)
}
}
impl<L1, M1, T1, I1, Th1, N1x, J1, L2, M2, T2, I2, Th2, N2x, J2>
ops::Div<Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>> for Qty<Dim<L1, M1, T1, I1, Th1, N1x, J1>>
where
L1: ops::Sub<L2>,
M1: ops::Sub<M2>,
T1: ops::Sub<T2>,
I1: ops::Sub<I2>,
Th1: ops::Sub<Th2>,
N1x: ops::Sub<N2x>,
J1: ops::Sub<J2>,
{
type Output = Qty<
Dim<
Diff<L1, L2>,
Diff<M1, M2>,
Diff<T1, T2>,
Diff<I1, I2>,
Diff<Th1, Th2>,
Diff<N1x, N2x>,
Diff<J1, J2>,
>,
>;
#[inline]
fn div(self, rhs: Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>) -> Self::Output {
Qty::from_ex(&self.inner / &rhs.inner)
}
}
impl<L1, M1, T1, I1, Th1, N1x, J1, L2, M2, T2, I2, Th2, N2x, J2>
ops::Div<&Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>> for Qty<Dim<L1, M1, T1, I1, Th1, N1x, J1>>
where
L1: ops::Sub<L2>,
M1: ops::Sub<M2>,
T1: ops::Sub<T2>,
I1: ops::Sub<I2>,
Th1: ops::Sub<Th2>,
N1x: ops::Sub<N2x>,
J1: ops::Sub<J2>,
{
type Output = Qty<
Dim<
Diff<L1, L2>,
Diff<M1, M2>,
Diff<T1, T2>,
Diff<I1, I2>,
Diff<Th1, Th2>,
Diff<N1x, N2x>,
Diff<J1, J2>,
>,
>;
#[inline]
fn div(self, rhs: &Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>) -> Self::Output {
Qty::from_ex(&self.inner / &rhs.inner)
}
}
impl<L1, M1, T1, I1, Th1, N1x, J1, L2, M2, T2, I2, Th2, N2x, J2>
ops::Div<Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>> for &Qty<Dim<L1, M1, T1, I1, Th1, N1x, J1>>
where
L1: ops::Sub<L2>,
M1: ops::Sub<M2>,
T1: ops::Sub<T2>,
I1: ops::Sub<I2>,
Th1: ops::Sub<Th2>,
N1x: ops::Sub<N2x>,
J1: ops::Sub<J2>,
{
type Output = Qty<
Dim<
Diff<L1, L2>,
Diff<M1, M2>,
Diff<T1, T2>,
Diff<I1, I2>,
Diff<Th1, Th2>,
Diff<N1x, N2x>,
Diff<J1, J2>,
>,
>;
#[inline]
fn div(self, rhs: Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>) -> Self::Output {
Qty::from_ex(&self.inner / &rhs.inner)
}
}
impl<L1, M1, T1, I1, Th1, N1x, J1, L2, M2, T2, I2, Th2, N2x, J2>
ops::Div<&Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>> for &Qty<Dim<L1, M1, T1, I1, Th1, N1x, J1>>
where
L1: ops::Sub<L2>,
M1: ops::Sub<M2>,
T1: ops::Sub<T2>,
I1: ops::Sub<I2>,
Th1: ops::Sub<Th2>,
N1x: ops::Sub<N2x>,
J1: ops::Sub<J2>,
{
type Output = Qty<
Dim<
Diff<L1, L2>,
Diff<M1, M2>,
Diff<T1, T2>,
Diff<I1, I2>,
Diff<Th1, Th2>,
Diff<N1x, N2x>,
Diff<J1, J2>,
>,
>;
#[inline]
fn div(self, rhs: &Qty<Dim<L2, M2, T2, I2, Th2, N2x, J2>>) -> Self::Output {
Qty::from_ex(&self.inner / &rhs.inner)
}
}
impl<D> ops::Mul<i64> for Qty<D> {
type Output = Qty<D>;
#[inline]
fn mul(self, rhs: i64) -> Qty<D> {
Qty::from_ex(&self.inner * rhs)
}
}
impl<D> ops::Mul<i64> for &Qty<D> {
type Output = Qty<D>;
#[inline]
fn mul(self, rhs: i64) -> Qty<D> {
Qty::from_ex(&self.inner * rhs)
}
}
impl<D> ops::Mul<Qty<D>> for i64 {
type Output = Qty<D>;
#[inline]
fn mul(self, rhs: Qty<D>) -> Qty<D> {
Qty::from_ex(self * &rhs.inner)
}
}
impl<D> ops::Mul<&Qty<D>> for i64 {
type Output = Qty<D>;
#[inline]
fn mul(self, rhs: &Qty<D>) -> Qty<D> {
Qty::from_ex(self * &rhs.inner)
}
}
impl<D> ops::Div<i64> for Qty<D> {
type Output = Qty<D>;
#[inline]
fn div(self, rhs: i64) -> Qty<D> {
Qty::from_ex(&self.inner / rhs)
}
}
impl<D> ops::Div<i64> for &Qty<D> {
type Output = Qty<D>;
#[inline]
fn div(self, rhs: i64) -> Qty<D> {
Qty::from_ex(&self.inner / rhs)
}
}
impl<D> ops::Mul<&Ex> for Qty<D> {
type Output = Qty<D>;
#[inline]
fn mul(self, rhs: &Ex) -> Qty<D> {
Qty::from_ex(&self.inner * rhs)
}
}
impl<D> ops::Mul<&Ex> for &Qty<D> {
type Output = Qty<D>;
#[inline]
fn mul(self, rhs: &Ex) -> Qty<D> {
Qty::from_ex(&self.inner * rhs)
}
}
impl<D> ops::Mul<Qty<D>> for &Ex {
type Output = Qty<D>;
#[inline]
fn mul(self, rhs: Qty<D>) -> Qty<D> {
Qty::from_ex(self * &rhs.inner)
}
}
impl<D> ops::Mul<&Qty<D>> for &Ex {
type Output = Qty<D>;
#[inline]
fn mul(self, rhs: &Qty<D>) -> Qty<D> {
Qty::from_ex(self * &rhs.inner)
}
}
impl<D> ops::Div<&Ex> for Qty<D> {
type Output = Qty<D>;
#[inline]
fn div(self, rhs: &Ex) -> Qty<D> {
Qty::from_ex(&self.inner / rhs)
}
}
impl<D> ops::Div<&Ex> for &Qty<D> {
type Output = Qty<D>;
#[inline]
fn div(self, rhs: &Ex) -> Qty<D> {
Qty::from_ex(&self.inner / rhs)
}
}
#[inline]
pub fn assume_dimension<D>(ex: Ex) -> Qty<D> {
Qty {
inner: ex,
_dim: PhantomData,
}
}