//! Provide the basic operations on general expressions.
//!
//! See [AtomCore] for the possible operations.

use ahash::{HashMap, HashSet};
use rayon::ThreadPool;

use crate::{
    atom::{
        AddView, FunctionBuilder, Indeterminate, KeyLookup, MulView, NumView, VarView,
        representation::FunView,
    },
    coefficient::{Coefficient, CoefficientView, ConvertToRing},
    domains::{
        EuclideanDomain, InternalOrdering,
        atom::AtomField,
        factorized_rational_polynomial::{
            FactorizedRationalPolynomial, FromNumeratorAndFactorizedDenominator,
        },
        float::{FixedPrecision, Real, SingleFloat},
        integer::Z,
        rational::Rational,
        rational_polynomial::{
            FromNumeratorAndDenominator, RationalPolynomial, RationalPolynomialField,
        },
    },
    evaluate::{EvaluationDomain, EvaluatorBuilder},
    id::{
        AliasedAtom, BorrowReplacement, Condition, ConditionResult, Context, MatchSettings,
        Pattern, PatternAtomTreeIterator, PatternRestriction, ReplaceBuilder, ReplaceSettings,
    },
    poly::{
        Exponent, IntoVariableMap, PositiveExponent,
        factor::Factorize,
        gcd::PolynomialGCD,
        polynomial::MultivariatePolynomial,
        series::{Series, SeriesDepth},
    },
    printer::{AtomPrinter, CanonicalOrderingSettings, PrintOptions, PrintState},
    solve::SolveError,
    state::Workspace,
    tensors::{CanonicalTensor, matrix::Matrix},
    utils::{BorrowedOrOwned, Settable},
};

use super::{
    Atom, AtomOrView, AtomView, ListSlice, Symbol,
    representation::{InlineNum, InlineVar},
};
use crate::{
    evaluate::EvaluationError,
    poly::{PolynomialConversionError, series::SeriesError},
    tensors::TensorCanonicalizationError,
};

impl Atom {
    #[inline(always)]
    fn wrap<A: AtomCore>(self, state: &A) -> A::Output {
        state.atom_to_output(self)
    }
}

/// All core features of expressions, such as expansion and
/// pattern matching that leave the expression unchanged.
///
///
/// This trait is sealed, such that new methods can be added
/// without breaking existing implementations.
pub trait AtomCore: private::Sealed + Sized {
    type Output;

    /// Take a view of the atom.
    fn as_atom_view(&self) -> AtomView<'_>;

    fn atom_to_output(&self, atom: Atom) -> Self::Output;

