1use tan::{
10 context::Context,
11 error::Error,
12 expr::{annotate_type, has_type_annotation, Expr},
13 util::{expect_lock_read, module_util::require_module},
14};
15
16fn try_complex(expr: &Expr) -> Option<(f64, f64)> {
19 if !has_type_annotation(expr, "Complex") {
20 return None;
21 }
22
23 let Expr::Array(v) = expr.unpack() else {
24 return None;
25 };
26
27 let v = expect_lock_read(v);
28
29 let re = v[0].as_float().unwrap();
31 let im = v[1].as_float().unwrap();
32
33 Some((re, im))
34}
35
36#[inline(always)]
37fn make_complex(re: impl Into<Expr>, im: impl Into<Expr>) -> Expr {
38 let expr = Expr::array(vec![re.into(), im.into()]);
39 annotate_type(expr, "Complex")
40}
41
42pub fn complex_new(args: &[Expr]) -> Result<Expr, Error> {
46 let re = args.first().unwrap_or_else(|| &Expr::Float(0.0));
49 let im = args.get(1).unwrap_or_else(|| &Expr::Float(0.0));
50
51 Ok(make_complex(re.clone(), im.clone()))
53}
54
55pub fn complex_add(args: &[Expr]) -> Result<Expr, Error> {
56 let mut re_acc = 0.0;
59 let mut im_acc = 0.0;
60
61 for arg in args {
62 let Some((re, im)) = try_complex(arg) else {
63 return Err(Error::invalid_arguments(
64 &format!("{arg} is not a Complex"),
65 arg.range(),
66 ));
67 };
68 re_acc += re;
69 im_acc += im;
70 }
71
72 Ok(make_complex(re_acc, im_acc))
73}
74
75pub fn complex_mul(args: &[Expr]) -> Result<Expr, Error> {
79 let [c, z] = args else {
80 return Err(Error::invalid_arguments("requires two arguments", None));
81 };
82
83 let Some((a, b)) = try_complex(c) else {
84 return Err(Error::invalid_arguments(
85 &format!("{c} is not a Complex"),
86 c.range(),
87 ));
88 };
89
90 let Some((c, d)) = try_complex(z) else {
91 return Err(Error::invalid_arguments(
92 &format!("{z} is not a Complex"),
93 c.range(),
94 ));
95 };
96
97 let re = (a * c) - (b * d);
100 let im = (a * d) + (b * c);
101
102 Ok(make_complex(re, im))
103}
104
105pub fn complex_abs(args: &[Expr]) -> Result<Expr, Error> {
107 let [c] = args else {
108 return Err(Error::invalid_arguments("requires `self` argument", None));
109 };
110
111 let Some((a, b)) = try_complex(c) else {
112 return Err(Error::invalid_arguments(
113 &format!("{c} is not a Complex"),
114 c.range(),
115 ));
116 };
117
118 let magnitude = ((a * a) + (b * b)).sqrt();
124
125 Ok(magnitude.into())
126}
127
128pub fn setup_lib_math_complex(context: &mut Context) {
129 let module = require_module("math/complex", context);
131
132 module.insert_invocable("Complex", Expr::foreign_func(&complex_new));
136
137 module.insert_invocable("+$$Complex$$Complex", Expr::foreign_func(&complex_add));
138 module.insert_invocable(
139 "+$$Complex$$Complex$$Complex",
140 Expr::foreign_func(&complex_add),
141 );
142
143 module.insert_invocable("*$$Complex$$Complex", Expr::foreign_func(&complex_mul));
144
145 module.insert_invocable("abs", Expr::foreign_func(&complex_abs));
147 module.insert_invocable("abs$$Complex", Expr::foreign_func(&complex_abs));
148
149 }