1use ocas_atom::{Atom, AtomArena, Symbol, normalize::normalize};
8use ocas_calc::{diff, integrate, substitute, taylor};
9use ocas_core::arena::Arena;
10use ocas_parse::parse;
11use ocas_rewrite::rules::default_rules;
12use ocas_rewrite::simplify::simplify;
13use pyo3::exceptions::PyValueError;
14use pyo3::prelude::*;
15
16unsafe fn extend_str_lifetime(s: &str) -> &'static str {
25 unsafe { std::mem::transmute::<&str, &'static str>(s) }
26}
27
28pub(crate) unsafe fn extend_str_lifetime_pub(s: &str) -> &'static str {
31 unsafe { extend_str_lifetime(s) }
32}
33
34struct ExprInner {
37 arena_ptr: *mut Arena,
38 ctx_ptr: *mut AtomArena<'static>,
39 atom: Atom<'static>,
40}
41
42unsafe impl Send for ExprInner {}
45unsafe impl Sync for ExprInner {}
51
52impl Drop for ExprInner {
53 fn drop(&mut self) {
54 unsafe {
57 let _ = Box::from_raw(self.ctx_ptr);
58 let _ = Box::from_raw(self.arena_ptr);
59 }
60 }
61}
62
63struct ArenaGuard {
66 arena_ptr: *mut Arena,
67 ctx_ptr: *mut AtomArena<'static>,
68 armed: bool,
69}
70
71impl ArenaGuard {
72 fn new(arena_ptr: *mut Arena, ctx_ptr: *mut AtomArena<'static>) -> Self {
73 ArenaGuard {
74 arena_ptr,
75 ctx_ptr,
76 armed: true,
77 }
78 }
79
80 fn disarm(&mut self) {
81 self.armed = false;
82 }
83}
84
85impl Drop for ArenaGuard {
86 fn drop(&mut self) {
87 if self.armed {
88 unsafe {
90 let _ = Box::from_raw(self.ctx_ptr);
91 let _ = Box::from_raw(self.arena_ptr);
92 }
93 }
94 }
95}
96
97impl ExprInner {
98 fn ctx(&self) -> &'static AtomArena<'static> {
100 unsafe { &*self.ctx_ptr }
102 }
103
104 fn new_pair() -> (*mut Arena, *mut AtomArena<'static>) {
106 let arena_box: Box<Arena> = Box::new(Arena::new());
107 let arena_ptr = Box::into_raw(arena_box);
108 let arena_ref: &'static Arena = unsafe { &*arena_ptr };
110 let ctx = AtomArena::new(arena_ref);
111 let ctx_ptr = Box::into_raw(Box::new(ctx));
112 (arena_ptr, ctx_ptr)
113 }
114
115 fn build<F>(f: F) -> PyResult<Box<Self>>
117 where
118 F: FnOnce(&'static AtomArena<'static>) -> Result<Atom<'static>, String>,
119 {
120 let (arena_ptr, ctx_ptr) = Self::new_pair();
121 let mut guard = ArenaGuard::new(arena_ptr, ctx_ptr);
122 let ctx = unsafe { &*ctx_ptr };
123 let atom = f(ctx).map_err(PyValueError::new_err)?;
126 let normalized = normalize(ctx, atom);
127 guard.disarm();
128 Ok(Box::new(ExprInner {
129 arena_ptr,
130 ctx_ptr,
131 atom: normalized,
132 }))
133 }
134
135 fn from_str(input: &str) -> PyResult<Box<Self>> {
137 let static_input = unsafe { extend_str_lifetime(input) };
138 Self::build(|ctx| match parse(ctx, static_input) {
139 Ok(a) => Ok(a),
140 Err(e) => Err(format!("parse error: {e}")),
141 })
142 }
143
144 fn from_string_src(src: String) -> PyResult<Box<Self>> {
146 let static_src = unsafe { extend_str_lifetime(&src) };
147 Self::build(|ctx| match parse(ctx, static_src) {
148 Ok(a) => Ok(a),
149 Err(e) => Err(format!("parse error: {e}")),
150 })
151 }
152}
153
154#[pyclass(name = "Expression")]
164pub struct Expression {
165 inner: Box<ExprInner>,
166}
167
168impl Expression {
169 pub(crate) fn ctx_ref(&self) -> &'static AtomArena<'static> {
171 self.inner.ctx()
172 }
173
174 pub(crate) fn atom(&self) -> Atom<'static> {
176 self.inner.atom
177 }
178}
179
180#[pymethods]
181impl Expression {
182 #[new]
184 fn new(input: &str) -> PyResult<Self> {
185 Ok(Expression {
186 inner: ExprInner::from_str(input)?,
187 })
188 }
189
190 fn __str__(&self) -> String {
191 self.inner.atom.to_string()
192 }
193
194 fn __repr__(&self) -> String {
195 format!("Expression({:?})", self.inner.atom.to_string())
196 }
197
198 fn __add__(&self, other: &Expression) -> PyResult<Expression> {
199 let left = self.inner.atom.to_string();
200 let right = other.inner.atom.to_string();
201 let combined = format!("({left}) + ({right})");
202 Ok(Expression {
203 inner: ExprInner::from_string_src(combined)?,
204 })
205 }
206
207 fn __sub__(&self, other: &Expression) -> PyResult<Expression> {
208 let left = self.inner.atom.to_string();
209 let right = other.inner.atom.to_string();
210 let combined = format!("({left}) + (-1)*({right})");
211 Ok(Expression {
212 inner: ExprInner::from_string_src(combined)?,
213 })
214 }
215
216 fn __mul__(&self, other: &Expression) -> PyResult<Expression> {
217 let left = self.inner.atom.to_string();
218 let right = other.inner.atom.to_string();
219 let combined = format!("({left})*({right})");
220 Ok(Expression {
221 inner: ExprInner::from_string_src(combined)?,
222 })
223 }
224
225 fn __pow__(&self, other: &Expression, _modulo: Option<&Expression>) -> PyResult<Expression> {
226 let left = self.inner.atom.to_string();
227 let right = other.inner.atom.to_string();
228 let combined = format!("({left})^({right})");
229 Ok(Expression {
230 inner: ExprInner::from_string_src(combined)?,
231 })
232 }
233
234 fn __neg__(&self) -> PyResult<Expression> {
235 let src = self.inner.atom.to_string();
236 Ok(Expression {
237 inner: ExprInner::from_string_src(format!("(-1)*({src})"))?,
238 })
239 }
240
241 fn __eq__(&self, other: &Expression) -> bool {
242 let a = normalize(self.inner.ctx(), self.inner.atom);
244 let b = normalize(other.inner.ctx(), other.inner.atom);
245 a.to_string() == b.to_string()
246 }
247
248 fn __hash__(&self) -> u64 {
249 use std::collections::hash_map::DefaultHasher;
250 use std::hash::{Hash, Hasher};
251 let mut h = DefaultHasher::new();
252 self.inner.atom.to_string().hash(&mut h);
253 h.finish()
254 }
255
256 fn clone(&self) -> PyResult<Expression> {
258 let src = self.inner.atom.to_string();
259 Ok(Expression {
260 inner: ExprInner::from_string_src(src)?,
261 })
262 }
263
264 fn simplify(&self) -> PyResult<Expression> {
266 let src = self.inner.atom.to_string();
267 let static_src = unsafe { extend_str_lifetime(&src) };
268 ExprInner::build(|ctx| {
269 let a = parse(ctx, static_src).map_err(|e| e.to_string())?;
270 let rules = default_rules(ctx, &());
271 Ok(simplify(ctx, a, &rules, 20))
272 })
273 .map(|inner| Expression { inner })
274 }
275
276 fn diff(&self, var: &str) -> PyResult<Expression> {
278 let src = self.inner.atom.to_string();
279 let static_src = unsafe { extend_str_lifetime(&src) };
280 let var_sym = Symbol::new(var);
281 ExprInner::build(|ctx| match parse(ctx, static_src) {
282 Ok(a) => Ok(diff(ctx, a, var_sym)),
283 Err(e) => Err(e.to_string()),
284 })
285 .map(|inner| Expression { inner })
286 }
287
288 fn integrate(&self, var: &str) -> PyResult<Expression> {
290 let src = self.inner.atom.to_string();
291 let static_src = unsafe { extend_str_lifetime(&src) };
292 let var_sym = Symbol::new(var);
293 ExprInner::build(|ctx| match parse(ctx, static_src) {
294 Ok(a) => Ok(integrate(ctx, a, var_sym)),
295 Err(e) => Err(e.to_string()),
296 })
297 .map(|inner| Expression { inner })
298 }
299
300 fn taylor(&self, var: &str, point: &Expression, order: usize) -> PyResult<Expression> {
302 let expr_src = self.inner.atom.to_string();
303 let point_src = point.inner.atom.to_string();
304 let static_expr = unsafe { extend_str_lifetime(&expr_src) };
305 let static_point = unsafe { extend_str_lifetime(&point_src) };
306 let var_sym = Symbol::new(var);
307 ExprInner::build(|ctx| {
308 let e = parse(ctx, static_expr).map_err(|e| e.to_string())?;
309 let p = parse(ctx, static_point).map_err(|e| e.to_string())?;
310 Ok(taylor(ctx, e, var_sym, p, order))
311 })
312 .map(|inner| Expression { inner })
313 }
314
315 fn substitute(&self, var: &str, replacement: &Expression) -> PyResult<Expression> {
317 let expr_src = self.inner.atom.to_string();
318 let repl_src = replacement.inner.atom.to_string();
319 let static_expr = unsafe { extend_str_lifetime(&expr_src) };
320 let static_repl = unsafe { extend_str_lifetime(&repl_src) };
321 let var_sym = Symbol::new(var);
322 ExprInner::build(|ctx| {
323 let e = parse(ctx, static_expr).map_err(|e| e.to_string())?;
324 let r = parse(ctx, static_repl).map_err(|e| e.to_string())?;
325 Ok(substitute(ctx, e, var_sym, r))
326 })
327 .map(|inner| Expression { inner })
328 }
329}