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
28struct ExprInner {
31 arena_ptr: *mut Arena,
32 ctx_ptr: *mut AtomArena<'static>,
33 atom: Atom<'static>,
34}
35
36unsafe impl Send for ExprInner {}
39unsafe impl Sync for ExprInner {}
45
46impl Drop for ExprInner {
47 fn drop(&mut self) {
48 unsafe {
51 let _ = Box::from_raw(self.ctx_ptr);
52 let _ = Box::from_raw(self.arena_ptr);
53 }
54 }
55}
56
57struct ArenaGuard {
60 arena_ptr: *mut Arena,
61 ctx_ptr: *mut AtomArena<'static>,
62 armed: bool,
63}
64
65impl ArenaGuard {
66 fn new(arena_ptr: *mut Arena, ctx_ptr: *mut AtomArena<'static>) -> Self {
67 ArenaGuard {
68 arena_ptr,
69 ctx_ptr,
70 armed: true,
71 }
72 }
73
74 fn disarm(&mut self) {
75 self.armed = false;
76 }
77}
78
79impl Drop for ArenaGuard {
80 fn drop(&mut self) {
81 if self.armed {
82 unsafe {
84 let _ = Box::from_raw(self.ctx_ptr);
85 let _ = Box::from_raw(self.arena_ptr);
86 }
87 }
88 }
89}
90
91impl ExprInner {
92 fn ctx(&self) -> &'static AtomArena<'static> {
94 unsafe { &*self.ctx_ptr }
96 }
97
98 fn new_pair() -> (*mut Arena, *mut AtomArena<'static>) {
100 let arena_box: Box<Arena> = Box::new(Arena::new());
101 let arena_ptr = Box::into_raw(arena_box);
102 let arena_ref: &'static Arena = unsafe { &*arena_ptr };
104 let ctx = AtomArena::new(arena_ref);
105 let ctx_ptr = Box::into_raw(Box::new(ctx));
106 (arena_ptr, ctx_ptr)
107 }
108
109 fn build<F>(f: F) -> PyResult<Box<Self>>
111 where
112 F: FnOnce(&'static AtomArena<'static>) -> Result<Atom<'static>, String>,
113 {
114 let (arena_ptr, ctx_ptr) = Self::new_pair();
115 let mut guard = ArenaGuard::new(arena_ptr, ctx_ptr);
116 let ctx = unsafe { &*ctx_ptr };
117 let atom = f(ctx).map_err(PyValueError::new_err)?;
120 let normalized = normalize(ctx, atom);
121 guard.disarm();
122 Ok(Box::new(ExprInner {
123 arena_ptr,
124 ctx_ptr,
125 atom: normalized,
126 }))
127 }
128
129 fn from_str(input: &str) -> PyResult<Box<Self>> {
131 let static_input = unsafe { extend_str_lifetime(input) };
132 Self::build(|ctx| match parse(ctx, static_input) {
133 Ok(a) => Ok(a),
134 Err(e) => Err(format!("parse error: {e}")),
135 })
136 }
137
138 fn from_string_src(src: String) -> PyResult<Box<Self>> {
140 let static_src = unsafe { extend_str_lifetime(&src) };
141 Self::build(|ctx| match parse(ctx, static_src) {
142 Ok(a) => Ok(a),
143 Err(e) => Err(format!("parse error: {e}")),
144 })
145 }
146}
147
148#[pyclass(name = "Expression")]
158pub struct Expression {
159 inner: Box<ExprInner>,
160}
161
162#[pymethods]
163impl Expression {
164 #[new]
166 fn new(input: &str) -> PyResult<Self> {
167 Ok(Expression {
168 inner: ExprInner::from_str(input)?,
169 })
170 }
171
172 fn __str__(&self) -> String {
173 self.inner.atom.to_string()
174 }
175
176 fn __repr__(&self) -> String {
177 format!("Expression({:?})", self.inner.atom.to_string())
178 }
179
180 fn __add__(&self, other: &Expression) -> PyResult<Expression> {
181 let left = self.inner.atom.to_string();
182 let right = other.inner.atom.to_string();
183 let combined = format!("({left}) + ({right})");
184 Ok(Expression {
185 inner: ExprInner::from_string_src(combined)?,
186 })
187 }
188
189 fn __sub__(&self, other: &Expression) -> PyResult<Expression> {
190 let left = self.inner.atom.to_string();
191 let right = other.inner.atom.to_string();
192 let combined = format!("({left}) + (-1)*({right})");
193 Ok(Expression {
194 inner: ExprInner::from_string_src(combined)?,
195 })
196 }
197
198 fn __mul__(&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 __pow__(&self, other: &Expression, _modulo: Option<&Expression>) -> PyResult<Expression> {
208 let left = self.inner.atom.to_string();
209 let right = other.inner.atom.to_string();
210 let combined = format!("({left})^({right})");
211 Ok(Expression {
212 inner: ExprInner::from_string_src(combined)?,
213 })
214 }
215
216 fn __neg__(&self) -> PyResult<Expression> {
217 let src = self.inner.atom.to_string();
218 Ok(Expression {
219 inner: ExprInner::from_string_src(format!("(-1)*({src})"))?,
220 })
221 }
222
223 fn __eq__(&self, other: &Expression) -> bool {
224 let a = normalize(self.inner.ctx(), self.inner.atom);
226 let b = normalize(other.inner.ctx(), other.inner.atom);
227 a.to_string() == b.to_string()
228 }
229
230 fn __hash__(&self) -> u64 {
231 use std::collections::hash_map::DefaultHasher;
232 use std::hash::{Hash, Hasher};
233 let mut h = DefaultHasher::new();
234 self.inner.atom.to_string().hash(&mut h);
235 h.finish()
236 }
237
238 fn clone(&self) -> PyResult<Expression> {
240 let src = self.inner.atom.to_string();
241 Ok(Expression {
242 inner: ExprInner::from_string_src(src)?,
243 })
244 }
245
246 fn simplify(&self) -> PyResult<Expression> {
248 let src = self.inner.atom.to_string();
249 let static_src = unsafe { extend_str_lifetime(&src) };
250 ExprInner::build(|ctx| {
251 let a = parse(ctx, static_src).map_err(|e| e.to_string())?;
252 let rules = default_rules(ctx, &());
253 Ok(simplify(ctx, a, &rules, 20))
254 })
255 .map(|inner| Expression { inner })
256 }
257
258 fn diff(&self, var: &str) -> PyResult<Expression> {
260 let src = self.inner.atom.to_string();
261 let static_src = unsafe { extend_str_lifetime(&src) };
262 let var_sym = Symbol::new(var);
263 ExprInner::build(|ctx| match parse(ctx, static_src) {
264 Ok(a) => Ok(diff(ctx, a, var_sym)),
265 Err(e) => Err(e.to_string()),
266 })
267 .map(|inner| Expression { inner })
268 }
269
270 fn integrate(&self, var: &str) -> PyResult<Expression> {
272 let src = self.inner.atom.to_string();
273 let static_src = unsafe { extend_str_lifetime(&src) };
274 let var_sym = Symbol::new(var);
275 ExprInner::build(|ctx| match parse(ctx, static_src) {
276 Ok(a) => Ok(integrate(ctx, a, var_sym)),
277 Err(e) => Err(e.to_string()),
278 })
279 .map(|inner| Expression { inner })
280 }
281
282 fn taylor(&self, var: &str, point: &Expression, order: usize) -> PyResult<Expression> {
284 let expr_src = self.inner.atom.to_string();
285 let point_src = point.inner.atom.to_string();
286 let static_expr = unsafe { extend_str_lifetime(&expr_src) };
287 let static_point = unsafe { extend_str_lifetime(&point_src) };
288 let var_sym = Symbol::new(var);
289 ExprInner::build(|ctx| {
290 let e = parse(ctx, static_expr).map_err(|e| e.to_string())?;
291 let p = parse(ctx, static_point).map_err(|e| e.to_string())?;
292 Ok(taylor(ctx, e, var_sym, p, order))
293 })
294 .map(|inner| Expression { inner })
295 }
296
297 fn substitute(&self, var: &str, replacement: &Expression) -> PyResult<Expression> {
299 let expr_src = self.inner.atom.to_string();
300 let repl_src = replacement.inner.atom.to_string();
301 let static_expr = unsafe { extend_str_lifetime(&expr_src) };
302 let static_repl = unsafe { extend_str_lifetime(&repl_src) };
303 let var_sym = Symbol::new(var);
304 ExprInner::build(|ctx| {
305 let e = parse(ctx, static_expr).map_err(|e| e.to_string())?;
306 let r = parse(ctx, static_repl).map_err(|e| e.to_string())?;
307 Ok(substitute(ctx, e, var_sym, r))
308 })
309 .map(|inner| Expression { inner })
310 }
311}