Skip to main content

sim_lib_numbers_complex/implementation/
value.rs

1//! The `ComplexValue` object and its value class: the real/imaginary
2//! representation, its citizen encoding, and the read-constructor that rebuilds
3//! complex values.
4
5use std::sync::Arc;
6use std::sync::atomic::{AtomicU32, Ordering};
7
8use sim_citizen::{CitizenField, arity_error, decode_version};
9use sim_kernel::{
10    Args, Callable, Class, ClassId, ClassRef, Cx, DefaultFactory, Error, Expr, Factory,
11    NumberLiteral, NumberValue, Object, ObjectEncode, ObjectEncoding, ReadConstructor,
12    ReadConstructorRef, Result, ShapeRef, Symbol, TableRef, Value,
13};
14use sim_lib_numbers_core::domains;
15
16use super::literal::{number_domain, value_shape_symbol};
17use super::ops::canonical_complex;
18
19/// The symbol of the `numbers/complex` value class, used to register the class
20/// and to tag the read-constructor encoding of complex values.
21pub fn complex_value_class_symbol() -> Symbol {
22    domains::complex_value_class()
23}
24
25/// A complex number value: real and imaginary parts over an `f64` base scalar.
26#[derive(Clone, Debug, PartialEq)]
27pub struct ComplexValue {
28    /// The real part.
29    real: f64,
30    /// The imaginary part.
31    imag: f64,
32}
33
34impl ComplexValue {
35    /// Creates a complex value from its real and imaginary parts.
36    pub fn new(real: f64, imag: f64) -> Self {
37        Self { real, imag }
38    }
39
40    fn literal(&self) -> NumberLiteral {
41        NumberLiteral {
42            domain: number_domain(),
43            canonical: canonical_complex(self.real, self.imag),
44        }
45    }
46}
47
48impl Object for ComplexValue {
49    fn display(&self, _cx: &mut Cx) -> Result<String> {
50        Ok(self.literal().canonical)
51    }
52
53    fn as_any(&self) -> &dyn std::any::Any {
54        self
55    }
56}
57
58impl sim_kernel::ObjectCompat for ComplexValue {
59    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
60        if let Some(value) = cx.registry().class_by_symbol(&complex_value_class_symbol()) {
61            return Ok(value.clone());
62        }
63        DefaultFactory.class_stub(
64            sim_kernel::CORE_NUMBER_CLASS_ID,
65            Symbol::qualified("core", "Number"),
66        )
67    }
68
69    fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
70        Ok(Expr::Number(self.literal()))
71    }
72
73    fn as_table(&self, cx: &mut Cx) -> Result<Value> {
74        cx.factory().table(vec![
75            (
76                Symbol::new("kind"),
77                cx.factory().string("complex".to_owned())?,
78            ),
79            (Symbol::new("domain"), cx.factory().symbol(number_domain())?),
80            (
81                Symbol::new("real"),
82                cx.factory()
83                    .number_literal(domains::f64(), self.real.to_string())?,
84            ),
85            (
86                Symbol::new("imag"),
87                cx.factory()
88                    .number_literal(domains::f64(), self.imag.to_string())?,
89            ),
90        ])
91    }
92
93    fn as_number_value(&self) -> Option<&dyn NumberValue> {
94        Some(self)
95    }
96
97    fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
98        Some(self)
99    }
100}
101
102impl NumberValue for ComplexValue {
103    fn number_domain(&self, _cx: &mut Cx) -> Result<Symbol> {
104        Ok(number_domain())
105    }
106
107    fn number_literal(&self, _cx: &mut Cx) -> Result<Option<NumberLiteral>> {
108        Ok(Some(self.literal()))
109    }
110}
111
112impl ObjectEncode for ComplexValue {
113    fn object_encoding(&self, _cx: &mut Cx) -> Result<ObjectEncoding> {
114        Ok(ObjectEncoding::Constructor {
115            class: complex_value_class_symbol(),
116            args: vec![
117                Expr::Symbol(Symbol::new("v1")),
118                self.real.encode_field(),
119                self.imag.encode_field(),
120            ],
121        })
122    }
123}
124
125impl sim_citizen::Citizen for ComplexValue {
126    fn citizen_symbol() -> Symbol {
127        complex_value_class_symbol()
128    }
129
130    fn citizen_version() -> u32 {
131        1
132    }
133
134    fn citizen_arity() -> usize {
135        2
136    }
137
138    fn citizen_fields() -> &'static [&'static str] {
139        &["real", "imag"]
140    }
141}
142
143pub(crate) struct ComplexValueClass {
144    id: AtomicU32,
145}
146
147pub(crate) fn build_complex_value_class() -> Arc<ComplexValueClass> {
148    Arc::new(ComplexValueClass {
149        id: AtomicU32::new(0),
150    })
151}
152
153impl ComplexValueClass {
154    pub(crate) fn set_id(&self, id: ClassId) {
155        self.id.store(id.0, Ordering::Relaxed);
156    }
157}
158
159impl Object for ComplexValueClass {
160    fn display(&self, _cx: &mut Cx) -> Result<String> {
161        Ok(format!("#<class {}>", complex_value_class_symbol()))
162    }
163
164    fn as_any(&self) -> &dyn std::any::Any {
165        self
166    }
167}
168
169impl sim_kernel::ObjectCompat for ComplexValueClass {
170    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
171        if let Some(value) = cx
172            .registry()
173            .class_by_symbol(&Symbol::qualified("core", "Class"))
174        {
175            return Ok(value.clone());
176        }
177        DefaultFactory.class_stub(
178            sim_kernel::CORE_CLASS_CLASS_ID,
179            Symbol::qualified("core", "Class"),
180        )
181    }
182
183    fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
184        Ok(Expr::Symbol(complex_value_class_symbol()))
185    }
186
187    fn as_callable(&self) -> Option<&dyn Callable> {
188        Some(self)
189    }
190
191    fn as_class(&self) -> Option<&dyn Class> {
192        Some(self)
193    }
194
195    fn as_read_constructor(&self) -> Option<&dyn ReadConstructor> {
196        Some(self)
197    }
198}
199
200impl Callable for ComplexValueClass {
201    fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
202        let values = args.into_vec();
203        let [version, real, imag] = values.as_slice() else {
204            return Err(arity_error(complex_value_class_symbol(), 3, values.len()));
205        };
206        decode_version(cx, version.clone(), 1, complex_value_class_symbol())?;
207        let real = f64::decode_field_value(cx, real.clone(), "real")?;
208        let imag = f64::decode_field_value(cx, imag.clone(), "imag")?;
209        complex_value(cx, real, imag)
210    }
211}
212
213impl Class for ComplexValueClass {
214    fn id(&self) -> ClassId {
215        ClassId(self.id.load(Ordering::Relaxed))
216    }
217
218    fn symbol(&self) -> Symbol {
219        complex_value_class_symbol()
220    }
221
222    fn constructor_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
223        cx.factory().nil()
224    }
225
226    fn instance_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
227        Ok(cx
228            .registry()
229            .shape_by_symbol(&value_shape_symbol())
230            .cloned()
231            .unwrap_or(cx.factory().symbol(value_shape_symbol())?))
232    }
233
234    fn read_constructor(&self, cx: &mut Cx) -> Result<Option<ReadConstructorRef>> {
235        Ok(cx
236            .registry()
237            .class_by_symbol(&complex_value_class_symbol())
238            .cloned())
239    }
240
241    fn members(&self, cx: &mut Cx) -> Result<TableRef> {
242        cx.factory().table(vec![
243            (
244                Symbol::new("version"),
245                cx.factory()
246                    .number_literal(Symbol::qualified("citizen", "int"), "1".to_owned())?,
247            ),
248            (
249                Symbol::new("fields"),
250                cx.factory().list(vec![
251                    cx.factory().symbol(Symbol::new("real"))?,
252                    cx.factory().symbol(Symbol::new("imag"))?,
253                ])?,
254            ),
255        ])
256    }
257}
258
259impl ReadConstructor for ComplexValueClass {
260    fn symbol(&self) -> Symbol {
261        complex_value_class_symbol()
262    }
263
264    fn args_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
265        cx.factory().nil()
266    }
267
268    fn construct_read(&self, cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
269        if args.len() != 3 {
270            return Err(arity_error(complex_value_class_symbol(), 3, args.len()));
271        }
272        self.call(cx, Args::new(args))
273    }
274}
275
276/// Constructs an opaque `numbers/complex` value from finite real and imaginary
277/// parts, erroring when either part is non-finite.
278pub fn complex_value(cx: &mut Cx, real: f64, imag: f64) -> Result<Value> {
279    if !real.is_finite() || !imag.is_finite() {
280        return Err(Error::Eval(
281            "numbers/Complex constructor requires finite f64 parts".to_owned(),
282        ));
283    }
284    cx.factory().opaque(Arc::new(ComplexValue::new(real, imag)))
285}