mod coefficient;
mod core;
pub mod representation;
use colored::Colorize;
use smartstring::{LazyCompact, SmartString};
use crate::{
coefficient::Coefficient,
domains::{float::Complex, rational::Rational},
parser::{ParseSettings, Token},
printer::{AtomPrinter, PrintFunction, PrintOptions},
state::{RecycledAtom, State, SymbolData, Workspace},
transformer::StatsOptions,
utils::Settable,
};
use std::{borrow::Cow, cmp::Ordering, hash::Hash, ops::DerefMut};
pub use self::core::AtomCore;
pub use self::representation::{
Add, AddView, Fun, InlineNum, InlineVar, KeyLookup, ListIterator, ListSlice, Mul, MulView, Num,
NumView, Pow, PowView, Var, VarView,
};
use self::representation::{FunView, RawAtom};
#[derive(Clone)]
pub struct NamespacedSymbol {
pub namespace: Cow<'static, str>,
pub symbol: Cow<'static, str>,
pub file: Cow<'static, str>,
pub line: usize,
}
impl NamespacedSymbol {
pub fn parse(s: &str) -> NamespacedSymbol {
let (namespace, _partial_symbol) = s.rsplit_once("::").unwrap_or_else(|| {
panic!("Input {s} does not contain a symbol in the format `namespace::symbol`.")
});
NamespacedSymbol {
namespace: namespace.to_string().into(),
symbol: s.to_string().into(),
file: "".into(),
line: 0,
}
}
pub fn try_parse<S: AsRef<str>>(s: S) -> Option<NamespacedSymbol> {
let (namespace, _partial_symbol) = s.as_ref().rsplit_once("::")?;
Some(NamespacedSymbol {
namespace: namespace.to_string().into(),
symbol: s.as_ref().to_string().into(),
file: "".into(),
line: 0,
})
}
pub fn try_parse_lit(s: &'static str) -> Option<NamespacedSymbol> {
let (namespace, _partial_symbol) = s.rsplit_once("::")?;
Some(NamespacedSymbol {
namespace: namespace.into(),
symbol: s.into(),
file: "".into(),
line: 0,
})
}
}
impl TryFrom<&str> for NamespacedSymbol {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(NamespacedSymbol::parse(value))
}
}
#[macro_export]
macro_rules! wrap_symbol {
($e:literal) => {{
if let Some(mut s) = $crate::atom::NamespacedSymbol::try_parse_lit($e) {
s.file = file!().into();
s.line = line!() as usize;
s
} else {
let ns = if $crate::state::State::BUILTIN_SYMBOL_NAMES.contains(&$e) {
"symbolica"
} else {
$crate::namespace!()
};
$crate::atom::NamespacedSymbol {
symbol: format!("{}::{}", ns, $e).into(),
namespace: ns.into(),
file: file!().into(),
line: line!() as usize,
}
}
}};
($e:expr) => {{
if let Some(mut s) = $crate::atom::NamespacedSymbol::try_parse($e) {
s.file = file!().into();
s.line = line!() as usize;
s
} else {
let ns = if $crate::state::State::is_builtin_name(&$e) {
"symbolica"
} else {
$crate::namespace!()
};
$crate::atom::NamespacedSymbol {
symbol: format!("{}::{}", ns, $e).into(),
namespace: ns.into(),
file: file!().into(),
line: line!() as usize,
}
}
}};
}
pub struct DefaultNamespace<'a> {
pub namespace: Cow<'static, str>,
pub data: &'a str,
pub file: Cow<'static, str>,
pub line: usize,
}
impl DefaultNamespace<'_> {
pub fn attach_namespace(&self, s: &str) -> NamespacedSymbol {
if let Some(mut s) = NamespacedSymbol::try_parse(s) {
s.file = self.file.clone();
s.line = self.line;
s
} else if State::BUILTIN_SYMBOL_NAMES.contains(&s) {
NamespacedSymbol {
symbol: format!("symbolica::{s}").into(),
namespace: "symbolica".into(),
file: "".into(),
line: 0,
}
} else {
NamespacedSymbol {
symbol: format!("{}::{}", self.namespace, s).into(),
namespace: self.namespace.clone(),
file: self.file.clone(),
line: self.line,
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! wrap_input {
($e:expr) => {
$crate::atom::DefaultNamespace {
data: $e.as_ref(),
namespace: $crate::namespace!().into(),
file: file!().into(),
line: line!() as usize,
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! with_default_namespace {
($e:expr, $namespace: expr) => {
$crate::atom::DefaultNamespace {
data: $e.as_ref(),
namespace: $namespace.into(),
file: file!().into(),
line: line!() as usize,
}
};
}
#[macro_export]
macro_rules! namespace {
() => {{ env!("CARGO_CRATE_NAME") }};
}
#[macro_export]
macro_rules! hide_namespace {
($e:expr) => {
$crate::atom::AtomCore::printer(
&$e,
$crate::printer::PrintOptions {
hide_namespace: Some($crate::namespace!()),
..$crate::printer::PrintOptions::new()
},
)
};
}
pub type NormalizationFunction = Box<dyn Fn(AtomView, &mut Settable<Atom>) + Send + Sync>;
pub type DerivativeFunction = Box<dyn Fn(AtomView, usize, &mut Settable<Atom>) + Send + Sync>;
#[derive(Debug, Clone, PartialEq)]
pub enum SymbolAttribute {
Symmetric,
Antisymmetric,
Cyclesymmetric,
Linear,
Scalar,
Real,
Integer,
Positive,
}
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(
feature = "bincode",
derive(bincode_trait_derive::BorrowDecodeFromDecode),
trait_decode(trait = crate::state::HasStateMap),
)]
pub struct Symbol {
id: u32,
wildcard_level: u8,
is_symmetric: bool,
is_antisymmetric: bool,
is_cyclesymmetric: bool,
is_linear: bool,
is_scalar: bool,
is_real: bool,
is_integer: bool,
is_positive: bool,
}
impl std::fmt::Debug for Symbol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
let data = self.get_data();
write!(
f,
"Symbol(name: {}, id: {}, attributes: {:?}, tags: {:?})",
data.name,
self.id,
self.get_attributes(),
data.tags
)
} else {
self.format(&PrintOptions::file(), f)
}
}
}
impl std::fmt::Display for Symbol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.format(&PrintOptions::from_fmt(f), f)
}
}
pub struct SymbolBuilder {
symbol: NamespacedSymbol,
attributes: Option<Cow<'static, [SymbolAttribute]>>,
tags: Vec<String>,
normalization_function: Option<NormalizationFunction>,
print_function: Option<PrintFunction>,
derivative_function: Option<DerivativeFunction>,
}
impl SymbolBuilder {
pub fn new(symbol: NamespacedSymbol) -> Self {
SymbolBuilder {
symbol,
attributes: None,
tags: vec![],
normalization_function: None,
print_function: None,
derivative_function: None,
}
}
pub fn with_attributes(
mut self,
attributes: impl Into<Cow<'static, [SymbolAttribute]>>,
) -> Self {
self.attributes = Some(attributes.into());
self
}
pub fn with_tags<T: AsRef<[U]>, U: AsRef<str>>(mut self, tags: T) -> Self {
self.tags = tags.as_ref().iter().map(|x| x.as_ref().into()).collect();
self
}
pub fn with_normalization_function(
mut self,
normalization_function: impl Fn(AtomView, &mut Settable<Atom>) + Send + Sync + 'static,
) -> Self {
self.normalization_function = Some(Box::new(normalization_function));
self
}
pub fn with_print_function(
mut self,
print_function: impl Fn(AtomView, &PrintOptions) -> Option<String> + Send + Sync + 'static,
) -> Self {
self.print_function = Some(Box::new(print_function));
self
}
pub fn with_derivative_function(
mut self,
derivative_function: impl Fn(AtomView, usize, &mut Settable<Atom>) + Send + Sync + 'static,
) -> Self {
self.derivative_function = Some(Box::new(derivative_function));
self
}
pub fn build(self) -> Result<Symbol, SmartString<LazyCompact>> {
self.build_with_state(&mut State::get_state_mut())
}
pub(crate) fn build_with_state(
self,
state: &mut State,
) -> Result<Symbol, SmartString<LazyCompact>> {
let (namespace, partial_symbol) =
self.symbol.symbol.rsplit_once("::").ok_or_else(|| {
SmartString::from(format!(
"Input {} does not contain a symbol in the format `namespace::symbol`.",
self.symbol.symbol,
))
})?;
for tag in &self.tags {
if !tag.contains("::") {
return Err(format!("Tag {} must contain a namespace", tag).into());
}
}
Token::check_symbol_namespace(namespace)?;
Token::check_symbol_name(partial_symbol)?;
if self.attributes.is_none()
&& self.normalization_function.is_none()
&& self.print_function.is_none()
&& self.derivative_function.is_none()
&& self.tags.is_empty()
{
state.get_symbol(self.symbol)
} else {
state.get_symbol_with_attributes(
self.symbol,
self.attributes.as_ref().map(|x| x.as_ref()).unwrap_or(&[]),
self.normalization_function,
self.print_function,
self.derivative_function,
self.tags,
)
}
}
}
impl Symbol {
pub const ARG: Symbol = State::ARG;
pub const COEFF: Symbol = State::COEFF;
pub const EXP: Symbol = State::EXP;
pub const LOG: Symbol = State::LOG;
pub const SIN: Symbol = State::SIN;
pub const COS: Symbol = State::COS;
pub const SQRT: Symbol = State::SQRT;
pub const CONJ: Symbol = State::CONJ;
pub const SEP: Symbol = State::SEP;
pub const DERIVATIVE: Symbol = State::DERIVATIVE;
pub const E: Symbol = State::E;
pub const PI: Symbol = State::PI;
pub const PI_STR: &'static str = "𝜋";
pub const E_STR: &'static str = "𝑒";
pub const SEP_STR: &'static str = "‖";
pub fn new(name: NamespacedSymbol) -> SymbolBuilder {
SymbolBuilder::new(name)
}
pub fn parse(name: DefaultNamespace) -> Result<Self, String> {
Token::parse_symbol(&name.data, &name, &mut State::get_state_mut())
}
pub fn to_atom(self) -> Atom {
Atom::var(self)
}
pub fn get_name(&self) -> &str {
State::get_name(*self)
}
pub fn get_stripped_name(&self) -> &str {
let d = self.get_data();
&d.name[d.namespace.len() + 2..]
}
pub fn get_id(&self) -> u32 {
self.id
}
pub fn get_namespace(&self) -> &'static str {
State::get_symbol_namespace(*self)
}
pub fn get_wildcard_level(&self) -> u8 {
self.wildcard_level
}
pub fn is_symmetric(&self) -> bool {
self.is_symmetric
}
pub fn is_antisymmetric(&self) -> bool {
self.is_antisymmetric
}
pub fn is_cyclesymmetric(&self) -> bool {
self.is_cyclesymmetric
}
pub fn is_linear(&self) -> bool {
self.is_linear
}
pub fn is_scalar(&self) -> bool {
self.is_scalar
}
pub fn is_real(&self) -> bool {
self.is_real
}
pub fn is_integer(&self) -> bool {
self.is_integer
}
pub fn is_positive(&self) -> bool {
self.is_positive
}
pub fn is_builtin(self) -> bool {
State::is_builtin(self)
}
pub fn get_tags(&self) -> &[String] {
&self.get_data().tags
}
pub fn has_tag(&self, tag: impl AsRef<str>) -> bool {
let r = tag.as_ref();
self.get_data().tags.iter().any(|x| x == r)
}
pub fn get_attributes(&self) -> Vec<SymbolAttribute> {
let mut attrs = vec![];
if self.is_symmetric {
attrs.push(SymbolAttribute::Symmetric);
}
if self.is_antisymmetric {
attrs.push(SymbolAttribute::Antisymmetric);
}
if self.is_cyclesymmetric {
attrs.push(SymbolAttribute::Cyclesymmetric);
}
if self.is_linear {
attrs.push(SymbolAttribute::Linear);
}
if self.is_scalar {
attrs.push(SymbolAttribute::Scalar);
}
if self.is_real {
attrs.push(SymbolAttribute::Real);
}
if self.is_integer {
attrs.push(SymbolAttribute::Integer);
}
if self.is_positive {
attrs.push(SymbolAttribute::Positive);
}
attrs
}
pub fn has_attributes_of(&self, s: Symbol) -> bool {
for t in s.get_tags() {
if !self.has_tag(t) {
return false;
}
}
(!s.is_antisymmetric() || self.is_antisymmetric())
&& (!s.is_symmetric() || self.is_symmetric())
&& (!s.is_cyclesymmetric() || self.is_cyclesymmetric())
&& (!s.is_linear() || self.is_linear())
&& (!s.is_positive() || self.is_positive())
&& (!s.is_integer() || self.is_integer())
&& (!s.is_real() || self.is_real())
&& (!s.is_scalar() || self.is_scalar())
}
pub const fn raw_var(id: u32, wildcard_level: u8) -> Self {
Symbol {
id,
wildcard_level,
is_symmetric: false,
is_antisymmetric: false,
is_cyclesymmetric: false,
is_linear: false,
is_scalar: false,
is_real: false,
is_integer: false,
is_positive: false,
}
}
pub const fn raw_fn(
id: u32,
wildcard_level: u8,
is_symmetric: bool,
is_antisymmetric: bool,
is_cyclesymmetric: bool,
is_linear: bool,
is_scalar: bool,
is_real: bool,
is_integer: bool,
is_positive: bool,
) -> Self {
Symbol {
id,
wildcard_level,
is_symmetric,
is_antisymmetric,
is_cyclesymmetric,
is_linear,
is_scalar: is_scalar || is_real || is_integer || is_positive,
is_real: is_real || is_integer || is_positive,
is_integer,
is_positive,
}
}
fn get_attributes_tuple_str(&self) -> [(&'static str, bool); 8] {
[
("symmetric", self.is_symmetric),
("antisymmetric", self.is_antisymmetric),
("cyclesymmetric", self.is_cyclesymmetric),
("linear", self.is_linear),
("scalar", self.is_scalar),
("real", self.is_real),
("integer", self.is_integer),
("positive", self.is_positive),
]
}
pub fn get_attributes_tuple(&self) -> [(SymbolAttribute, bool); 8] {
[
(SymbolAttribute::Symmetric, self.is_symmetric),
(SymbolAttribute::Antisymmetric, self.is_antisymmetric),
(SymbolAttribute::Cyclesymmetric, self.is_cyclesymmetric),
(SymbolAttribute::Linear, self.is_linear),
(SymbolAttribute::Scalar, self.is_scalar),
(SymbolAttribute::Real, self.is_real),
(SymbolAttribute::Integer, self.is_integer),
(SymbolAttribute::Positive, self.is_positive),
]
}
pub fn format<W: std::fmt::Write>(
&self,
opts: &PrintOptions,
f: &mut W,
) -> Result<(), std::fmt::Error> {
let data = self.get_data();
let (namespace, name) = (&data.namespace, &data.name[data.namespace.len() + 2..]);
if let Some(custom_print) = &data.custom_print {
if let Some(s) = custom_print(InlineVar::new(*self).as_view(), opts) {
f.write_str(&s)?;
return Ok(());
}
}
if opts.mode.is_latex() {
match *self {
Symbol::E => f.write_char('e'),
Symbol::PI => f.write_str("\\pi"),
Symbol::COS => f.write_str("\\cos"),
Symbol::SIN => f.write_str("\\sin"),
Symbol::EXP => f.write_str("\\exp"),
Symbol::LOG => f.write_str("\\log"),
_ => {
f.write_str(name)?;
if !opts.hide_all_namespaces {
f.write_fmt(format_args!("_{{\\tiny \text{{{namespace}}}}}"))
} else {
Ok(())
}
}
}
} else {
if (!opts.hide_all_namespaces || opts.include_attributes)
&& !State::is_builtin(*self)
&& (opts.hide_namespace != Some(namespace) || opts.include_attributes)
{
if opts.color_namespace && opts.mode.is_symbolica() {
f.write_fmt(format_args!("{}", namespace.dimmed().italic()))?;
if opts.include_attributes {
f.write_fmt(format_args!("{}", "::{".dimmed()))?;
let mut first = true;
for (x, t) in self.get_attributes_tuple_str() {
if t {
if !first {
f.write_fmt(format_args!("{}", ",".dimmed()))?;
}
first = false;
f.write_fmt(format_args!("{}", x.dimmed()))?;
}
}
if self.get_tags().len() > 0 {
for tag in self.get_tags() {
if !first {
f.write_fmt(format_args!("{}", ",".dimmed()))?;
}
first = false;
f.write_fmt(format_args!("{}", tag.dimmed()))?;
}
}
f.write_fmt(format_args!("{}", "}".dimmed()))?;
}
f.write_fmt(format_args!("{}", "::".dimmed()))?;
} else {
if opts.mode.is_mathematica() {
for part in namespace.split("::") {
let mut inside_full_form_unicode = false;
for c in part.split(Symbol::SEP_STR) {
if inside_full_form_unicode {
f.write_fmt(format_args!("\\[{}]", c))?;
} else {
f.write_str(c)?;
}
inside_full_form_unicode = !inside_full_form_unicode;
}
f.write_char('`')?;
}
} else {
f.write_fmt(format_args!("{namespace}::"))?;
}
if opts.mode.is_symbolica() && opts.include_attributes {
f.write_str("{")?;
let mut first = true;
for (x, t) in self.get_attributes_tuple_str() {
if t {
if !first {
f.write_char(',')?;
}
first = false;
f.write_str(x)?;
}
}
if self.get_tags().len() > 0 {
for tag in self.get_tags() {
if !first {
f.write_char(',')?;
}
first = false;
f.write_str(tag)?;
}
}
f.write_str("}::")?;
}
}
}
if opts.mode.is_symbolica() && opts.color_builtin_symbols && name.ends_with('_') {
f.write_fmt(format_args!("{}", name.cyan().italic()))
} else if opts.mode.is_symbolica()
&& opts.color_builtin_symbols
&& State::is_builtin(*self)
{
f.write_fmt(format_args!("{}", name.purple()))
} else if opts.mode.is_mathematica() {
if State::is_builtin(*self) {
match *self {
Symbol::E => f.write_str("E"),
Symbol::PI => f.write_str("Pi"),
Symbol::COS => f.write_str("Cos"),
Symbol::SIN => f.write_str("Sin"),
Symbol::EXP => f.write_str("Exp"),
Symbol::LOG => f.write_str("Log"),
Symbol::SQRT => f.write_str("Sqrt"),
Symbol::CONJ => f.write_str("Conjugate"),
Symbol::DERIVATIVE => f.write_str("Derivative"),
_ => f.write_str(name),
}
} else {
let mut inside_full_form_unicode = false;
for c in name.split(Symbol::SEP_STR) {
if inside_full_form_unicode {
f.write_fmt(format_args!("\\[{}]", c))?;
} else {
f.write_str(c)?;
}
inside_full_form_unicode = !inside_full_form_unicode;
}
Ok(())
}
} else {
f.write_str(name)
}
}
}
pub(crate) fn get_data(self) -> &'static SymbolData {
State::get_symbol_data(self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AtomType {
Num,
Var,
Add,
Mul,
Pow,
Fun,
}
impl std::fmt::Display for AtomType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AtomType::Num => write!(f, "Num"),
AtomType::Var => write!(f, "Var"),
AtomType::Add => write!(f, "Add"),
AtomType::Mul => write!(f, "Mul"),
AtomType::Pow => write!(f, "Pow"),
AtomType::Fun => write!(f, "Fun"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SliceType {
Add,
Mul,
Arg,
One,
Pow,
Empty,
}
pub enum AtomView<'a> {
Num(NumView<'a>),
Var(VarView<'a>),
Fun(FunView<'a>),
Pow(PowView<'a>),
Mul(MulView<'a>),
Add(AddView<'a>),
}
impl Clone for AtomView<'_> {
fn clone(&self) -> Self {
*self
}
}
impl Copy for AtomView<'_> {}
impl Eq for AtomView<'_> {}
impl PartialOrd for AtomView<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for AtomView<'_> {
fn cmp(&self, other: &Self) -> Ordering {
self.cmp(other)
}
}
impl Hash for AtomView<'_> {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
AtomView::Num(a) => a.hash(state),
AtomView::Var(a) => a.hash(state),
AtomView::Fun(a) => a.hash(state),
AtomView::Pow(a) => a.hash(state),
AtomView::Mul(a) => a.hash(state),
AtomView::Add(a) => a.hash(state),
}
}
}
impl std::fmt::Display for AtomView<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
AtomPrinter::new(*self).fmt(f)
}
}
impl From<Symbol> for Atom {
fn from(symbol: Symbol) -> Atom {
Atom::var(symbol)
}
}
impl From<AtomView<'_>> for Atom {
fn from(view: AtomView) -> Atom {
view.to_owned()
}
}
impl<'a> From<NumView<'a>> for AtomView<'a> {
fn from(n: NumView<'a>) -> AtomView<'a> {
AtomView::Num(n)
}
}
impl<'a> From<VarView<'a>> for AtomView<'a> {
fn from(n: VarView<'a>) -> AtomView<'a> {
AtomView::Var(n)
}
}
impl<'a> From<FunView<'a>> for AtomView<'a> {
fn from(n: FunView<'a>) -> AtomView<'a> {
AtomView::Fun(n)
}
}
impl<'a> From<MulView<'a>> for AtomView<'a> {
fn from(n: MulView<'a>) -> AtomView<'a> {
AtomView::Mul(n)
}
}
impl<'a> From<AddView<'a>> for AtomView<'a> {
fn from(n: AddView<'a>) -> AtomView<'a> {
AtomView::Add(n)
}
}
#[derive(Clone, Debug)]
pub enum AtomOrView<'a> {
Atom(Atom),
View(AtomView<'a>),
}
impl std::fmt::Display for AtomOrView<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AtomOrView::Atom(a) => a.fmt(f),
AtomOrView::View(a) => a.fmt(f),
}
}
}
impl PartialEq for AtomOrView<'_> {
#[inline]
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(AtomOrView::Atom(a), AtomOrView::Atom(b)) => a == b,
(AtomOrView::View(a), AtomOrView::View(b)) => a == b,
_ => self.as_view() == other.as_view(),
}
}
}
impl Eq for AtomOrView<'_> {}
impl PartialOrd for AtomOrView<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for AtomOrView<'_> {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(AtomOrView::Atom(a1), AtomOrView::Atom(a2)) => a1.as_view().cmp(&a2.as_view()),
(AtomOrView::Atom(a1), AtomOrView::View(a2)) => a1.as_view().cmp(a2),
(AtomOrView::View(a1), AtomOrView::Atom(a2)) => a1.cmp(&a2.as_view()),
(AtomOrView::View(a1), AtomOrView::View(a2)) => a1.cmp(a2),
}
}
}
impl Hash for AtomOrView<'_> {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
AtomOrView::Atom(a) => a.as_view().hash(state),
AtomOrView::View(a) => a.hash(state),
}
}
}
impl<'a, T> From<T> for AtomOrView<'a>
where
T: Into<Coefficient>,
{
fn from(v: T) -> AtomOrView<'a> {
AtomOrView::Atom(Atom::num(v.into()))
}
}
impl<'a> From<Symbol> for AtomOrView<'a> {
fn from(s: Symbol) -> AtomOrView<'a> {
AtomOrView::Atom(Atom::var(s))
}
}
impl<'a> From<&'a Symbol> for AtomOrView<'a> {
fn from(s: &'a Symbol) -> AtomOrView<'a> {
AtomOrView::Atom(Atom::var(*s))
}
}
impl<'a> From<Atom> for AtomOrView<'a> {
fn from(a: Atom) -> AtomOrView<'a> {
AtomOrView::Atom(a)
}
}
impl<'a> From<&'a Atom> for AtomOrView<'a> {
fn from(a: &'a Atom) -> AtomOrView<'a> {
AtomOrView::View(a.as_view())
}
}
impl<'a> From<AtomView<'a>> for AtomOrView<'a> {
fn from(a: AtomView<'a>) -> AtomOrView<'a> {
AtomOrView::View(a)
}
}
impl<'a> From<&AtomView<'a>> for AtomOrView<'a> {
fn from(a: &AtomView<'a>) -> AtomOrView<'a> {
AtomOrView::View(*a)
}
}
impl<'a> AtomOrView<'a> {
pub fn into_owned(self) -> Atom {
match self {
AtomOrView::Atom(a) => a,
AtomOrView::View(a) => a.to_owned(),
}
}
pub fn as_view(&'a self) -> AtomView<'a> {
match self {
AtomOrView::Atom(a) => a.as_view(),
AtomOrView::View(a) => *a,
}
}
pub fn as_mut(&mut self) -> &mut Atom {
match self {
AtomOrView::Atom(a) => a,
AtomOrView::View(a) => {
let mut oa = Atom::default();
oa.set_from_view(a);
*self = AtomOrView::Atom(oa);
match self {
AtomOrView::Atom(a) => a,
_ => unreachable!(),
}
}
}
}
}
impl AtomView<'_> {
pub fn to_owned(&self) -> Atom {
let mut a = Atom::default();
a.set_from_view(self);
a
}
pub fn clone_into(&self, target: &mut Atom) {
target.set_from_view(self);
}
pub fn to_plain_string(&self) -> String {
format!("{}", self.printer(PrintOptions::file()))
}
pub fn nterms(&self) -> usize {
if let AtomView::Add(a) = self {
a.get_nargs()
} else {
1
}
}
pub fn with_stats<F: Fn(AtomView) -> Atom>(&self, op: F, o: &StatsOptions) -> Atom {
let start_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or(std::time::Duration::from_secs(0));
let t = std::time::Instant::now();
let out = op(*self);
let dt = t.elapsed();
o.print(*self, out.as_view(), start_time, dt);
out
}
#[inline]
pub fn is_zero(&self) -> bool {
if let AtomView::Num(n) = self {
n.is_zero()
} else {
false
}
}
#[inline]
pub fn is_one(&self) -> bool {
if let AtomView::Num(n) = self {
n.is_one()
} else {
false
}
}
fn sub_no_norm(&self, workspace: &Workspace, rhs: AtomView<'_>) -> RecycledAtom {
let mut e = workspace.new_atom();
let a = e.to_add();
a.extend(*self);
a.extend(rhs.neg_no_norm(workspace).as_view());
e
}
fn mul_no_norm(&self, workspace: &Workspace, rhs: AtomView<'_>) -> RecycledAtom {
let mut e = workspace.new_atom();
let a = e.to_mul();
a.extend(*self);
a.extend(rhs);
e
}
fn pow_no_norm(&self, workspace: &Workspace, exp: AtomView<'_>) -> RecycledAtom {
let mut e = workspace.new_atom();
e.to_pow(*self, exp);
e
}
fn div_no_norm(&self, workspace: &Workspace, div: AtomView<'_>) -> RecycledAtom {
self.mul_no_norm(
workspace,
div.pow_no_norm(workspace, workspace.new_num(-1).as_view())
.as_view(),
)
}
fn neg_no_norm(&self, workspace: &Workspace) -> RecycledAtom {
self.mul_no_norm(workspace, workspace.new_num(-1).as_view())
}
pub fn add_with_ws_into(&self, workspace: &Workspace, rhs: AtomView<'_>, out: &mut Atom) {
self.add_normalized(rhs, workspace, out);
}
pub fn sub_with_ws_into(&self, workspace: &Workspace, rhs: AtomView<'_>, out: &mut Atom) {
self.sub_no_norm(workspace, rhs)
.as_view()
.normalize(workspace, out);
}
pub fn mul_with_ws_into(&self, workspace: &Workspace, rhs: AtomView<'_>, out: &mut Atom) {
self.mul_no_norm(workspace, rhs)
.as_view()
.normalize(workspace, out);
}
pub fn pow_with_ws_into(&self, workspace: &Workspace, exp: AtomView<'_>, out: &mut Atom) {
self.pow_no_norm(workspace, exp)
.as_view()
.normalize(workspace, out);
}
pub fn div_with_ws_into(&self, workspace: &Workspace, div: AtomView<'_>, out: &mut Atom) {
self.div_no_norm(workspace, div)
.as_view()
.normalize(workspace, out);
}
pub fn neg_with_ws_into(&self, workspace: &Workspace, out: &mut Atom) {
self.neg_no_norm(workspace)
.as_view()
.normalize(workspace, out);
}
pub fn get_byte_size(&self) -> usize {
match self {
AtomView::Num(n) => n.get_byte_size(),
AtomView::Var(v) => v.get_byte_size(),
AtomView::Fun(f) => f.get_byte_size(),
AtomView::Pow(p) => p.get_byte_size(),
AtomView::Mul(m) => m.get_byte_size(),
AtomView::Add(a) => a.get_byte_size(),
}
}
}
#[must_use]
#[derive(Clone)]
#[cfg_attr(
feature = "bincode",
derive(bincode_trait_derive::BorrowDecodeFromDecode),
trait_decode(trait = crate::state::HasStateMap)
)]
pub enum Atom {
Num(Num),
Var(Var),
Fun(Fun),
Pow(Pow),
Mul(Mul),
Add(Add),
Zero,
}
impl Atom {
pub const I_STR: &'static str = "𝑖";
pub fn i() -> Atom {
Atom::num(Complex::<Rational>::new_i())
}
}
impl Default for Atom {
#[inline]
fn default() -> Self {
Atom::Zero
}
}
impl std::fmt::Display for Atom {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
AtomPrinter::new(self.as_view()).fmt(f)
}
}
impl std::fmt::Debug for Atom {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_view().fmt(fmt)
}
}
impl From<Num> for Atom {
fn from(n: Num) -> Atom {
Atom::Num(n)
}
}
impl From<Var> for Atom {
fn from(n: Var) -> Atom {
Atom::Var(n)
}
}
impl From<Add> for Atom {
fn from(n: Add) -> Atom {
Atom::Add(n)
}
}
impl From<Mul> for Atom {
fn from(n: Mul) -> Atom {
Atom::Mul(n)
}
}
impl From<Fun> for Atom {
fn from(n: Fun) -> Atom {
Atom::Fun(n)
}
}
impl PartialEq for Atom {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.as_view() == other.as_view()
}
}
impl Eq for Atom {}
impl Hash for Atom {
#[inline(always)]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_view().hash(state)
}
}
impl PartialOrd for Atom {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Atom {
fn cmp(&self, other: &Self) -> Ordering {
self.as_view().cmp(&other.as_view())
}
}
impl<T: Into<Coefficient> + Clone> PartialEq<T> for Atom {
fn eq(&self, other: &T) -> bool {
*self == Atom::num(other.clone())
}
}
impl<T: Into<Coefficient> + Clone> PartialOrd<T> for Atom {
fn partial_cmp(&self, other: &T) -> Option<Ordering> {
Some(self.cmp(&Atom::num(other.clone().into())))
}
}
impl Atom {
pub fn new() -> Atom {
Atom::default()
}
pub fn parse(input: DefaultNamespace, settings: ParseSettings) -> Result<Atom, String> {
Workspace::get_local().with(|ws| Token::parse(input.data, settings)?.to_atom(&input, ws))
}
#[inline]
pub fn var(id: Symbol) -> Atom {
Var::new(id).into()
}
#[inline]
pub fn num<T: Into<Coefficient>>(num: T) -> Atom {
let c = num.into();
if c.is_zero() {
Atom::Zero
} else {
Num::new(c).into()
}
}
#[inline]
pub fn is_zero(&self) -> bool {
self.as_view().is_zero()
}
#[inline]
pub fn is_one(&self) -> bool {
self.as_view().is_one()
}
pub fn nterms(&self) -> usize {
self.as_view().nterms()
}
pub fn to_plain_string(&self) -> String {
format!("{}", self.printer(PrintOptions::file()))
}
pub fn with_stats<F: Fn(AtomView) -> Atom>(&self, op: F, o: &StatsOptions) -> Atom {
self.as_view().with_stats(op, o)
}
pub fn repeat_map<F: Fn(AtomView) -> Atom>(&mut self, op: F) {
let mut res;
loop {
res = op(self.as_view());
if res == *self {
break;
}
std::mem::swap(self, &mut res);
}
}
#[inline]
pub fn to_num(&mut self, coeff: Coefficient) -> &mut Num {
let buffer = std::mem::replace(self, Atom::Zero).into_raw();
*self = Atom::Num(Num::new_into(coeff, buffer));
if let Atom::Num(n) = self {
n
} else {
unreachable!()
}
}
#[inline]
pub fn to_var(&mut self, id: Symbol) -> &mut Var {
let buffer = std::mem::replace(self, Atom::Zero).into_raw();
*self = Atom::Var(Var::new_into(id, buffer));
if let Atom::Var(n) = self {
n
} else {
unreachable!()
}
}
#[inline]
pub fn to_fun(&mut self, id: Symbol) -> &mut Fun {
let buffer = std::mem::replace(self, Atom::Zero).into_raw();
*self = Atom::Fun(Fun::new_into(id, buffer));
if let Atom::Fun(n) = self {
n
} else {
unreachable!()
}
}
#[inline]
pub fn to_pow(&mut self, base: AtomView, exp: AtomView) -> &mut Pow {
let buffer = std::mem::replace(self, Atom::Zero).into_raw();
*self = Atom::Pow(Pow::new_into(base, exp, buffer));
if let Atom::Pow(n) = self {
n
} else {
unreachable!()
}
}
#[inline]
pub fn to_mul(&mut self) -> &mut Mul {
let buffer = std::mem::replace(self, Atom::Zero).into_raw();
*self = Atom::Mul(Mul::new_into(buffer));
if let Atom::Mul(n) = self {
n
} else {
unreachable!()
}
}
#[inline]
pub fn to_add(&mut self) -> &mut Add {
let buffer = std::mem::replace(self, Atom::Zero).into_raw();
*self = Atom::Add(Add::new_into(buffer));
if let Atom::Add(n) = self {
n
} else {
unreachable!()
}
}
#[inline(always)]
pub fn into_raw(self) -> RawAtom {
match self {
Atom::Num(n) => n.into_raw(),
Atom::Var(v) => v.into_raw(),
Atom::Fun(f) => f.into_raw(),
Atom::Pow(p) => p.into_raw(),
Atom::Mul(m) => m.into_raw(),
Atom::Add(a) => a.into_raw(),
Atom::Zero => RawAtom::new(),
}
}
#[inline(always)]
pub fn set_from_view(&mut self, view: &AtomView) {
let buffer = std::mem::replace(self, Atom::Zero).into_raw();
match view {
AtomView::Num(n) => *self = Atom::Num(Num::from_view_into(n, buffer)),
AtomView::Var(v) => *self = Atom::Var(Var::from_view_into(v, buffer)),
AtomView::Fun(f) => *self = Atom::Fun(Fun::from_view_into(f, buffer)),
AtomView::Pow(p) => *self = Atom::Pow(Pow::from_view_into(p, buffer)),
AtomView::Mul(m) => *self = Atom::Mul(Mul::from_view_into(m, buffer)),
AtomView::Add(a) => *self = Atom::Add(Add::from_view_into(a, buffer)),
}
}
#[inline(always)]
pub fn as_view(&self) -> AtomView<'_> {
match self {
Atom::Num(n) => AtomView::Num(n.to_num_view()),
Atom::Var(v) => AtomView::Var(v.to_var_view()),
Atom::Fun(f) => AtomView::Fun(f.to_fun_view()),
Atom::Pow(p) => AtomView::Pow(p.to_pow_view()),
Atom::Mul(m) => AtomView::Mul(m.to_mul_view()),
Atom::Add(a) => AtomView::Add(a.to_add_view()),
Atom::Zero => AtomView::ZERO,
}
}
#[inline(always)]
pub(crate) fn set_normalized(&mut self, normalized: bool) {
match self {
Atom::Num(_) => {}
Atom::Var(_) => {}
Atom::Fun(a) => a.set_normalized(normalized),
Atom::Pow(a) => a.set_normalized(normalized),
Atom::Mul(a) => a.set_normalized(normalized),
Atom::Add(a) => a.set_normalized(normalized),
Atom::Zero => {}
}
}
}
#[derive(Clone)]
pub struct FunctionBuilder {
handle: RecycledAtom,
}
impl FunctionBuilder {
pub fn new(name: Symbol) -> FunctionBuilder {
let mut a = RecycledAtom::new();
a.to_fun(name);
FunctionBuilder { handle: a }
}
pub fn add_arg<'a, T: Into<AtomOrView<'a>>>(mut self, arg: T) -> FunctionBuilder {
if let Atom::Fun(f) = self.handle.deref_mut() {
f.add_arg(arg.into().as_view());
}
self
}
pub fn add_args<'a, T>(mut self, args: &'a [T]) -> FunctionBuilder
where
&'a T: Into<AtomOrView<'a>>,
{
if let Atom::Fun(f) = self.handle.deref_mut() {
for a in args {
f.add_arg(a.into().as_view());
}
}
self
}
pub fn finish(self) -> Atom {
Workspace::get_local().with(|ws| {
let mut f = ws.new_atom();
self.handle.as_view().normalize(ws, &mut f);
f.into_inner()
})
}
}
pub trait FunctionArgument {
fn add_arg_to_function_builder(&self, f: FunctionBuilder) -> FunctionBuilder;
}
impl FunctionArgument for Atom {
fn add_arg_to_function_builder(&self, f: FunctionBuilder) -> FunctionBuilder {
f.add_arg(self.as_view())
}
}
impl FunctionArgument for &Atom {
fn add_arg_to_function_builder(&self, f: FunctionBuilder) -> FunctionBuilder {
f.add_arg(self.as_view())
}
}
impl FunctionArgument for &mut Atom {
fn add_arg_to_function_builder(&self, f: FunctionBuilder) -> FunctionBuilder {
f.add_arg(self.as_view())
}
}
impl FunctionArgument for AtomView<'_> {
fn add_arg_to_function_builder(&self, f: FunctionBuilder) -> FunctionBuilder {
f.add_arg(*self)
}
}
impl FunctionArgument for &AtomView<'_> {
fn add_arg_to_function_builder(&self, f: FunctionBuilder) -> FunctionBuilder {
f.add_arg(**self)
}
}
impl FunctionArgument for Symbol {
fn add_arg_to_function_builder(&self, f: FunctionBuilder) -> FunctionBuilder {
let t = InlineVar::new(*self);
f.add_arg(t.as_view())
}
}
impl<T: Into<Coefficient> + Clone> FunctionArgument for T {
fn add_arg_to_function_builder(&self, f: FunctionBuilder) -> FunctionBuilder {
f.add_arg(Atom::num(self.clone()))
}
}
#[macro_export]
macro_rules! function {
($name: expr) => {
{
$crate::atom::FunctionBuilder::new($name).finish()
}
};
($name: expr, $($id: expr),*) => {
{
let mut f = $crate::atom::FunctionBuilder::new($name);
$(
f = $crate::atom::FunctionArgument::add_arg_to_function_builder(&$id, f);
)+
f.finish()
}
};
}
#[macro_export]
macro_rules! tag {
($name: expr) => {
if !$name.contains("::") {
let mut s = String::from($crate::namespace!());
s.push_str("::");
s.push_str($name.as_ref());
s
} else {
String::from($name)
}
};
}
#[macro_export]
macro_rules! symbol {
($id: expr) => {
$crate::atom::Symbol::new($crate::wrap_symbol!($id)).build().unwrap()
};
($id: expr; $($attr: ident),*) => {
$crate::atom::Symbol::new($crate::wrap_symbol!($id)).with_attributes(&[$($crate::atom::SymbolAttribute::$attr,)*]).build().unwrap()
};
($id: expr, $($a: tt = $value: expr),*) => {
{
let mut b = $crate::atom::Symbol::new($crate::wrap_symbol!($id));
$(
b = $crate::symbol_set_attr!(b, $a = $value);
)+
b.build().unwrap()
}
};
($id: expr; $($attr: ident),+; $($a: ident = $value: expr),*) => {
{
let mut b = $crate::atom::Symbol::new($crate::wrap_symbol!($id)).with_attributes(&[$($crate::atom::SymbolAttribute::$attr,)*]);
$(
b = $crate::symbol_set_attr!(b, $a = $value);
)+
b.build().unwrap()
}
};
($($id: expr),*) => {
{
(
$(
$crate::atom::Symbol::new($crate::wrap_symbol!($id)).build().unwrap(),
)+
)
}
};
($($id: expr),*; tag = $tag: expr) => {
{
(
$(
$crate::atom::Symbol::new($crate::wrap_symbol!($id)).with_tags(std::slice::from_ref(&$tag)).build().unwrap(),
)+
)
}
};
($($id: expr),*; tags = $tags: expr) => {
{
(
$(
$crate::atom::Symbol::new($crate::wrap_symbol!($id)).with_tags($tags).build().unwrap(),
)+
)
}
};
($($id: expr),*; $($attr: ident),*) => {
{
macro_rules! gen_attr {
() => {
&[$($crate::atom::SymbolAttribute::$attr,)*]
};
}
(
$(
$crate::atom::Symbol::new($crate::wrap_symbol!($id)).with_attributes(gen_attr!()).build().unwrap(),
)+
)
}
};
($($id: expr),*; $($attr: ident),*; tag = $tag: expr) => {
{
macro_rules! gen_attr {
() => {
&[$($crate::atom::SymbolAttribute::$attr,)*]
};
}
(
$(
$crate::atom::Symbol::new($crate::wrap_symbol!($id)).with_attributes(gen_attr!()).with_tags(std::slice::from_ref(&$tag)).build().unwrap(),
)+
)
}
};
($($id: expr),*; $($attr: ident),*; tags = $tags: expr) => {
{
macro_rules! gen_attr {
() => {
&[$($crate::atom::SymbolAttribute::$attr,)*]
};
}
(
$(
$crate::atom::Symbol::new($crate::wrap_symbol!($id)).with_attributes(gen_attr!()).with_tags($tags).build().unwrap(),
)+
)
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! symbol_set_attr {
() => {{}};
($b: expr, norm = $norm: expr) => {
$b.with_normalization_function($norm)
};
($b: expr, print = $print: expr) => {
$b.with_print_function($print)
};
($b: expr, der = $der: expr) => {
$b.with_derivative_function($der)
};
($b: expr, tag = $tag: expr) => {
$b.with_tags(std::slice::from_ref(&$tag))
};
($b: expr, tags = $tags: expr) => {
$b.with_tags($tags)
};
}
#[macro_export]
macro_rules! try_symbol {
($id: expr) => {
$crate::atom::Symbol::new($crate::wrap_symbol!($id)).build()
};
($id: expr; $($attr: ident),*) => {
$crate::atom::Symbol::new($crate::wrap_symbol!($id)).with_attributes(&[$($crate::atom::SymbolAttribute::$attr,)*]).build()
};
($id: expr, $($a: tt = $value: expr),*) => {
{
let mut b = $crate::atom::Symbol::new($crate::wrap_symbol!($id));
$(
b = $crate::symbol_set_attr!(b, $a = $value);
)+
b.build()
}
};
($id: expr; $($attr: ident),+; $($a: ident = $value: expr),*) => {
{
let mut b = $crate::atom::Symbol::new($crate::wrap_symbol!($id)).with_attributes(&[$($crate::atom::SymbolAttribute::$attr,)*]);
$(
b = $crate::symbol_set_attr!(b, $a = $value);
)+
b.build()
}
};
($($id: expr),*) => {
{
(
$(
$crate::atom::Symbol::new($crate::wrap_symbol!($id)).build(),
)+
)
}
};
($($id: expr),*; $($attr: ident),*) => {
{
macro_rules! gen_attr {
() => {
&[$($crate::atom::SymbolAttribute::$attr,)*]
};
}
(
$(
$crate::atom::Symbol::new($crate::wrap_symbol!($id)).with_attributes(gen_attr!()).build(),
)+
)
}
};
}
#[macro_export]
macro_rules! parse {
($($all_args:tt)*) => {
$crate::try_parse!($($all_args)*).unwrap()
};
}
#[macro_export]
macro_rules! try_parse {
($s: expr) => {
$crate::atom::Atom::parse(
$crate::wrap_input!($s),
$crate::parser::ParseSettings::symbolica(),
)
};
($s: expr, Mathematica) => {{
$crate::atom::Atom::parse(
$crate::wrap_input!($s),
$crate::parser::ParseSettings::mathematica(),
)
}};
($s: expr, settings = $settings: expr) => {{ $crate::atom::Atom::parse($crate::wrap_input!($s), $settings) }};
($s: expr, default_namespace = $ns: expr) => {
$crate::atom::Atom::parse(
$crate::with_default_namespace!($s, $ns),
$crate::parser::ParseSettings::symbolica(),
)
};
($s: expr, Mathematica, default_namespace = $ns: expr) => {{
$crate::atom::Atom::parse(
$crate::with_default_namespace!($s, $ns),
$crate::parser::ParseSettings::mathematica(),
)
}};
($s: expr, settings = $settings: expr, default_namespace = $ns: expr) => {{ $crate::atom::Atom::parse($crate::with_default_namespace!($s, $ns), $settings) }};
}
#[macro_export]
macro_rules! parse_lit {
($s: expr) => {{
$crate::atom::Atom::parse(
$crate::wrap_input!(stringify!($s)),
$crate::parser::ParseSettings::symbolica(),
)
.unwrap()
}};
($s: expr, default_namespace = $ns: expr) => {{
$crate::atom::Atom::parse(
$crate::with_default_namespace!(stringify!($s), $ns),
$crate::parser::ParseSettings::symbolica(),
)
.unwrap()
}};
}
#[macro_export]
macro_rules! try_parse_lit {
($s: expr) => {{
$crate::atom::Atom::parse(
$crate::wrap_input!(stringify!($s)),
$crate::parser::ParseSettings::symbolica(),
)
}};
($s: expr, default_namespace = $ns: expr) => {{
$crate::atom::Atom::parse(
$crate::with_default_namespace!(stringify!($s), $ns),
$crate::parser::ParseSettings::symbolica(),
)
}};
}
impl Atom {
pub fn npow<T: Into<Coefficient>>(&self, exp: T) -> Atom {
Workspace::get_local().with(|ws| {
let n = ws.new_num(exp);
let mut t = ws.new_atom();
self.as_view()
.pow_no_norm(ws, n.as_view())
.as_view()
.normalize(ws, &mut t);
t.into_inner()
})
}
pub fn pow<T: AtomCore>(&self, exp: T) -> Atom {
Workspace::get_local().with(|ws| {
let mut t = ws.new_atom();
self.as_view()
.pow_no_norm(ws, exp.as_atom_view())
.as_view()
.normalize(ws, &mut t);
t.into_inner()
})
}
pub fn rpow<T: AtomCore>(&self, base: T) -> Atom {
Workspace::get_local().with(|ws| {
let mut t = ws.new_atom();
base.as_atom_view()
.pow_no_norm(ws, self.as_view())
.as_view()
.normalize(ws, &mut t);
t.into_inner()
})
}
pub fn add_many<T: AtomCore>(args: &[T]) -> Atom {
let mut out = Atom::new();
Workspace::get_local().with(|ws| {
let mut t = ws.new_atom();
let add = t.to_add();
for a in args {
add.extend(a.as_atom_view());
}
t.as_view().normalize(ws, &mut out);
});
out
}
pub fn mul_many<T: AtomCore>(args: &[T]) -> Atom {
let mut out = Atom::new();
Workspace::get_local().with(|ws| {
let mut t = ws.new_atom();
let add = t.to_mul();
for a in args {
add.extend(a.as_atom_view());
}
t.as_view().normalize(ws, &mut out);
});
out
}
}
mod ops;
impl AsRef<Atom> for Atom {
fn as_ref(&self) -> &Atom {
self
}
}
#[cfg(test)]
mod test {
use crate::{
atom::{Atom, AtomCore},
function,
};
use super::FunctionBuilder;
#[test]
fn parse_macro() {
assert_eq!(parse_lit!(x ^ 2 + 5 + f(x)), parse!("x ^ 2 + 5 + f(x)"));
}
#[test]
fn debug() {
let x = parse!("v1+f1(v2)");
assert_eq!(
format!("{x:#?}"),
"AddView { data: [5, 17, 2, 13, 2, 1, 13, 3, 5, 0, 0, 0, 1, 43, 2, 1, 14] }"
);
assert_eq!(
x.get_all_symbols(true),
[symbol!("v1"), symbol!("v2"), symbol!("f1")]
.into_iter()
.collect(),
);
assert_eq!(x.as_view().get_byte_size(), 17);
}
#[test]
fn composition() {
let v1 = parse!("v1");
let v2 = parse!("v2");
let f1_id = symbol!("f1");
let f1 = function!(f1_id, v1, v2, Atom::num(2));
let r = (-(&v2 + &v1 + 2) * &v2 * 6).npow(5) / &v2.pow(&v1) * &f1 / 4;
let res = parse!("1/4*(v2^v1)^-1*(-6*v2*(v1+v2+2))^5*f1(v1,v2,2)");
assert_eq!(res, r);
}
#[test]
fn building() {
let _ = FunctionBuilder::new(symbol!("a"))
.add_arg(1)
.add_args(&[1, 2])
.add_arg(symbol!("a"))
.add_args(&[symbol!("b")])
.add_args(&[parse!("a")])
.add_arg(parse!("a"))
.add_arg(parse!("a"))
.add_arg(parse!("a").as_view())
.finish();
}
}