use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
use parking_lot::{Mutex, RwLock};
use crate::base::arena::Arena;
use crate::base::assumptions::{Assumption, AssumptionCache, Props};
use crate::base::config::EvalConfig;
use crate::base::node::{CtxId, ExprId};
static NEXT_CTX_ID: AtomicU32 = AtomicU32::new(0);
pub(crate) struct ContextInner {
pub(crate) arena: Arena,
pub(crate) assumptions: Mutex<AssumptionCache>,
}
#[derive(Clone)]
pub struct Context {
pub(crate) id: CtxId,
pub(crate) inner: Arc<RwLock<ContextInner>>,
}
impl Context {
pub fn new() -> Self {
Self::with_config(EvalConfig::default())
}
pub fn with_config(config: EvalConfig) -> Self {
let id = CtxId(NEXT_CTX_ID.fetch_add(1, Ordering::Relaxed));
Context {
id,
inner: Arc::new(RwLock::new(ContextInner {
arena: Arena::with_config(config),
assumptions: Mutex::new(AssumptionCache::new()),
})),
}
}
#[inline]
fn make_ex(&self, id: ExprId) -> crate::api::expr::Ex {
crate::api::expr::Ex::from_raw_parts(self.id, Arc::clone(&self.inner), id)
}
#[inline]
fn make_set_ex(&self, id: ExprId) -> crate::api::expr::SetEx {
crate::api::expr::SetEx::from_raw_parts(self.id, Arc::clone(&self.inner), id)
}
pub fn symbol(&self, name: &str) -> crate::api::expr::Ex {
assert!(!name.is_empty(), "symbol name cannot be empty");
let id = self.inner.write().arena.symbol(name);
self.make_ex(id)
}
pub fn var(&self, name: &str) -> crate::api::expr::Ex {
self.symbol(name)
}
pub fn parse(
&self,
input: &str,
) -> Result<crate::api::expr::Ex, crate::base::errors::SymplexError> {
crate::output::parse::parse(self, input).map_err(|e| {
crate::base::errors::SymplexError::ComputationFailed {
operation: "parse",
reason: e.to_string(),
}
})
}
pub fn symbol_with(&self, name: &str, assumptions: &[Assumption]) -> crate::api::expr::Ex {
assert!(!name.is_empty(), "symbol name cannot be empty");
let mut inner = self.inner.write();
let sym_id = inner.arena.symbols.intern(name);
let expr_id = inner
.arena
.intern(crate::base::node::ExprNode::Symbol(sym_id));
let mut a = crate::base::assumptions::Assumptions::default();
for assumption in assumptions {
let (prop, value) = assumption.to_prop_value();
if value {
a.assert_true(prop);
} else {
a.assert_false(prop);
}
}
inner.arena.set_symbol_assumptions(sym_id, a);
inner.assumptions.lock().set_symbol_assumptions(expr_id, a);
drop(inner);
self.make_ex(expr_id)
}
pub fn query(&self, ex: &crate::api::expr::Ex, prop: Props) -> Option<bool> {
let inner = self.inner.read();
inner
.assumptions
.lock()
.query(&inner.arena, ex.raw_id(), prop)
}
pub fn int(&self, n: i64) -> crate::api::expr::Ex {
let id = self.inner.write().arena.int(n);
self.make_ex(id)
}
#[must_use]
pub fn zero(&self) -> crate::api::expr::Ex {
self.int(0)
}
#[must_use]
pub fn one(&self) -> crate::api::expr::Ex {
self.int(1)
}
pub fn rational(&self, p: i64, q: i64) -> crate::api::expr::Ex {
let id = self.inner.write().arena.rational(p, q);
self.make_ex(id)
}
pub fn pi(&self) -> crate::api::expr::Ex {
let id = self.inner.read().arena.pi;
self.make_ex(id)
}
pub fn e(&self) -> crate::api::expr::Ex {
let id = self.inner.read().arena.e_const;
self.make_ex(id)
}
pub fn i_unit(&self) -> crate::api::expr::Ex {
let id = self.inner.read().arena.i_unit;
self.make_ex(id)
}
pub fn euler_gamma(&self) -> crate::api::expr::Ex {
let id = self.inner.read().arena.euler_gamma;
self.make_ex(id)
}
pub fn catalan(&self) -> crate::api::expr::Ex {
let id = self.inner.read().arena.catalan;
self.make_ex(id)
}
pub fn golden_ratio(&self) -> crate::api::expr::Ex {
let id = self.inner.read().arena.golden_ratio;
self.make_ex(id)
}
pub fn physical_constant(
&self,
name: &str,
value: crate::api::expr::Ex,
) -> crate::api::expr::Ex {
let val_id = value.raw_id();
let id = self.inner.write().arena.physical_constant(name, val_id);
self.make_ex(id)
}
pub fn infinity(&self) -> crate::api::expr::Ex {
let id = self.inner.read().arena.infinity;
self.make_ex(id)
}
pub fn neg_infinity(&self) -> crate::api::expr::Ex {
let inner = self.inner.read();
let id = inner.arena.neg_infinity;
drop(inner);
self.make_ex(id)
}
pub fn complex_infinity(&self) -> crate::api::expr::Ex {
let id = self.inner.read().arena.complex_infinity;
self.make_ex(id)
}
pub fn nan(&self) -> crate::api::expr::Ex {
let id = self.inner.read().arena.nan;
self.make_ex(id)
}
pub fn empty_set(&self) -> crate::api::expr::SetEx {
let id = self.inner.read().arena.empty_set;
self.make_set_ex(id)
}
pub fn universal_set(&self) -> crate::api::expr::SetEx {
let id = self.inner.read().arena.universal_set;
self.make_set_ex(id)
}
pub fn reals(&self) -> crate::api::expr::SetEx {
let mut inner = self.inner.write();
let neg_inf = inner.arena.neg_infinity;
let inf = inner.arena.infinity;
let id = inner
.arena
.interval(neg_inf, inf, crate::base::node::INTERVAL_BOTH_OPEN);
drop(inner);
self.make_set_ex(id)
}
pub fn interval(
&self,
start: &crate::api::expr::Ex,
end: &crate::api::expr::Ex,
left_open: bool,
right_open: bool,
) -> crate::api::expr::SetEx {
let mut flags: u8 = 0;
if left_open {
flags |= crate::base::node::INTERVAL_LEFT_OPEN;
}
if right_open {
flags |= crate::base::node::INTERVAL_RIGHT_OPEN;
}
let id = self
.inner
.write()
.arena
.interval(start.raw_id(), end.raw_id(), flags);
self.make_set_ex(id)
}
pub fn finite_set(&self, elements: &[crate::api::expr::Ex]) -> crate::api::expr::SetEx {
let ids: Vec<ExprId> = elements.iter().map(|e| e.raw_id()).collect();
let id = self.inner.write().arena.finite_set(&ids);
self.make_set_ex(id)
}
pub fn with_arena_mut<R>(&self, f: impl FnOnce(&mut crate::base::arena::Arena) -> R) -> R {
let mut guard = self.inner.write();
f(&mut guard.arena)
}
pub fn from_tree(&self, tree: &crate::output::tree::ExprTree) -> crate::api::expr::Ex {
let mut inner = self.inner.write();
let id = crate::output::tree::tree_to_expr(&mut inner.arena, tree);
drop(inner);
self.make_ex(id)
}
pub fn from_json(&self, json: &str) -> Result<crate::api::expr::Ex, serde_json::Error> {
let tree: crate::output::tree::ExprTree = serde_json::from_str(json)?;
Ok(self.from_tree(&tree))
}
pub fn solve_system<E: crate::api::expr_solve_ext::ZeroForm>(
&self,
equations: &[E],
variables: &[crate::api::expr::Ex],
) -> Result<crate::api::expr_solve_ext::LinearSolution, crate::base::errors::SymplexError> {
crate::api::expr_solve_ext::linsolve(equations, variables)
}
pub fn compact(&self, roots: &[crate::api::expr::Ex]) -> (Context, Vec<crate::api::expr::Ex>) {
let new_ctx = Context::new();
if roots.is_empty() {
tracing::debug!("compact: 0 roots — returning empty context");
return (new_ctx, Vec::new());
}
let src_inner = self.inner.read();
let mut dst_inner = new_ctx.inner.write();
let mut map = rustc_hash::FxHashMap::default();
let mut new_roots = Vec::with_capacity(roots.len());
for root in roots {
let new_id = crate::base::compact::transfer_subtree(
&src_inner.arena,
&mut dst_inner.arena,
root.raw_id(),
&mut map,
);
new_roots.push(crate::api::expr::Ex::from_raw_parts(
new_ctx.id,
Arc::clone(&new_ctx.inner),
new_id,
));
}
tracing::debug!(
src_nodes = src_inner.arena.node_count(),
dst_nodes = dst_inner.arena.node_count(),
roots = roots.len(),
transferred = map.len(),
"compact: arena compaction complete",
);
drop(dst_inner);
drop(src_inner);
(new_ctx, new_roots)
}
pub fn node_count(&self) -> usize {
self.inner.read().arena.node_count()
}
pub fn liveness_ratio(&self, roots: &[crate::api::expr::Ex]) -> f64 {
let root_ids: Vec<crate::base::node::ExprId> = roots.iter().map(|r| r.raw_id()).collect();
let inner = self.inner.read();
crate::base::compact::liveness_ratio(&inner.arena, &root_ids)
}
pub fn should_compact(&self, roots: &[crate::api::expr::Ex]) -> bool {
let root_ids: Vec<crate::base::node::ExprId> = roots.iter().map(|r| r.raw_id()).collect();
let inner = self.inner.read();
crate::base::compact::should_compact(&inner.arena, &root_ids)
}
}
impl Default for Context {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Context")
.field("id", &self.id)
.field("node_count", &self.inner.read().arena.node_count())
.finish()
}
}