    /// Get a function view if the atom is a function.
    fn as_fun_view(&self) -> Option<FunView<'_>> {
        match self.as_atom_view() {
            AtomView::Fun(f) => Some(f),
            _ => None,
        }
    }

    /// Get a variable view if the atom is a variable.
    fn as_var_view(&self) -> Option<VarView<'_>> {
        match self.as_atom_view() {
            AtomView::Var(v) => Some(v),
            _ => None,
        }
    }

    /// Get a numerical view if the atom is a number.
    fn as_num_view(&self) -> Option<NumView<'_>> {
        match self.as_atom_view() {
            AtomView::Num(n) => Some(n),
            _ => None,
        }
    }

    /// Get a multiplication view if the atom is a multiplication.
    fn as_mul_view(&self) -> Option<MulView<'_>> {
        match self.as_atom_view() {
            AtomView::Mul(m) => Some(m),
            _ => None,
        }
    }

    /// Get an addition view if the atom is an addition.
    fn as_add_view(&self) -> Option<AddView<'_>> {
        match self.as_atom_view() {
            AtomView::Add(a) => Some(a),
            _ => None,
        }
    }

    /// Export the atom and state to a binary stream. It can be loaded
    /// with [Atom::import].
    fn export<W: std::io::Write>(&self, dest: &mut W) -> Result<(), std::io::Error> {
        self.as_atom_view().export(dest)
    }

    /// Get the symbol of a variable or function.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let atom = parse!("f(x)");
    /// assert_eq!(atom.get_symbol(), Some(symbol!("f")));
    /// ```
    #[inline(always)]
    fn get_symbol(&self) -> Option<Symbol> {
        match self.as_atom_view() {
            AtomView::Var(v) => Some(v.get_symbol()),
            AtomView::Fun(f) => Some(f.get_symbol()),
            _ => None,
        }
    }

    /// Take the `self` to the power `exp`. Use [`Self::rpow`] for the reverse operation.
    fn pow<'a, T: Into<AtomOrView<'a>>>(&self, exp: T) -> Self::Output {
        Workspace::get_local().with(|ws| {
            let mut t = ws.new_atom();
            self.as_atom_view()
                .pow_no_norm(ws, exp.into().as_atom_view())
                .as_view()
                .normalize(ws, &mut t);
            t.into_inner().wrap(self)
        })
    }

    /// Take `base` to the power `self`.
    fn rpow<'a, T: Into<AtomOrView<'a>>>(&self, base: T) -> Self::Output {
        Workspace::get_local().with(|ws| {
            let mut t = ws.new_atom();
            base.into()
                .as_atom_view()
                .pow_no_norm(ws, self.as_atom_view())
                .as_view()
                .normalize(ws, &mut t);
            t.into_inner().wrap(self)
        })
    }

    /// Collect terms involving the same power of `x`, where `x` is a variable or function, e.g.
    ///
    /// ```math
    /// collect(x + x * y + x^2, x) = x * (1+y) + x^2
    /// ```
    ///
    /// Use [collect_symbol](AtomCore::collect_symbol) to collect using the name of a function only.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + x * y + x^2");
    /// let x = parse!("x");
    /// let collected = expr.collect::<u8>(x);
    /// assert_eq!(collected, parse!("x * (1 + y) + x^2"));
    /// ```
    fn collect<'a, E: Exponent>(&self, x: impl Into<AtomOrView<'a>>) -> Self::Output {
        self.as_atom_view().collect::<E, _>(x).wrap(self)
    }

    /// Collect terms involving the same power of `x` and map both the collected key and its coefficient.
    ///
    /// The first map is applied to the *key* (the quantity collected in), and the second map is
    /// applied to the coefficient.
    fn collect_mapped<'a, E: Exponent>(
        &self,
        x: impl Into<AtomOrView<'a>>,
        key_map: impl Fn(AtomView, &mut Settable<'_, Atom>),
        coeff_map: impl Fn(AtomView, &mut Settable<'_, Atom>),
    ) -> Self::Output {
        self.as_atom_view()
            .collect_mapped::<E, _>(x, &key_map, &coeff_map)
            .wrap(self)
    }

    /// Collect terms involving the same power of variables or functions with the name `x`, e.g.
    ///
    /// ```math
    /// collect_symbol(f(1,2) + x*f*(1,2), f) = (1+x)*f(1,2)
    /// ```
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("f(1,2) + x*f(1,2)");
    /// let collected = expr.collect_symbol::<u8>(symbol!("f"));
    /// assert_eq!(collected, parse!("(1+x)*f(1,2)"));
    /// ```
    fn collect_symbol<E: Exponent>(&self, x: Symbol) -> Self::Output {
        self.as_atom_view().collect_symbol::<E>(x).wrap(self)
    }

    /// Collect terms involving the same power of variables or functions with the name `x`,
    /// and map both the collected key and its coefficient.
    fn collect_symbol_mapped<E: Exponent>(
        &self,
        x: Symbol,
        key_map: impl Fn(AtomView, &mut Settable<'_, Atom>),
        coeff_map: impl Fn(AtomView, &mut Settable<'_, Atom>),
    ) -> Self::Output {
        self.as_atom_view()
            .collect_symbol_mapped::<E>(x, &key_map, &coeff_map)
            .wrap(self)
    }

    /// Collect terms involving the same power of `x`, where `x` is a variable or function, e.g.
    ///
    /// ```math
    /// collect(x + x * y + x^2, x) = x * (1+y) + x^2
    /// ```
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + x * y + x^2 + z + z^2");
    /// let x = parse!("x");
    /// let z = parse!("z");
    /// let collected = expr.collect_multiple::<u8>(&[x, z]);
    /// assert_eq!(collected, parse!("x * (1 + y) + x^2 + z + z^2"));
    /// ```
    fn collect_multiple<E: Exponent>(&self, xs: &[impl AtomCore]) -> Self::Output {
        self.as_atom_view().collect_multiple::<E, _>(xs).wrap(self)
    }

    /// Collect terms involving the same power of `x` in `xs`,
    /// and map both the collected key and its coefficient.
    fn collect_multiple_mapped<E: Exponent>(
        &self,
        xs: &[impl AtomCore],
        key_map: impl Fn(AtomView, &mut Settable<'_, Atom>),
        coeff_map: impl Fn(AtomView, &mut Settable<'_, Atom>),
    ) -> Self::Output {
        self.as_atom_view()
            .collect_multiple_mapped::<E, _>(xs, &key_map, &coeff_map)
            .wrap(self)
    }

    /// Collect common factors from (nested) sums.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x*(x+y*x+x^2+y*(x+x^2))");
    /// let collected = expr.collect_factors();
    /// assert_eq!(collected, parse!("x^2*(1+x+y+y*(1+x))"));
    /// ```
    fn collect_factors(&self) -> Self::Output {
        self.as_atom_view().collect_factors().wrap(self)
    }

    /// Iteratively extract the minimal common powers of an indeterminate `v` for every term that contains `v`
    /// and continue to the next indeterminate in `variables`.
    /// This is a generalization of Horner's method for polynomials.
    ///
    /// If no variables are provided, a heuristically determined variable ordering is used
    /// that minimizes the number of operations.
    ///
    /// # Example
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("v1 + v1*v2 + 2 v1*v2*v3 + v1^2 + v1^3*y + v1^4*z");
    /// let collected = expr.collect_horner(Some(&[symbol!("v1"), symbol!("v2")]));
    /// assert_eq!(collected, parse!("v1*(1+v1*(1+v1*(v1*z+y))+v2*(1+2*v3))"));
    /// ```
    fn collect_horner<'a, V: Into<Indeterminate> + Clone>(
        &self,
        variables: Option<&[V]>,
    ) -> Self::Output {
        let vs = variables.map(|v| v.iter().map(|x| x.clone().into()).collect::<Vec<_>>());

        self.as_atom_view()
            .horner_scheme(vs.as_deref(), false, false)
            .wrap(self)
    }

    /// Count the number of occurrences of each non-constant and non-variable subexpression in the expression.
    /// Subexpressions that occur inside other subexpressions will only be counted once.
    ///
    /// # Example
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("f(1+x) + x*f(1+x) + z*(1+x)");
    /// let count = expr.count_subexpressions();
    /// assert_eq!(count.get(&parse!("f(1+x)").as_view()).unwrap(), &2);
    /// assert_eq!(count.get(&parse!("1+x").as_view()).unwrap(), &2);
    //// ```
    fn count_subexpressions<'a>(&'a self) -> HashMap<AtomView<'a>, usize> {
        let mut subexpressions: HashMap<AtomView, usize> = HashMap::default();
        self.as_atom_view()
            .count_subexpressions(&mut subexpressions);
        subexpressions
    }

    /// Extract subexpressions and replace the subexpressions with
    /// a mapped value given by `f`. The arguments of `f` are the subexpression, the number of occurrences of the subexpression
    /// and the index of the subexpression in the list of subexpressions. `f` should return `None` if the subexpression should not be replaced, and `Some(replacement)` if it should be replaced with `replacement`.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let a = parse!("f(1+x) + x*f(1+x) + z*(1+x)")
    ///     .alias_subexpressions(|_a, _count, i| Some(function!(symbol!("se"), i)));
    /// assert_eq!(a.get_root(), &parse!("se(0) + x*se(0) + z*se(1)"));
    ///
    /// assert_eq!(a.get_aliases()[&parse!("se(0)")], parse!("f(se(1))"));
    /// assert_eq!(a.get_aliases()[&parse!("se(1)")], parse!("1+x"));
    /// ```
    fn alias_subexpressions(
        &self,
        f: impl FnMut(AtomView, usize, usize) -> Option<Atom>,
    ) -> AliasedAtom {
        self.as_atom_view().alias_subexpressions(f)
    }

    /// Collect terms involving the same power of `x` in `xs`, where `xs` is a list of indeterminates.
    /// Return the list of key-coefficient pairs
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + x * y + x^2 + z + z^2");
    /// let x = parse!("x");
    /// let z = parse!("z");
    /// let coeff_list = expr.coefficient_list::<u8>(&[x, z]);
    /// assert_eq!(coeff_list.len(), 4);
    /// ```
    fn coefficient_list<E: Exponent>(&self, xs: &[impl AtomCore]) -> Vec<(Atom, Atom)> {
        self.as_atom_view().coefficient_list::<E, _>(xs)
    }

    /// Collect terms involving the literal occurrence of `x`.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + x * y + x^2");
    /// let x = parse!("x");
    /// let coeff = expr.coefficient(x);
    /// let r = parse!("1+y");
    /// assert_eq!(coeff, coeff);
    /// ```
    fn coefficient<'a, T: Into<AtomOrView<'a>>>(&self, x: T) -> Self::Output {
        Workspace::get_local().with(|ws| {
            self.as_atom_view()
                .coefficient_with_ws(x.into().as_atom_view(), ws)
                .wrap(self)
        })
    }

    /// Write the expression over a common denominator.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("1/x + 1/y");
    /// let together = expr.together();
    /// let r = parse!("(x + y) / (x * y)");
    /// assert_eq!(together, r);
    /// ```
    fn together(&self) -> Self::Output {
        self.as_atom_view().together().wrap(self)
    }

    /// Write the expression as a sum of terms with minimal denominators in `x`.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("(x + y) / (x * y)");
    /// let apart = expr.apart(symbol!("x"));
    /// let r = parse!("1 / y + 1 / x");
    /// assert_eq!(apart, r);
    /// ```
    fn apart<'a, V: Into<BorrowedOrOwned<'a, Indeterminate>>>(&self, x: V) -> Self::Output {
        self.as_atom_view().apart(x.into().borrow()).wrap(self)
    }

    /// Write the expression as a sum of terms with minimal denominators in all variables.
    /// This method computes a Groebner basis and may therefore be slow for large inputs.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("(2y-x)/(y*(x+y)*(y-x))");
    /// let apart = expr.apart_multivariate();
    /// let r = parse!("3/(2*y*x+2*y^2)+1/(2*y^2-2*x*y)");
    /// assert_eq!(apart, r);
    /// ```
    fn apart_multivariate(&self) -> Self::Output {
        self.as_atom_view().apart_multivariate().wrap(self)
    }

    /// Cancel all common factors between numerators and denominators.
    /// Any non-canceling parts of the expression will not be rewritten.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("(x^2 - 1) / (x - 1)");
    /// let canceled = expr.cancel();
    /// let r = parse!("x+1");
    /// assert_eq!(canceled, r);
    /// ```
    fn cancel(&self) -> Self::Output {
        self.as_atom_view().cancel().wrap(self)
    }

    /// Factor the expression over the rationals.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x^2 - 1");
    /// let factored = expr.factor();
    /// let r = parse!("(x - 1) * (x + 1)");
    /// assert_eq!(factored, r);
    /// ```
    fn factor(&self) -> Self::Output {
        self.as_atom_view().factor().wrap(self)
    }

    /// Collect numerical factors by removing the numerical content from additions.
    /// For example, `-2*x + 4*x^2 + 6*x^3` will be transformed into `-2*(x - 2*x^2 - 3*x^3)`.
    ///
    /// The first argument of the addition is normalized to a positive quantity.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("-2*x + 4*x^2 + 6*x^3");
    /// let collected_num = expr.collect_num();
    /// let r = parse!("-2 * (x - 2 * x^2 - 3 * x^3)");
    /// assert_eq!(collected_num, r);
    /// ```
    fn collect_num(&self) -> Self::Output {
        self.as_atom_view().collect_num().wrap(self)
    }

    /// Collect terms that have the same numerical factor.
    /// For example, `2*x + 2*x^2 + x^3` will be transformed into `2(x+x^2)+x^3`.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("2*x + 2*x^2 + x^3");
    /// let collected_num = expr.collect_by_coefficient();
    /// let r = parse!("2(x+x^2)+x^3");
    /// assert_eq!(collected_num, r);
    /// ```
    fn collect_by_coefficient(&self) -> Self::Output {
        self.as_atom_view().collect_by_coefficient().wrap(self)
    }

    /// Expand an expression. The function [AtomCore::expand_via_poly] may be faster.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("(x + 1)^2");
    /// let expanded = expr.expand();
    /// let r = parse!("x^2 + 2 * x + 1");
    /// assert_eq!(expanded, r);
    /// ```
    fn expand(&self) -> Self::Output {
        self.as_atom_view().expand().wrap(self)
    }

    /// Expand the expression by converting it to a polynomial, optionally
    /// only in the indeterminate `var`. The parameter `E` should be a numerical type
    /// that fits the largest exponent in the expanded expression. Often,
    /// `u8` or `u16` is sufficient.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("(x + 1)^2");
    /// let expanded = expr.expand_via_poly::<u8, Atom>(None);
    /// let r = parse!("x^2 + 2 * x + 1");
    /// assert_eq!(expanded, r);
    /// ```
    fn expand_via_poly<E: Exponent, T: AtomCore>(&self, var: impl Into<Option<T>>) -> Self::Output {
        self.as_atom_view()
            .expand_via_poly::<E>(var.into().as_ref().map(|x| x.as_atom_view()))
            .wrap(self)
    }

    /// Expand an expression in the variable or function `var`.
    /// If it is a variable, any function with that variable name is also expanded in.
    /// To expand in multiple functions at the same time, wrap them in a function with the same symbol first,
    /// using a match and replace, and then expand in that function.
    ///
    /// The function [AtomCore::expand_via_poly] may be faster.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("(1+x)*(1+y)^2");
    /// let expanded = expr.expand_in(parse!("x"));
    /// let r = parse!("(1+y)^2 + (1+y)^2*x");
    /// assert_eq!(expanded, r);
    /// ```
    fn expand_in<'a, T: Into<AtomOrView<'a>>>(&self, var: T) -> Self::Output {
        self.as_atom_view()
            .expand_in(var.into().as_atom_view())
            .wrap(self)
    }

    /// Expand an expression, returning `true` iff the expression changed.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("(x + 1)^2");
    /// let mut out = Atom::new();
    /// let changed = expr.expand_into::<Atom>(None, &mut out);
    /// let r = parse!("x^2 + 2 * x + 1");
    /// assert!(changed);
    /// assert_eq!(out, r);
    /// ```
    fn expand_into<T: AtomCore>(&self, var: impl Into<Option<T>>, out: &mut Atom) -> bool {
        self.as_atom_view()
            .expand_into(var.into().as_ref().map(|x| x.as_atom_view()), out)
    }

    /// Distribute numbers in the expression, for example:
    /// `2*(x+y)` -> `2*x+2*y`.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("2*(x+y)");
    /// let expanded_num = expr.expand_num();
    /// let r = parse!("2 * x + 2 * y");
    /// assert_eq!(expanded_num, r);
    /// ```
    fn expand_num(&self) -> Self::Output {
        self.as_atom_view().expand_num().wrap(self)
    }

    /// Check if the expression is expanded, optionally in only the variable or function `var`.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x^2 + 2*x + 1");
    /// let is_expanded = expr.is_expanded::<Atom>(None);
    /// assert!(is_expanded);
    /// ```
    fn is_expanded<T: AtomCore>(&self, var: Option<T>) -> bool {
        self.as_atom_view()
            .is_expanded(var.as_ref().map(|x| x.as_atom_view()))
    }

    /// Take a derivative of the expression with respect to `x`.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x^2 + 2*x + 1");
    /// let derivative = expr.derivative(symbol!("x"));
    /// let r = parse!("2 * x + 2");
    /// assert_eq!(derivative, r);
    /// ```
    fn derivative<'a, V: Into<BorrowedOrOwned<'a, Indeterminate>>>(&self, x: V) -> Self::Output {
        self.as_atom_view().derivative(x.into().borrow()).wrap(self)
    }

    /// Take a derivative of the expression with respect to `x` and
    /// write the result in `out`.
    /// Returns `true` if the derivative is non-zero.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x^2 + 2*x + 1");
    /// let mut out = Atom::new();
    /// let non_zero = expr.derivative_into(symbol!("x"), &mut out);
    /// assert!(non_zero);
    /// assert_eq!(out, parse!("2 * x + 2"));
    /// ```
    fn derivative_into<'a, V: Into<BorrowedOrOwned<'a, Indeterminate>>>(
        &self,
        x: V,
        out: &mut Atom,
    ) -> bool {
        self.as_atom_view().derivative_into(x.into().borrow(), out)
    }

    /// Series expand in `x` around `expansion_point` to depth `depth`.
    /// To expand to a relative depth, use [SeriesDepth::Relative].
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("exp(x)");
    /// let series = expr.series(symbol!("x"), 0, 4).unwrap();
    /// assert_eq!(
    ///     series.to_atom(),
    ///     parse!("1 + x + x^2 / 2 + x^3 / 6 + x^4 / 24")
    /// );
    /// ```
    fn series<'a, 'b, T, V, D>(
        &self,
        x: V,
        expansion_point: T,
        depth: D,
    ) -> Result<Series<AtomField>, SeriesError>
    where
        T: Into<AtomOrView<'b>>,
        V: Into<BorrowedOrOwned<'a, Indeterminate>>,
        D: Into<SeriesDepth>,
    {
        let expansion_point = expansion_point.into();
        let x = x.into();
        let x = x.borrow();
        let depth = depth.into();
        self.as_atom_view()
            .series(x, expansion_point.as_atom_view(), depth)
    }

    /// Find the root of a function in `x` numerically over the reals using Newton's method.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x^2 - 2");
    /// let root = expr.nsolve(symbol!("x"), 1.0, 1e-7, 100).unwrap();
    /// assert!((root - 1.414213562373095).abs() < 1e-7);
    /// ```
    fn nsolve<
        'a,
        N: SingleFloat + EvaluationDomain + Real + PartialOrd,
        V: Into<BorrowedOrOwned<'a, Indeterminate>>,
    >(
        &self,
        x: V,
        init: N,
        prec: N,
        max_iterations: usize,
    ) -> Result<N, SolveError> {
        self.as_atom_view()
            .nsolve(x.into().borrow(), init, prec, max_iterations)
    }

    /// Solve a non-linear system numerically over the reals using Newton's method.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr1 = parse!("x^2 + y^2 - 1");
    /// let expr2 = parse!("x^2 - y");
    /// let system = &[expr1, expr2];
    /// let vars = &[symbol!("x").into(), symbol!("y").into()];
    /// let init = &[F64::from(0.5), F64::from(0.5)];
    /// let roots = Atom::nsolve_system(system, vars, init, 1e-7.into(), 100).unwrap();
    /// assert!((roots[0].into_inner() - 0.786151377757424).abs() < 1e-7);
    /// assert!((roots[1].into_inner() - 0.6180339887498941).abs() < 1e-7);
    /// ```
    fn nsolve_system<
        N: SingleFloat
            + Real
            + EvaluationDomain
            + PartialOrd
            + InternalOrdering
            + Eq
            + std::hash::Hash,
        T: AtomCore,
    >(
        system: &[T],
        vars: &[Indeterminate],
        init: &[N],
        prec: N,
        max_iterations: usize,
    ) -> Result<Vec<N>, SolveError> {
        AtomView::nsolve_system(system, vars, init, prec, max_iterations)
    }

    /// Solve a system that is linear in `vars`, if possible.
    /// Each expression in `system` is understood to yield 0.
    ///
    /// If the system is underdetermined, a partial solution is returned
    /// where each bound variable is a linear combination of the free
    /// variables. The free variables are chosen such that they have the
    /// highest index in the `vars` list.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr1 = parse!("2*x + y - 1");
    /// let expr2 = parse!("x + y + 1");
    /// let system = &[expr1, expr2];
    /// let vars = &[parse!("x"), parse!("y")];
    /// let solution = Atom::solve_linear_system::<u8, _, _>(system, vars).unwrap();
    /// assert_eq!(solution, [Atom::num(2), Atom::num(-3)]);
    /// ```
    ///
    /// Underdetermined system example:
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let (v1, v2, v3) = symbol!("v1", "v2", "v3");
    /// let eqs = ["v1 + v2 - 3", "2*v1 + 2*v2 - 6", "v1 + v3 - 5"];
    /// let system: Vec<_> = eqs.iter().map(|e| parse!(e)).collect();
    ///
    /// let sol = Atom::solve_linear_system::<u8, _, Atom>(
    ///     &system,
    ///     &[v1.into(), v2.into(), v3.into()],
    /// );
    ///
    /// assert_eq!(
    ///     sol,
    ///     Err(SolveError::Underdetermined {
    ///         rank: 2,
    ///         partial_solution: vec![parse!("-v3+5"), parse!("v3-2"), parse!("v3"),],
    ///     })
    /// );
    /// ```
    fn solve_linear_system<E: PositiveExponent, T1: AtomCore, T2: AtomCore>(
        system: &[T1],
        vars: &[T2],
    ) -> Result<Vec<Atom>, SolveError> {
        AtomView::solve_linear_system::<E, T1, T2>(system, vars)
    }

    /// Convert a system of linear equations to a matrix representation, returning the matrix
    /// and the right-hand side.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr1 = parse!("2*x + y - 1");
    /// let expr2 = parse!("x - y + 1");
    /// let system = &[expr1, expr2];
    /// let vars = &[parse!("x"), parse!("y")];
    /// let (matrix, rhs) = Atom::system_to_matrix::<u8, _, _>(system, vars).unwrap();
    /// let one = matrix.field().one();
    /// assert_eq!(
    ///     matrix.into_vec(),
    ///     [&one + &one, one.clone(), one.clone(), -one.clone()]
    /// );
    /// assert_eq!(rhs.into_vec(), [one.clone(), -one]);
    /// ```
    fn system_to_matrix<E: PositiveExponent, T1: AtomCore, T2: AtomCore>(
        system: &[T1],
        vars: &[T2],
    ) -> Result<
        (
            Matrix<RationalPolynomialField<Z, E>>,
            Matrix<RationalPolynomialField<Z, E>>,
        ),
        SolveError,
    > {
        AtomView::system_to_matrix::<E, T1, T2>(system, vars)
    }

    /// Evaluate an expression.
    /// For repeated evaluations, use [Self::evaluator()] and convert
    /// to an optimized version or generate a compiled version of your expression.
    ///
    /// All variables and all user functions that do not have an evaluation hook,
    /// must occur in the map.
    ///
    /// # Example
    ///
    /// ```
    /// # use ahash::HashMap;
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + y");
    /// let x = parse!("x");
    /// let y = parse!("y");
    /// let mut const_map = HashMap::default();
    /// const_map.insert(x.clone(), 1.0);
    /// const_map.insert(y.clone(), 2.0);
    /// let result = expr.evaluate(&const_map).unwrap();
    /// assert_eq!(result, 3.0);
    /// ```
    fn evaluate<A: AtomCore + KeyLookup, T: Real + EvaluationDomain + FixedPrecision>(
        &self,
        map: &HashMap<A, T>,
    ) -> Result<T, EvaluationError> {
        self.as_atom_view()
            .evaluate(map, T::BINARY_PRECISION as u32)
    }

    /// Evaluate an expression with a given precision.
    /// For repeated evaluations, use [Self::evaluator()] and convert
    /// to an optimized version or generate a compiled version of your expression.
    ///
    /// All variables and all user functions that do not have an evaluation hook,
    /// must occur in the map..
    ///
    /// # Example
    ///
    /// ```
    /// # use ahash::HashMap;
    /// use symbolica::prelude::*;
    /// let expr = parse!("2x");
    /// let x = parse!("x");
    /// let mut const_map = HashMap::default();
    /// const_map.insert(x.clone(), Float::with_val(200, 3));
    /// let result = expr.evaluate_with_prec(&const_map, 200).unwrap();
    /// assert_eq!(result, Float::with_val(200, 6));
    /// ```
    fn evaluate_with_prec<A: AtomCore + KeyLookup, T: Real + EvaluationDomain>(
        &self,
        map: &HashMap<A, T>,
        binary_prec: u32,
    ) -> Result<T, EvaluationError> {
        self.as_atom_view().evaluate(map, binary_prec)
    }

    /// Evaluate an expression in a given ring.
    ///
    /// All variables and all user functions that do not have an evaluation hook,
    /// must occur in the map.
    ///
    /// # Example
    ///
    /// ```
    /// # use ahash::HashMap;
    /// use symbolica::prelude::*;
    /// let expr = parse!("1/3*x^2 + 2");
    /// let x = parse!("x");
    /// let mut const_map = HashMap::default();
    /// let r = Zp::new(5);
    /// const_map.insert(x.clone(), r.nth(3.into()));
    /// let result = expr.evaluate_in_ring(&const_map, &r).unwrap();
    /// assert_eq!(result, r.zero());
    /// ```
    fn evaluate_in_ring<A: AtomCore + KeyLookup, R: ConvertToRing>(
        &self,
        map: &HashMap<A, R::Element>,
        ring: &R,
    ) -> Result<R::Element, EvaluationError> {
        self.as_atom_view().evaluate_in_ring(map, ring)
    }

    /// Create an efficient evaluator for a (nested) expression.
    /// All free parameters must appear in `params` and all nested functions
    /// must be registered using [`EvaluatorBuilder::add_function`] or [`EvaluatorBuilder::add_tagged_function`].
    /// All other functions, must have an evaluation hook.
    ///
    /// For the best performance, the evaluator should be JIT-compiled
    /// ([`crate::evaluate::ExpressionEvaluator::jit_compile`]) or compiled to C++ with inline ASM
    /// using [`crate::evaluate::ExpressionEvaluator::export_cpp`].
    ///
    /// # Examples
    ///
    /// A simple evaluation without nested expressions:
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let params = vec![parse!("x"), parse!("y")];
    /// let mut evaluator = parse!("x + y")
    ///     .evaluator(&params)
    ///     .build()
    ///     .unwrap()
    ///     .map_coeff(&|x| x.re.to_f64());
    /// assert_eq!(evaluator.evaluate_single(&[1.0, 2.0]), 3.0);
    /// ```
    ///
    /// An evaluation with a nested function `f(x) = x^2 + 1`:
    /// ```rust
    /// use symbolica::prelude::*;
    /// let params = vec![parse!("x")];
    /// let mut evaluator = parse!("f(x)")
    ///     .evaluator(&params)
    ///     .add_function(symbol!("f"), vec![symbol!("x")], parse!("x^2 + 1"))?
    ///     .build()?
    ///     .map_coeff(&|x| x.re.to_f64());
    /// assert_eq!(evaluator.evaluate_single(&[2.0]), 5.0);
    /// # Ok::<(), EvaluationError>(())
    /// ```
    ///
    /// An evaluation with externally defined functions:
    /// ```rust
    /// use symbolica::prelude::*;
    ///
    /// let _ = symbol!(
    ///     "symbolica::eval::f",
    ///     eval = EvaluationInfo::new().register(|args: &[f64]| args[0] * args[0] + args[1])
    /// );
    ///
    /// let params = vec![parse!("x"), parse!("y")];
    /// let mut evaluator = parse!("symbolica::eval::f(x,y)").evaluator(&params).build().unwrap().map_coeff(&|x| x.re.to_f64());
    /// assert_eq!(evaluator.evaluate_single(&[2.0, 3.0]), 7.0);
    /// ```
    fn evaluator<A: AtomCore>(&self, params: &[A]) -> EvaluatorBuilder<'_> {
        EvaluatorBuilder::new(self.as_atom_view(), params)
    }

    /// Create an efficient evaluator for (nested) expressions.
    /// All free parameters must appear in `params` and all nested functions
    /// must be registered using [`EvaluatorBuilder::add_function`] or [`EvaluatorBuilder::add_tagged_function`].
    /// All other functions, must have an evaluation hook.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr1 = parse!("x + y");
    /// let expr2 = parse!("x - y");
    /// let params = vec![parse!("x"), parse!("y")];
    /// let mut evaluator = Atom::evaluator_multiple(&[expr1, expr2], &params)
    ///     .build()
    ///     .unwrap()
    ///     .map_coeff(&|c| c.re.to_f64());
    /// let mut out = vec![0., 0.];
    /// evaluator.evaluate(&[1.0, 2.0], &mut out);
    /// assert_eq!(out, &[3.0, -1.0]);
    /// ```
    fn evaluator_multiple<'a, A: AtomCore, P: AtomCore>(
        exprs: &'a [A],
        params: &[P],
    ) -> EvaluatorBuilder<'a> {
        EvaluatorBuilder::new_multiple(exprs, params)
    }

    /// Check if the expression could be 0, using (potentially) numerical sampling with
    /// a given tolerance and number of iterations.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("(x+1)^2 - x^2 - 2x - 1");
    /// let result = expr.zero_test(100, 1e-7);
    /// assert_eq!(result, ConditionResult::Inconclusive);
    /// ```
    fn zero_test(&self, iterations: usize, tolerance: f64) -> ConditionResult {
        self.as_atom_view().zero_test(iterations, tolerance)
    }

    /// Set the coefficient ring to the multivariate rational polynomial with `vars` variables.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x*y + x^2*y + y/(1+x)");
    /// let result = expr.set_coefficient_ring(symbol!("x"));
    /// let r = result.set_coefficient_ring(Vec::<Symbol>::new());
    /// assert_eq!(r, parse!("y*(x+1)^-1*(x+2*x^2+x^3+1)"));
    /// ```
    fn set_coefficient_ring(&self, vars: impl IntoVariableMap) -> Self::Output {
        let vars = vars
            .into_var_map()
            .expect("Could not convert variables to a variable map")
            .expect("A variable map is required");
        self.as_atom_view().set_coefficient_ring(&vars).wrap(self)
    }

    /// Convert all coefficients and built-in functions to floats with a given precision `decimal_prec`.
    /// The precision of floating point coefficients in the input will be truncated to `decimal_prec`.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("cos(1/3) + 1/2");
    /// let result = expr.to_float(2);
    /// assert_eq!(result.to_string(), "1.4");
    /// ```
    fn to_float(&self, decimal_prec: u32) -> Self::Output {
        let mut a = Atom::new();
        self.as_atom_view().to_float_into(decimal_prec, &mut a);
        a.wrap(self)
    }

    /// Convert all coefficients and built-in functions to floats with a given precision `decimal_prec`.
    /// The precision of floating point coefficients in the input will be truncated to `decimal_prec`.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("1/3");
    /// let mut out = Atom::new();
    /// expr.to_float_into(2, &mut out);
    /// assert_eq!(out.to_string(), "3.3e-1");
    /// ```
    fn to_float_into(&self, decimal_prec: u32, out: &mut Atom) {
        self.as_atom_view().to_float_into(decimal_prec, out);
    }

    /// Map all coefficients using a given function.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("0.33*x + 3");
    /// let out = expr.map_coefficient(|c| match c {
    ///     CoefficientView::Natural(r, d, ri, di) => {
    ///         Coefficient::Float(Complex::new(Rational::from((r, d)).to_multi_prec_float(53),
    ///             Rational::from((ri, di)).to_multi_prec_float(53)))
    ///     }
    ///     _ => c.to_owned(),
    /// });
    /// assert_eq!(
    ///     out,
    ///     parse!("3.30000000000000e-1*x+3.00000000000000")
    /// );
    /// ```
    fn map_coefficient<F: Fn(CoefficientView) -> Coefficient + Copy>(&self, f: F) -> Self::Output {
        self.as_atom_view().map_coefficient(f).wrap(self)
    }

    /// Map all coefficients using a given function.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("0.33*x + 3");
    /// let mut out = Atom::new();
    /// expr.map_coefficient_into(|c| match c {
    ///     CoefficientView::Natural(r, d, ri, di) => {
    ///         Coefficient::Float(Complex::new(Rational::from((r, d)).to_multi_prec_float(53),
    ///             Rational::from((ri, di)).to_multi_prec_float(53)))
    ///     }
    ///     _ => c.to_owned(),
    /// }, &mut out);
    /// assert_eq!(
    ///     out,
    ///     parse!("3.30000000000000e-1*x+3.00000000000000")
    /// );
    /// ```
    fn map_coefficient_into<F: Fn(CoefficientView) -> Coefficient + Copy>(
        &self,
        f: F,
        out: &mut Atom,
    ) {
        self.as_atom_view().map_coefficient_into(f, out);
    }

    /// Map all floating point and rational coefficients to the best rational approximation
    /// in the interval `[self*(1-relative_error),self*(1+relative_error)]`.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("0.333");
    /// let result = expr.rationalize(&(1, 100).into());
    /// assert_eq!(result, Atom::num((1, 3)));
    /// ```
    fn rationalize(&self, relative_error: &Rational) -> Self::Output {
        self.as_atom_view()
            .rationalize_coefficients(relative_error)
            .wrap(self)
    }

    /// Convert the atom to a polynomial, optionally in the variable ordering
    /// specified by `var_map`. If new variables are encountered, they are
    /// added to the variable map. Similarly, non-polynomial parts are automatically
    /// defined as a new independent variable in the polynomial.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x^2 + 2*x + 1");
    /// let poly: MultivariatePolynomial<_> = expr.to_polynomial(&Q, None);
    /// assert_eq!(poly.to_expression(), parse!("x^2 + 2 * x + 1"));
    /// ```
    ///
    /// With explicit variable ordering:
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x^2 +x*y + 3*y^2");
    /// let poly: MultivariatePolynomial<_> = expr.to_polynomial(&Q, [symbol!("y"), symbol!("x")]);
    /// assert_eq!(poly.lcoeff(), 3);
    /// ```
    fn to_polynomial<R: EuclideanDomain + ConvertToRing, E: Exponent>(
        &self,
        field: &R,
        var_map: impl IntoVariableMap,
    ) -> MultivariatePolynomial<R, E> {
        self.try_to_polynomial(field, var_map).unwrap()
    }

    /// Convert the atom to a polynomial, optionally in the variable ordering
    /// specified by `var_map`. If new variables are encountered, they are
    /// added to the variable map. Similarly, non-polynomial parts are automatically
    /// defined as a new independent variable in the polynomial.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x^2 + 2*x + 1");
    /// let poly: MultivariatePolynomial<_> = expr.try_to_polynomial(&Q, None).unwrap();
    /// assert_eq!(poly.to_expression(), parse!("x^2 + 2 * x + 1"));
    /// ```
    ///
    /// With explicit variable ordering:
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x^2 + 2*x + 1");
    /// let poly: MultivariatePolynomial<_> = expr.try_to_polynomial(&Q, symbol!("x")).unwrap();
    /// assert_eq!(poly.to_expression(), parse!("x^2 + 2 * x + 1"));
    /// ```
    fn try_to_polynomial<R: EuclideanDomain + ConvertToRing, E: Exponent>(
        &self,
        field: &R,
        var_map: impl IntoVariableMap,
    ) -> Result<MultivariatePolynomial<R, E>, PolynomialConversionError> {
        self.as_atom_view().try_to_polynomial(
            field,
            var_map
                .into_var_map()
                .map_err(PolynomialConversionError::InvalidVariableMap)?,
        )
    }

    /// Convert the atom to a polynomial in specific variables.
    /// All other parts will be collected into the coefficient, which
    /// is a general expression.
    ///
    /// This routine does not perform expansions.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x^2 + y*x + x + 1");
    /// let poly = expr.to_polynomial_in_vars::<u8>(symbol!("x"));
    /// assert_eq!(
    ///     poly.flatten(false),
    ///     parse!("x^2 + (1+y)*x + 1")
    /// );
    /// ```
    fn to_polynomial_in_vars<E: Exponent>(
        &self,
        var_map: impl IntoVariableMap,
    ) -> MultivariatePolynomial<AtomField, E> {
        let var_map = var_map
            .into_var_map()
            .expect("Could not convert variables to a variable map")
            .expect("A variable map is required");
        self.as_atom_view().to_polynomial_in_vars(&var_map)
    }

    /// Convert the atom to a rational polynomial, optionally in the variable ordering
    /// specified by `var_map`. If new variables are encountered, they are
    /// added to the variable map. Similarly, non-rational polynomial parts are automatically
    /// defined as a new independent variable in the rational polynomial.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("(x^2 + 2*x + 1) / (x + 1)");
    /// let rat_poly: RationalPolynomial<_> = expr.to_rational_polynomial(&Q, &Z, None);
    /// assert_eq!(rat_poly.to_expression(), parse!("1+x"));
    /// ```
    fn to_rational_polynomial<
        R: EuclideanDomain + ConvertToRing,
        RO: EuclideanDomain + PolynomialGCD<E>,
        E: PositiveExponent,
    >(
        &self,
        field: &R,
        out_field: &RO,
        var_map: impl IntoVariableMap,
    ) -> RationalPolynomial<RO, E>
    where
        RationalPolynomial<RO, E>:
            FromNumeratorAndDenominator<R, RO, E> + FromNumeratorAndDenominator<RO, RO, E>,
    {
        self.try_to_rational_polynomial(field, out_field, var_map)
            .unwrap()
    }

    /// Convert the atom to a rational polynomial, optionally in the variable ordering
    /// specified by `var_map`. If new variables are encountered, they are
    /// added to the variable map. Similarly, non-rational polynomial parts are automatically
    /// defined as a new independent variable in the rational polynomial.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("(x^2 + 2*x + 1) / (x + 1)");
    /// let rat_poly: RationalPolynomial<_> =
    ///     expr.try_to_rational_polynomial(&Q, &Z, None).unwrap();
    /// assert_eq!(rat_poly.to_expression(), parse!("1+x"));
    /// ```
    fn try_to_rational_polynomial<
        R: EuclideanDomain + ConvertToRing,
        RO: EuclideanDomain + PolynomialGCD<E>,
        E: PositiveExponent,
    >(
        &self,
        field: &R,
        out_field: &RO,
        var_map: impl IntoVariableMap,
    ) -> Result<RationalPolynomial<RO, E>, PolynomialConversionError>
    where
        RationalPolynomial<RO, E>:
            FromNumeratorAndDenominator<R, RO, E> + FromNumeratorAndDenominator<RO, RO, E>,
    {
        self.as_atom_view().try_to_rational_polynomial(
            field,
            out_field,
            var_map
                .into_var_map()
                .map_err(PolynomialConversionError::InvalidVariableMap)?,
        )
    }

    /// Convert the atom to a rational polynomial with factorized denominators, optionally in the variable ordering
    /// specified by `var_map`. If new variables are encountered, they are
    /// added to the variable map. Similarly, non-rational polynomial parts are automatically
    /// defined as a new independent variable in the rational polynomial.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("(x^2 + 2*x + 1) / (x + 1)");
    /// let fact_rat_poly: FactorizedRationalPolynomial<_> =
    ///     expr.to_factorized_rational_polynomial(&Q, &Z, None);
    /// assert_eq!(
    ///     fact_rat_poly.numerator.to_expression(),
    ///     parse!("x+1")
    /// );
    /// ```
    fn to_factorized_rational_polynomial<
        R: EuclideanDomain + ConvertToRing,
        RO: EuclideanDomain + PolynomialGCD<E>,
        E: PositiveExponent,
    >(
        &self,
        field: &R,
        out_field: &RO,
        var_map: impl IntoVariableMap,
    ) -> FactorizedRationalPolynomial<RO, E>
    where
        FactorizedRationalPolynomial<RO, E>: FromNumeratorAndFactorizedDenominator<R, RO, E>
            + FromNumeratorAndFactorizedDenominator<RO, RO, E>,
        MultivariatePolynomial<RO, E>: Factorize,
    {
        self.try_to_factorized_rational_polynomial(field, out_field, var_map)
            .unwrap()
    }

    /// Convert the atom to a rational polynomial with factorized denominators, optionally in the variable ordering
    /// specified by `var_map`. If new variables are encountered, they are
    /// added to the variable map. Similarly, non-rational polynomial parts are automatically
    /// defined as a new independent variable in the rational polynomial.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("(x^2 + 2*x + 1) / (x + 1)");
    /// let fact_rat_poly: FactorizedRationalPolynomial<_> =
    ///     expr.try_to_factorized_rational_polynomial(&Q, &Z, None).unwrap();
    /// assert_eq!(
    ///     fact_rat_poly.numerator.to_expression(),
    ///     parse!("x+1")
    /// );
    /// ```
    fn try_to_factorized_rational_polynomial<
        R: EuclideanDomain + ConvertToRing,
        RO: EuclideanDomain + PolynomialGCD<E>,
        E: PositiveExponent,
    >(
        &self,
        field: &R,
        out_field: &RO,
        var_map: impl IntoVariableMap,
    ) -> Result<FactorizedRationalPolynomial<RO, E>, PolynomialConversionError>
    where
        FactorizedRationalPolynomial<RO, E>: FromNumeratorAndFactorizedDenominator<R, RO, E>
            + FromNumeratorAndFactorizedDenominator<RO, RO, E>,
        MultivariatePolynomial<RO, E>: Factorize,
    {
        self.as_atom_view().try_to_factorized_rational_polynomial(
            field,
            out_field,
            var_map
                .into_var_map()
                .map_err(PolynomialConversionError::InvalidVariableMap)?,
        )
    }

    /// Format the atom. See [AtomCore::printer] for more convenient printing.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + y");
    /// let mut output = String::new();
    /// expr.format(&mut output, &PrintOptions::file_no_namespace(), PrintState::default()).unwrap();
    /// assert_eq!(output, "x+y");
    /// ```
    fn format<W: std::fmt::Write>(
        &self,
        fmt: &mut W,
        opts: &PrintOptions,
        print_state: PrintState,
    ) -> Result<bool, std::fmt::Error> {
        self.as_atom_view().format(fmt, opts, print_state)
    }

    /// Construct a printer for the atom with special options.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x^2");
    /// let opts = PrintOptions {
    ///     double_star_for_exponentiation: true,
    ///     hide_all_namespaces: true,
    ///    ..Default::default()
    /// };
    /// let printer = expr.printer(opts);
    /// assert_eq!(printer.to_string(), "x**2");
    /// ```
    fn printer(&self, opts: PrintOptions) -> AtomPrinter<'_> {
        AtomPrinter::new_with_options(self.as_atom_view(), opts)
    }

    /// Print the atom in a form that is independent of any implementation details, such
    /// as the definition order of the symbols. Use [AtomCore::to_canonical_string] for a fully
    /// canonical representation.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let (y, x) = symbol!("canon::y", "canon::x");
    /// let expr = x.to_atom() + y;
    /// let canonical_str = expr.to_canonically_ordered_string(
    ///     CanonicalOrderingSettings::new()
    ///         .include_namespace(false)
    ///         .include_attributes(false),
    /// );
    /// assert_eq!(canonical_str, "x+y");
    /// ```
    fn to_canonically_ordered_string(&self, settings: CanonicalOrderingSettings) -> String {
        self.as_atom_view().to_canonically_ordered_string(settings)
    }

    /// Print the atom in a form that is unique and independent of any implementation details.
    /// The resulting string can be parsed back to the same expression.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + y");
    /// let canonical_str = expr.to_canonical_string();
    /// assert_eq!(canonical_str, "symbolica::{}::x+symbolica::{}::y");
    /// ```
    fn to_canonical_string(&self) -> String {
        self.as_atom_view().to_canonical_string()
    }

    /// Map the function `f` over all terms.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + y");
    /// let result = expr.map_terms_single_core(|term| term.expand());
    /// assert_eq!(result, parse!("x + y"));
    /// ```
    fn map_terms_single_core(&self, f: impl Fn(AtomView) -> Atom) -> Self::Output {
        self.as_atom_view().map_terms_single_core(f).wrap(self)
    }

    /// Map the function `f` over all terms, using parallel execution with `n_cores` cores.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + y");
    /// let result = expr.map_terms(|term| term.expand(), 4);
    /// assert_eq!(result, parse!("x + y"));
    /// ```
    fn map_terms(
        &self,
        f: impl Fn(AtomView) -> Atom + Send + Sync,
        n_cores: usize,
    ) -> Self::Output {
        self.as_atom_view().map_terms(f, n_cores).wrap(self)
    }

    /// Map the function `f` over all terms, using parallel execution with `n_cores` cores.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + y");
    /// let pool = rayon::ThreadPoolBuilder::new().num_threads(4).build().unwrap();
    /// let result = expr.map_terms_with_pool(|term| term.expand(), &pool);
    /// assert_eq!(result, parse!("x + y"));
    /// ```
    fn map_terms_with_pool(
        &self,
        f: impl Fn(AtomView) -> Atom + Send + Sync,
        p: &ThreadPool,
    ) -> Self::Output {
        self.as_atom_view().map_terms_with_pool(f, p).wrap(self)
    }

    /// Canonize (products of) tensors in the expression by relabeling repeated indices.
    /// The tensors must be written as functions, with its indices as the arguments.
    /// Subexpressions, constants and open indices are supported.
    ///
    /// If the contracted indices are distinguishable (for example in their dimension),
    /// you can provide a group marker as the second element in the tuple of the index
    /// specification.
    /// This makes sure that an index will not be renamed to an index from a different group.
    ///
    /// Returns the canonical expression, as well as the external indices and ordered dummy indices
    /// appearing in the canonical expression.
    ///
    /// Example
    /// -------
    /// ```
    /// # use symbolica::prelude::*;
    /// #
    /// # fn main() {
    /// let _ = symbol!("fs"; Symmetric);
    /// let _ = symbol!("fc"; Cyclesymmetric);
    /// let a = parse!("fs(mu2,mu3)*fc(mu4,mu2,k1,mu4,k1,mu3)");
    ///
    /// let mu1 = parse!("mu1");
    /// let mu2 = parse!("mu2");
    /// let mu3 = parse!("mu3");
    /// let mu4 = parse!("mu4");
    ///
    /// let r = a.canonize_tensors([(mu1, 0), (mu2, 0), (mu3, 0), (mu4, 0)]).unwrap();
    /// println!("{}", r.canonical_form);
    /// # }
    /// ```
    /// yields `fs(mu1,mu2)*fc(mu1,k1,mu3,k1,mu2,mu3)`.
    fn canonize_tensors<I, T: AtomCore, G: Ord + std::hash::Hash>(
        &self,
        indices: I,
    ) -> Result<CanonicalTensor<T, G>, TensorCanonicalizationError>
    where
        I: IntoIterator<Item = (T, G)>,
    {
        self.as_atom_view().canonize_tensors(indices)
    }

    fn to_pattern(&self) -> Pattern {
        Pattern::from_view(self.as_atom_view(), true)
    }

    /// Get all symbols in the expression, optionally including function symbols.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + y");
    /// let symbols = expr.get_all_symbols(true);
    /// assert!(symbols.contains(&symbol!("x")));
    /// assert!(symbols.contains(&symbol!("y")));
    /// ```
    fn get_all_symbols(&self, include_function_symbols: bool) -> HashSet<Symbol> {
        self.as_atom_view()
            .get_all_symbols(include_function_symbols)
    }

    /// Get all variables and functions in the expression.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + f(x)");
    /// let indeterminates = expr.get_all_indeterminates(true);
    /// assert!(indeterminates.contains(&Atom::var(symbol!("x")).as_view()));
    /// assert!(indeterminates.contains(&parse!("f(x)").as_view()));
    /// ```
    fn get_all_indeterminates(&self, enter_functions: bool) -> HashSet<AtomView<'_>> {
        self.as_atom_view().get_all_indeterminates(enter_functions)
    }

    /// Returns true iff `self` contains the symbol `s`.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + y");
    /// let contains_x = expr.contains_symbol(symbol!("x"));
    /// assert!(contains_x);
    /// ```
    fn contains_symbol(&self, s: Symbol) -> bool {
        self.as_atom_view().contains_symbol(s)
    }

    /// Returns true iff `self` contains `a` literally.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + y");
    /// let x = parse!("x");
    /// let contains_x = expr.contains(x);
    /// assert!(contains_x);
    /// ```
    fn contains<'a, T: Into<AtomOrView<'a>>>(&self, s: T) -> bool {
        self.as_atom_view().contains(s.into().as_atom_view())
    }

    /// Returns true iff `self` is scalar, i.e. contains only numbers and symbols with the `Scalar` attribute.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let _ = symbol!("x_scalar"; Scalar);
    /// let expr = parse!("3*2^x_scalar + (1+x_scalar)^2");
    /// assert!(expr.is_scalar());
    /// ```
    fn is_scalar(&self) -> bool {
        self.as_atom_view().is_scalar()
    }

    /// Returns true iff an expression is real. Symbols must have the `Real` attribute.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let _ = symbol!("x_real"; Real);
    /// let expr = parse!("3*2^x_real + (1+x_real)^2 + (1/2)^x_real");
    /// assert!(expr.is_real());
    /// ```
    fn is_real(&self) -> bool {
        self.as_atom_view().is_real()
    }

    /// Returns true iff an expression only consists of integer numbers and symbols with the `Integer` attribute.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let _ = symbol!("x_integer"; Integer);
    /// let expr = parse!("3*2^x_integer + (1+x_integer)^2");
    /// assert!(expr.is_integer());
    /// ```
    fn is_integer(&self) -> bool {
        self.as_atom_view().is_integer()
    }

    /// Returns true iff an expression is positive. Symbols must have the `Positive` attribute.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let _ = symbol!("x_p"; Positive);
    /// let expr = parse!("3*2^x_p + (1+x_p)^2 + (1/2)^x_p");
    /// assert!(expr.is_positive());
    /// ```
    fn is_positive(&self) -> bool {
        self.as_atom_view().is_positive()
    }

    /// Returns true iff an expression has no explicit infinities and is not indeterminate.
    ///
    /// # Example
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("3x + x^2 + log(0)");
    /// assert!(!expr.is_finite());
    /// ```
    fn is_finite(&self) -> bool {
        self.as_atom_view().is_finite()
    }

    /// Returns true iff an expression is constant, i.e. contains no user-defined symbols or functions.
    ///
    /// # Example
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("cos(2 + exp(3) ) + 1/3");
    /// assert!(expr.is_constant());
    /// ```
    fn is_constant(&self) -> bool {
        self.as_atom_view().is_constant()
    }

    /// Check if the expression can be considered a polynomial in some variables, including
    /// redefinitions. For example `f(x)+y` is considered a polynomial in `f(x)` and `y`, whereas
    /// `f(x)+x` is not a polynomial.
    ///
    /// Rational powers or powers in variables are not rewritten, e.g. `x^(2y)` is not considered
    /// polynomial in `x^y`.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("f(x) + y");
    /// let is_poly = expr.is_polynomial(true, false);
    /// assert!(is_poly.is_some());
    /// ```
    fn is_polynomial(
        &self,
        allow_not_expanded: bool,
        allow_negative_powers: bool,
    ) -> Option<HashSet<AtomView<'_>>> {
        self.as_atom_view()
            .is_polynomial(allow_not_expanded, allow_negative_powers)
    }

    /// Exponentiate the atom.
    fn exp(&self) -> Self::Output {
        FunctionBuilder::new(Symbol::EXP)
            .add_arg(self.as_atom_view())
            .finish()
            .wrap(self)
    }

    /// Take the logarithm of the atom.
    fn log(&self) -> Self::Output {
        FunctionBuilder::new(Symbol::LOG)
            .add_arg(self.as_atom_view())
            .finish()
            .wrap(self)
    }

    /// Take the sine of the atom.
    fn sin(&self) -> Self::Output {
        FunctionBuilder::new(Symbol::SIN)
            .add_arg(self.as_atom_view())
            .finish()
            .wrap(self)
    }

    /// Take the cosine of the atom.
    fn cos(&self) -> Self::Output {
        FunctionBuilder::new(Symbol::COS)
            .add_arg(self.as_atom_view())
            .finish()
            .wrap(self)
    }

    /// Take the square root of the atom.
    fn sqrt(&self) -> Self::Output {
        FunctionBuilder::new(Symbol::SQRT)
            .add_arg(self.as_atom_view())
            .finish()
            .wrap(self)
    }

    /// Take the absolute value of the atom.
    fn abs(&self) -> Self::Output {
        FunctionBuilder::new(Symbol::ABS)
            .add_arg(self.as_atom_view())
            .finish()
            .wrap(self)
    }

    /// Take the complex conjugate of the atom.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x+2 + 3^x + (5+2i) * (test::{real}::real) + (-2)^x");
    /// let result = expr.conj();
    /// assert_eq!(result, parse!("(5-2𝑖)*test::real+3^conj(x)+conj(x)+conj((-2)^x)+2"));
    /// ```
    fn conj(&self) -> Self::Output {
        FunctionBuilder::new(Symbol::CONJ)
            .add_arg(self.as_atom_view())
            .finish()
            .wrap(self)
    }

    /// Replace all occurrences of the pattern. The right-hand side is
    /// either another pattern, or a function that maps the matched wildcards to a new expression.
    ///
    /// # Examples
    ///
    /// Replace all occurrences of `x` with `z`:
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + y");
    /// let pattern = parse!("x");
    /// let replacement = parse!("z");
    /// let result = expr.replace(pattern).with(replacement);
    /// assert_eq!(result, parse!("z + y"));
    /// ```
    ///
    /// Set a condition `x_ > 1` (conditions can be chained with `&` and `|`):
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("f(1) + f(2) + f(3)");
    /// let out = expr
    ///     .replace(parse!("f(x_)"))
    ///     .when(symbol!("x_").filter(|x| x > 1))
    ///     .with(parse!("f(x_ - 1)"));
    /// assert_eq!(out, parse!("2*f(1) + f(2)"));
    /// ```
    ///
    /// Use a map as a right-hand side:
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let (f, x_) = symbol!("f", "x_");
    /// let a = function!(f, 1) * function!(f, 3);
    /// let p = function!(f, x_);
    ///
    /// let r = a.replace(p).with_map(move |m| {
    ///     function!(
    ///         f,
    ///         parse!(&format!(
    ///             "p{}",
    ///             m.get(x_)
    ///                 .unwrap()
    ///                 .to_atom()
    ///                 .printer(PrintOptions::file()),
    ///         ))
    ///     )
    /// });
    /// let res = parse!("f(p1)*f(p3)");
    /// assert_eq!(r, res);
    /// ```
    ///
    /// Access the match stack to filter for an ascending order of `x`, `y`, `z`:
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("f(1, 2, 3)");
    /// let out = expr
    ///     .replace(parse!("f(x_,y_,z_)"))
    ///     .when(Condition::match_stack(|m| {
    ///         if let Some(x) = m.get(symbol!("x_")) {
    ///             if let Some(y) = m.get(symbol!("y_")) {
    ///                 if x.to_atom() > y.to_atom() {
    ///                     return ConditionResult::False;
    ///                 }
    ///                 if let Some(z) = m.get(symbol!("z_")) {
    ///                     if y.to_atom() > z.to_atom() {
    ///                         return ConditionResult::False;
    ///                     }
    ///                 }
    ///                 return ConditionResult::True;
    ///             }
    ///         }
    ///         ConditionResult::Inconclusive
    ///     }))
    ///     .with(parse!("1"));
    /// assert_eq!(out, parse!("1"));
    /// ```
    fn replace<'b, P: Into<BorrowedOrOwned<'b, Pattern>>>(
        &self,
        pattern: P,
    ) -> ReplaceBuilder<'_, 'b> {
        self.as_atom_view().replace(pattern)
    }

    /// Replace all occurrences of the patterns, where replacements are tested in the order that they are given.
    /// To repeatedly replace multiple patterns, wrap the call in [Atom::repeat_map].
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let (x, y) = (parse!("x"), parse!("y"));
    /// let expr = &x + &y;
    /// let result = expr.replace_multiple([
    ///     x.to_pattern().replace_with(y.clone()),
    ///     Replacement::new(y.clone(), x.clone()),
    /// ]);
    /// assert_eq!(result, x + y);
    /// ```
    fn replace_multiple<I, T>(&self, replacements: I) -> Self::Output
    where
        I: IntoIterator<Item = T>,
        T: BorrowReplacement,
    {
        self.as_atom_view()
            .replace_multiple(replacements, ReplaceSettings::default())
            .wrap(self)
    }

    /// Replace all occurrences of the patterns, where replacements are tested in the order that they are given.
    /// To repeatedly replace multiple patterns, wrap the call in [Atom::repeat_map].
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let (x, y) = (parse!("x"), parse!("y"));
    /// let expr = &x + &y;
    /// let result = expr.replace_multiple_with_settings([
    ///     Replacement::new(x.clone(), y.clone()),
    ///     Replacement::new(y.clone(), x.clone()),
    /// ], ReplaceSettings::default());
    /// assert_eq!(result, x + y);
    /// ```
    fn replace_multiple_with_settings<I, T>(
        &self,
        replacements: I,
        settings: ReplaceSettings,
    ) -> Self::Output
    where
        I: IntoIterator<Item = T>,
        T: BorrowReplacement,
    {
        self.as_atom_view()
            .replace_multiple(replacements, settings)
            .wrap(self)
    }

    /// Replace all occurrences of the patterns, where replacements are tested in the order that they are given.
    /// Returns `true` iff a match was found.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + y");
    /// let pattern1 = parse!("x").to_pattern();
    /// let replacement1 = parse!("y").to_pattern();
    /// let pattern2 = parse!("y").to_pattern();
    /// let replacement2 = parse!("x").to_pattern();
    /// let mut out = Atom::new();
    /// let replacements = [
    ///     Replacement::new(pattern1, replacement1),
    ///     Replacement::new(pattern2, replacement2),
    /// ];
    /// let changed = expr.replace_multiple_into(&replacements, &mut out);
    /// assert!(changed);
    /// assert_eq!(out, parse!("x + y"));
    /// ```
    fn replace_multiple_into<I, T>(&self, replacements: I, out: &mut Atom) -> bool
    where
        I: IntoIterator<Item = T>,
        T: BorrowReplacement,
    {
        self.as_atom_view()
            .replace_multiple_into(replacements, ReplaceSettings::default(), out)
    }

    /// Replace part of an expression by calling the map `m` on each subexpression.
    /// The function `m` must write the new expression to `out`, or leave it unchanged if no replacement is needed.
    /// A [Context] object is passed to the function, which contains information about the current position in the expression.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let (x, y, z) = symbol!("x", "y", "z");
    /// let expr = Atom::var(x) + y;
    /// let result = expr.replace_map(|term, _ctx, out| {
    ///     if term.get_symbol() == Some(x) {
    ///         **out = Atom::from(z);
    ///     }
    /// });
    /// assert_eq!(result, Atom::var(y) + z);
    /// ```
    fn replace_map<F: FnMut(AtomView, &Context, &mut Settable<'_, Atom>)>(
        &self,
        m: F,
    ) -> Self::Output {
        self.as_atom_view().replace_map(m).wrap(self)
    }

    /// Replace part of an expression by calling the map `m` on each subexpression.
    /// The expressions are visited in depth-first order, starting with the deepest subexpressions.
    /// The function `m` must write the new expression into `out`, or leave it unchanged if no replacement is needed.
    /// A [Context] object is passed to the function, which contains information about the current position in the expression and
    /// also whether any child of the current expression was changed by the map.
    fn replace_map_bottom_up<F: FnMut(AtomView, &Context, &mut Settable<'_, Atom>)>(
        &self,
        m: F,
    ) -> Self::Output {
        self.as_atom_view()
            .replace_map_bottom_up(m, true)
            .wrap(self)
    }

    /// Call the function `v` for every subexpression. If `v` returns `true`, the
    /// subexpressions of the current expression will be visited.
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let mut has_complex_coefficient = false;
    /// let expr = parse!("3*f(x,f(4+2𝑖))");
    /// let result = expr.visitor(&mut |a| {
    ///     if let AtomView::Num(n) = a {
    ///         if !n.get_coeff_view().is_real() {
    ///             has_complex_coefficient = true;
    ///         }
    ///     }
    ///     !has_complex_coefficient // early abort when found
    /// });
    /// assert!(has_complex_coefficient);
    /// ```
    fn visitor<'a, F: FnMut(AtomView<'a>) -> bool>(&'a self, v: &mut F) {
        self.as_atom_view().visitor(v)
    }

    /// Return an iterator over matched expressions.
    /// Alternatively, use [ReplaceBuilder::match_iter].
    ///
    /// # Example
    ///
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("f(1) + f(2)");
    /// let pattern = parse!("f(x_)").to_pattern();
    /// let mut iter = expr.pattern_match(&pattern, None, None);
    /// let result = iter.next().unwrap();
    /// assert_eq!(
    ///     result.get(&symbol!("x_")).unwrap(),
    ///     &Atom::num(1)
    /// );
    /// ```
    fn pattern_match<
        'a: 'b,
        'b,
        C: Into<Option<&'b Condition<PatternRestriction>>>,
        S: Into<Option<&'b MatchSettings>>,
    >(
        &'a self,
        pattern: &'b Pattern,
        conditions: C,
        settings: S,
    ) -> PatternAtomTreeIterator<'a, 'b> {
        PatternAtomTreeIterator::new(
            pattern,
            self.as_atom_view(),
            conditions.into(),
            settings.into(),
        )
    }

    /// Return an iterator over all terms in the expression.
    ///
    /// # Example
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("x + y + z");
    /// let mut iter = expr.terms();
    /// assert_eq!(iter.next().unwrap(), parse!("x").as_view());
    /// assert_eq!(iter.next().unwrap(), parse!("y").as_view());
    /// assert_eq!(iter.next().unwrap(), parse!("z").as_view());
    /// assert_eq!(iter.next(), None);
    /// ```
    fn terms(&self) -> impl Iterator<Item = AtomView<'_>> {
        let s = self.as_atom_view();
        match self.as_atom_view() {
            AtomView::Add(a) => a.to_slice().iter(),
            _ => ListSlice::from_one(s).iter(),
        }
    }

    /// Return an iterator over the children of the atom.
    ///
    /// # Example
    /// ```
    /// use symbolica::prelude::*;
    /// let expr = parse!("f(x,y)");
    /// let mut iter = expr.children();
    /// assert_eq!(iter.next().unwrap(), parse!("x").as_view());
    /// assert_eq!(iter.next().unwrap(), parse!("y").as_view());
    /// assert_eq!(iter.next(), None);
    /// ```
    /// return `x, y`
    fn children(&self) -> impl Iterator<Item = AtomView<'_>> {
        match self.as_atom_view() {
            AtomView::Add(a) => a.to_slice().iter(),
            AtomView::Mul(a) => a.to_slice().iter(),
            AtomView::Pow(a) => a.to_slice().iter(),
            AtomView::Fun(a) => a.to_slice().iter(),
            AtomView::Num(_) | AtomView::Var(_) => ListSlice::empty().iter(),
        }
    }
}

