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