1use std::sync::Arc;
6
7use sim_kernel::{
8 AbiVersion, ClassRef, Cx, DefaultFactory, Dependency, Export, Expr, Factory, Lib, LibManifest,
9 LibTarget, Linker, NumberDomain, NumberLiteral, Object, PromotionRule, Result, Symbol, Value,
10 ValuePromotionRule, Version,
11};
12use sim_lib_numbers_core::{
13 NumberLiteralClass, NumberLiteralShape, ScalarBinaryOp, ScalarOps, ScalarReductionOp,
14 ScalarUnaryOp, class_surface_or_symbol, domains, install_scalar_ops, shape_surface_or_symbol,
15};
16use sim_shape::shape_value;
17
18use super::ops::{
19 ComplexRuleFn, ValueRuleFn, canonical_complex, parse_complex_literal, register_promotions,
20};
21use super::surface::NumberValueShape;
22use super::value::{build_complex_value_class, complex_value_class_symbol};
23
24pub fn number_domain() -> Symbol {
27 domains::complex()
28}
29
30pub fn literal_class_symbol() -> Symbol {
33 domains::literal_class("complex")
34}
35
36pub fn literal_instance_shape_symbol() -> Symbol {
39 Symbol::qualified(literal_class_symbol().to_string(), "instance-shape")
40}
41
42pub fn value_shape_symbol() -> Symbol {
45 domains::value_shape(&number_domain())
46}
47
48pub fn f64_domain() -> Symbol {
50 domains::f64()
51}
52
53pub fn i64_domain() -> Symbol {
55 domains::i64()
56}
57
58pub fn rational_domain() -> Symbol {
61 domains::rational()
62}
63
64pub fn add_symbol() -> Symbol {
66 Symbol::qualified("math", "add")
67}
68
69pub fn sub_symbol() -> Symbol {
71 Symbol::qualified("math", "sub")
72}
73
74pub fn mul_symbol() -> Symbol {
76 Symbol::qualified("math", "mul")
77}
78
79pub fn div_symbol() -> Symbol {
81 Symbol::qualified("math", "div")
82}
83
84pub fn neg_symbol() -> Symbol {
86 Symbol::qualified("math", "neg")
87}
88
89pub fn sum_symbol() -> Symbol {
92 Symbol::qualified("math", "sum")
93}
94
95pub fn product_symbol() -> Symbol {
98 Symbol::qualified("math", "product")
99}
100
101#[sim_citizen_derive::non_citizen(
102 reason = "numbers/complex number-domain marker; reconstruct by loading the complex number lib",
103 kind = "marker"
104)]
105pub struct ComplexNumberDomain;
109
110impl NumberDomain for ComplexNumberDomain {
111 fn symbol(&self) -> Symbol {
112 number_domain()
113 }
114
115 fn parse_priority(&self) -> i32 {
116 -10
117 }
118
119 fn parse_literal(&self, cx: &mut Cx, text: &str) -> Result<Option<Value>> {
120 let Some((real, imag)) = parse_complex_literal(text) else {
121 return Ok(None);
122 };
123 cx.factory()
124 .number_literal(number_domain(), canonical_complex(real, imag))
125 .map(Some)
126 }
127
128 fn encode_literal(&self, cx: &mut Cx, value: Value) -> Result<Option<NumberLiteral>> {
129 let expr = value.object().as_expr(cx)?;
130 match expr {
131 Expr::Number(number) if number.domain == self.symbol() => Ok(Some(number)),
132 _ => Ok(None),
133 }
134 }
135
136 fn promotions(&self) -> Vec<PromotionRule> {
137 Vec::new()
138 }
139}
140
141impl Object for ComplexNumberDomain {
142 fn display(&self, _cx: &mut Cx) -> Result<String> {
143 Ok("#<number-domain numbers/complex>".to_owned())
144 }
145
146 fn as_any(&self) -> &dyn std::any::Any {
147 self
148 }
149}
150
151impl sim_kernel::ObjectCompat for ComplexNumberDomain {
152 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
153 sim_lib_numbers_core::number_domain_class_stub(cx)
154 }
155 fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
156 Ok(Expr::Symbol(number_domain()))
157 }
158 fn as_table(&self, cx: &mut Cx) -> Result<Value> {
159 let literal_class = class_surface_or_symbol(cx, literal_class_symbol())?;
160 let instance_shape = shape_surface_or_symbol(cx, literal_instance_shape_symbol())?;
161 let value_shape = shape_surface_or_symbol(cx, value_shape_symbol())?;
162 cx.factory().table(vec![
163 (Symbol::new("symbol"), cx.factory().symbol(number_domain())?),
164 (
165 Symbol::new("kind"),
166 cx.factory().string("number-domain".to_owned())?,
167 ),
168 (
169 Symbol::new("numeric-family"),
170 cx.factory().string("complex".to_owned())?,
171 ),
172 (
173 Symbol::new("canonical-form"),
174 cx.factory().string("a+bi".to_owned())?,
175 ),
176 (
177 Symbol::new("parse-priority"),
178 cx.factory().string("-10".to_owned())?,
179 ),
180 (Symbol::new("literal-class"), literal_class),
181 (Symbol::new("instance-shape"), instance_shape),
182 (Symbol::new("value-shape"), value_shape),
183 ])
184 }
185 fn as_number_domain(&self) -> Option<&dyn NumberDomain> {
186 Some(self)
187 }
188}
189
190pub struct ComplexNumbersLib;
209
210impl ComplexNumbersLib {
211 pub fn new() -> Self {
213 Self
214 }
215}
216
217impl Default for ComplexNumbersLib {
218 fn default() -> Self {
219 Self::new()
220 }
221}
222
223impl Lib for ComplexNumbersLib {
224 fn manifest(&self) -> LibManifest {
225 LibManifest {
226 id: number_domain(),
227 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
228 abi: AbiVersion { major: 0, minor: 1 },
229 target: LibTarget::HostRegistered,
230 requires: Vec::<Dependency>::new(),
231 capabilities: Vec::new(),
232 exports: vec![
233 Export::NumberDomain {
234 symbol: number_domain(),
235 number_domain_id: None,
236 },
237 Export::Class {
238 symbol: literal_class_symbol(),
239 class_id: None,
240 },
241 Export::Class {
242 symbol: complex_value_class_symbol(),
243 class_id: None,
244 },
245 Export::Shape {
246 symbol: literal_instance_shape_symbol(),
247 shape_id: None,
248 },
249 Export::Shape {
250 symbol: value_shape_symbol(),
251 shape_id: None,
252 },
253 ],
254 }
255 }
256
257 fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
258 let instance_shape = Arc::new(NumberLiteralShape::new(
259 number_domain(),
260 "ComplexLiteral",
261 [
262 "number literal in the numbers/complex domain",
263 "matches Expr::Number where domain == numbers/complex",
264 ],
265 ));
266 let literal_class = Arc::new(NumberLiteralClass::new(
267 literal_class_symbol(),
268 number_domain(),
269 "complex",
270 "a+bi",
271 literal_instance_shape_symbol(),
272 instance_shape.clone(),
273 ));
274 let value_shape = Arc::new(NumberValueShape::new(
275 number_domain(),
276 "ComplexValue",
277 [
278 "number value in the numbers/complex domain",
279 "accepts any NumberValue where domain == numbers/complex",
280 ],
281 ));
282 linker.number_domain_value(
283 number_domain(),
284 DefaultFactory
285 .opaque(Arc::new(ComplexNumberDomain))
286 .expect("number domain should be boxable"),
287 )?;
288 let class_id = linker.class_value(
289 literal_class_symbol(),
290 DefaultFactory
291 .opaque(literal_class.clone())
292 .expect("number literal class should be boxable"),
293 )?;
294 literal_class.set_id(class_id);
295 register_complex_value_class(linker)?;
296 linker.shape_value(
297 literal_instance_shape_symbol(),
298 shape_value(literal_instance_shape_symbol(), instance_shape),
299 )?;
300 linker.shape_value(
301 value_shape_symbol(),
302 shape_value(value_shape_symbol(), value_shape),
303 )?;
304 register_promotions(linker);
305 for rule in [
306 ValuePromotionRule {
307 from_domain: f64_domain(),
308 to_domain: number_domain(),
309 cost: 1,
310 convert: super::ops::promote_f64_value_to_complex,
311 },
312 ValuePromotionRule {
313 from_domain: i64_domain(),
314 to_domain: number_domain(),
315 cost: 1,
316 convert: super::ops::promote_i64_value_to_complex,
317 },
318 ValuePromotionRule {
319 from_domain: rational_domain(),
320 to_domain: number_domain(),
321 cost: 1,
322 convert: super::ops::promote_rational_value_to_complex,
323 },
324 ] {
325 linker.value_promotion_rule(rule);
326 }
327 let binary = [
328 (
329 add_symbol(),
330 super::ops::complex_add_rule as ComplexRuleFn,
331 super::ops::complex_add_value_rule as ValueRuleFn,
332 ),
333 (
334 sub_symbol(),
335 super::ops::complex_sub_rule,
336 super::ops::complex_sub_value_rule,
337 ),
338 (
339 mul_symbol(),
340 super::ops::complex_mul_rule,
341 super::ops::complex_mul_value_rule,
342 ),
343 (
344 div_symbol(),
345 super::ops::complex_div_rule,
346 super::ops::complex_div_value_rule,
347 ),
348 ]
349 .into_iter()
350 .map(|(operator, literal_apply, value_apply)| ScalarBinaryOp {
351 operator,
352 literal_cost: 0,
353 literal_apply,
354 value_cost: 1,
355 value_apply,
356 })
357 .collect();
358 let ops = ScalarOps {
359 domain: number_domain(),
360 binary,
361 unary: vec![ScalarUnaryOp {
362 operator: neg_symbol(),
363 literal_cost: 0,
364 literal_apply: super::ops::complex_neg_rule,
365 value_cost: 1,
366 value_apply: super::ops::complex_neg_value_rule,
367 }],
368 reduction: vec![
369 ScalarReductionOp {
370 operator: sum_symbol(),
371 literal_cost: 0,
372 literal_apply: super::ops::complex_sum_rule,
373 value_cost: 1,
374 value_apply: super::ops::complex_sum_value_rule,
375 },
376 ScalarReductionOp {
377 operator: product_symbol(),
378 literal_cost: 0,
379 literal_apply: super::ops::complex_product_rule,
380 value_cost: 1,
381 value_apply: super::ops::complex_product_value_rule,
382 },
383 ],
384 };
385 install_scalar_ops(linker, &ops);
386 Ok(())
387 }
388}
389
390fn register_complex_value_class(linker: &mut Linker<'_>) -> Result<()> {
391 let complex_class = build_complex_value_class();
392 let class_id = linker.class_value(
393 complex_value_class_symbol(),
394 DefaultFactory
395 .opaque(complex_class.clone())
396 .expect("complex value class should be boxable"),
397 )?;
398 complex_class.set_id(class_id);
399 Ok(())
400}
401
402fn install_complex_value_citizen(linker: &mut Linker<'_>) -> Result<()> {
403 register_complex_value_class(linker)
404}
405
406fn conformance_complex_value_citizen(cx: &mut sim_kernel::Cx) -> Result<()> {
407 let value = super::value::complex_value(cx, 1.5, -2.25)?;
408 sim_citizen::check_value_fixture(cx, value)
409}
410
411sim_citizen::inventory::submit! {
412 sim_citizen::CitizenInfo {
413 symbol: "numbers/Complex",
414 version: 1,
415 crate_name: env!("CARGO_PKG_NAME"),
416 arity: 2,
417 install: install_complex_value_citizen,
418 conformance: conformance_complex_value_citizen,
419 }
420}