impl AtomCore for InlineVar {
    type Output = Atom;

    fn as_atom_view(&self) -> AtomView<'_> {
        self.as_view()
    }

    fn atom_to_output(&self, atom: Atom) -> Self::Output {
        atom
    }
}

impl AtomCore for InlineNum {
    type Output = Atom;
    fn as_atom_view(&self) -> AtomView<'_> {
        self.as_view()
    }

    fn atom_to_output(&self, atom: Atom) -> Self::Output {
        atom
    }
}

impl AtomCore for Indeterminate {
    type Output = Atom;
    fn as_atom_view(&self) -> AtomView<'_> {
        self.as_view()
    }

    fn atom_to_output(&self, atom: Atom) -> Self::Output {
        atom
    }
}

impl<'a> AtomCore for AtomView<'a> {
    type Output = Atom;
    fn as_atom_view(&self) -> AtomView<'a> {
        *self
    }

    fn atom_to_output(&self, atom: Atom) -> Self::Output {
        atom
    }
}

impl<T: AsRef<Atom>> AtomCore for T {
    type Output = Atom;
    fn as_atom_view(&self) -> AtomView<'_> {
        self.as_ref().as_view()
    }

    fn atom_to_output(&self, atom: Atom) -> Self::Output {
        atom
    }
}

