sim_lib_numbers_cas_diff/implementation/
function.rs1use std::{any::Any, sync::Arc};
5
6use sim_kernel::{
7 AbiVersion, Args, Callable, ClassRef, Cx, DefaultFactory, Dependency, Error, Export, Expr,
8 Factory, Lib, LibManifest, LibTarget, Linker, Object, Result, Symbol, Value, Version,
9};
10use sim_lib_numbers_cas::{
11 cas_expr_to_surface_expr, cas_expr_to_value, extract_symbolish, value_to_cas_expr,
12};
13use sim_lib_numbers_core::domains;
14
15use super::diff::{diff_cas, diff_symbol};
16use super::func_surface::func_surface_body;
17use super::integrate::integrate_sym_symbol;
18use super::integrate_function::IntegrateSymFunction;
19
20pub struct CasDiffLib;
26
27impl CasDiffLib {
28 pub fn new() -> Self {
30 Self
31 }
32}
33
34impl Default for CasDiffLib {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40impl Lib for CasDiffLib {
41 fn manifest(&self) -> LibManifest {
42 LibManifest {
43 id: domains::cas_diff(),
44 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
45 abi: AbiVersion { major: 0, minor: 1 },
46 target: LibTarget::HostRegistered,
47 requires: Vec::<Dependency>::new(),
48 capabilities: Vec::new(),
49 exports: vec![
50 Export::Function {
51 symbol: diff_symbol(),
52 function_id: None,
53 },
54 Export::Function {
55 symbol: integrate_sym_symbol(),
56 function_id: None,
57 },
58 ],
59 }
60 }
61
62 fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
63 linker.function_value(
64 diff_symbol(),
65 DefaultFactory
66 .opaque(Arc::new(DiffFunction))
67 .expect("diff function should be boxable"),
68 )?;
69 linker.function_value(
70 integrate_sym_symbol(),
71 DefaultFactory
72 .opaque(Arc::new(IntegrateSymFunction))
73 .expect("integrate-sym function should be boxable"),
74 )?;
75 Ok(())
76 }
77}
78
79#[derive(Clone)]
80struct DiffFunction;
81
82impl Object for DiffFunction {
83 fn display(&self, _cx: &mut Cx) -> Result<String> {
84 Ok(format!("#<function {}>", diff_symbol()))
85 }
86
87 fn as_any(&self) -> &dyn Any {
88 self
89 }
90}
91
92impl sim_kernel::ObjectCompat for DiffFunction {
93 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
94 if let Some(value) = cx
95 .registry()
96 .class_by_symbol(&Symbol::qualified("core", "Function"))
97 {
98 return Ok(value.clone());
99 }
100 DefaultFactory.class_stub(
101 sim_kernel::CORE_FUNCTION_CLASS_ID,
102 Symbol::qualified("core", "Function"),
103 )
104 }
105 fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
106 Ok(Expr::Symbol(diff_symbol()))
107 }
108 fn as_callable(&self) -> Option<&dyn Callable> {
109 Some(self)
110 }
111}
112
113impl Callable for DiffFunction {
114 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
115 let values = args.into_vec();
116 let [expr_value, var] = values.as_slice() else {
117 return Err(Error::Eval(format!(
118 "{} expects exactly two arguments",
119 diff_symbol()
120 )));
121 };
122 let var = extract_symbolish(cx, var)?.ok_or_else(|| {
123 Error::Eval(format!(
124 "{} expects a quoted symbol or symbol as its second argument",
125 diff_symbol()
126 ))
127 })?;
128 if let Some(number) = cx.number_value_ref(expr_value.clone())?
129 && number.domain == domains::func()
130 {
131 return diff_func_value(cx, expr_value.clone(), &var);
132 }
133 let expr = value_to_cas_expr(cx, expr_value.clone())?;
134 let derivative = diff_cas(cx, &expr, &var)?;
135 cas_expr_to_value(cx, derivative)
136 }
137}
138
139fn diff_func_value(cx: &mut Cx, value: Value, var: &Symbol) -> Result<Value> {
140 let (vars_expr, body) = func_surface_body(cx, &value)?;
141 let derivative = diff_cas(cx, &body, var)?;
142 let derivative_expr = cas_expr_to_surface_expr(cx, &derivative)?;
143 cx.eval_expr(Expr::Call {
144 operator: Box::new(Expr::Symbol(Symbol::new("fn"))),
145 args: vec![vars_expr, derivative_expr],
146 })
147}