impl AtomCore for AtomOrView<'_> {
    type Output = Atom;
    fn as_atom_view(&self) -> AtomView<'_> {
        self.as_view()
    }

    fn atom_to_output(&self, atom: Atom) -> Self::Output {
        atom
    }
}

impl AtomCore for AliasedAtom {
    type Output = AliasedAtom;

    fn as_atom_view(&self) -> AtomView<'_> {
        self.root.as_view()
    }

    fn atom_to_output(&self, atom: Atom) -> Self::Output {
        AliasedAtom {
            root: atom,
            aliases: self.aliases.clone(),
        }
    }

    fn count_subexpressions<'a>(&'a self) -> HashMap<AtomView<'a>, usize> {
        let mut count = HashMap::default();
        self.root.as_atom_view().count_subexpressions(&mut count);
        for e in &self.aliases {
            e.1.as_atom_view().count_subexpressions(&mut count);
        }
        count
    }

    fn alias_subexpressions(
        &self,
        f: impl FnMut(AtomView, usize, usize) -> Option<Atom>,
    ) -> AliasedAtom {
        self.clone().alias_subexpressions(f)
    }
}

mod private {
    use crate::atom::{AtomView, Indeterminate, InlineNum, InlineVar};

    pub trait Sealed {}

    impl Sealed for InlineVar {}
    impl Sealed for InlineNum {}
    impl Sealed for Indeterminate {}
    impl<'a> Sealed for AtomView<'a> {}
    impl<T: AsRef<super::Atom>> Sealed for T {}
    impl Sealed for super::AtomOrView<'_> {}
    impl Sealed for super::AliasedAtom {}
}
