1use bock_interp::{BockString, BuiltinRegistry, OrdF64, RuntimeError, TypeTag, Value};
4
5pub fn register(registry: &mut BuiltinRegistry) {
7 registry.register(TypeTag::Float, "add", float_add);
9 registry.register(TypeTag::Float, "sub", float_sub);
10 registry.register(TypeTag::Float, "mul", float_mul);
11 registry.register(TypeTag::Float, "div", float_div);
12 registry.register(TypeTag::Float, "rem", float_rem);
13 registry.register(TypeTag::Float, "pow", float_pow);
14 registry.register(TypeTag::Float, "negate", float_negate);
15
16 registry.register(TypeTag::Float, "compare", float_compare);
18
19 registry.register(TypeTag::Float, "hash_code", float_hash_code);
21
22 registry.register(TypeTag::Float, "display", float_display);
24
25 registry.register(TypeTag::Float, "abs", float_abs);
27 registry.register(TypeTag::Float, "floor", float_floor);
28 registry.register(TypeTag::Float, "ceil", float_ceil);
29 registry.register(TypeTag::Float, "round", float_round);
30 registry.register(TypeTag::Float, "to_int", float_to_int);
31 registry.register(TypeTag::Float, "sqrt", float_sqrt);
32 registry.register(TypeTag::Float, "is_nan", float_is_nan);
33 registry.register(TypeTag::Float, "is_infinite", float_is_infinite);
34 registry.register(TypeTag::Float, "min", float_min);
35 registry.register(TypeTag::Float, "max", float_max);
36 registry.register(TypeTag::Float, "clamp", float_clamp);
37}
38
39fn expect_float(args: &[Value], pos: usize, method: &str) -> Result<f64, RuntimeError> {
42 match args.get(pos) {
43 Some(Value::Float(v)) => Ok(v.0),
44 Some(other) => Err(RuntimeError::TypeError(format!(
45 "Float.{method} expects Float, got {other}"
46 ))),
47 None => Err(RuntimeError::ArityMismatch {
48 expected: pos + 1,
49 got: args.len(),
50 }),
51 }
52}
53
54fn float_add(args: &[Value]) -> Result<Value, RuntimeError> {
57 let a = expect_float(args, 0, "add")?;
58 let b = expect_float(args, 1, "add")?;
59 Ok(Value::Float(OrdF64(a + b)))
60}
61
62fn float_sub(args: &[Value]) -> Result<Value, RuntimeError> {
63 let a = expect_float(args, 0, "sub")?;
64 let b = expect_float(args, 1, "sub")?;
65 Ok(Value::Float(OrdF64(a - b)))
66}
67
68fn float_mul(args: &[Value]) -> Result<Value, RuntimeError> {
69 let a = expect_float(args, 0, "mul")?;
70 let b = expect_float(args, 1, "mul")?;
71 Ok(Value::Float(OrdF64(a * b)))
72}
73
74fn float_div(args: &[Value]) -> Result<Value, RuntimeError> {
75 let a = expect_float(args, 0, "div")?;
76 let b = expect_float(args, 1, "div")?;
77 Ok(Value::Float(OrdF64(a / b)))
78}
79
80fn float_rem(args: &[Value]) -> Result<Value, RuntimeError> {
81 let a = expect_float(args, 0, "rem")?;
82 let b = expect_float(args, 1, "rem")?;
83 Ok(Value::Float(OrdF64(a % b)))
84}
85
86fn float_pow(args: &[Value]) -> Result<Value, RuntimeError> {
87 let a = expect_float(args, 0, "pow")?;
88 let b = expect_float(args, 1, "pow")?;
89 Ok(Value::Float(OrdF64(a.powf(b))))
90}
91
92fn float_negate(args: &[Value]) -> Result<Value, RuntimeError> {
93 let a = expect_float(args, 0, "negate")?;
94 Ok(Value::Float(OrdF64(-a)))
95}
96
97fn float_compare(args: &[Value]) -> Result<Value, RuntimeError> {
103 let a = expect_float(args, 0, "compare")?;
104 let b = expect_float(args, 1, "compare")?;
105 Ok(Value::ordering(a.total_cmp(&b)))
106}
107
108fn float_hash_code(args: &[Value]) -> Result<Value, RuntimeError> {
111 use std::hash::{Hash, Hasher};
112 let a = expect_float(args, 0, "hash_code")?;
113 let mut hasher = std::collections::hash_map::DefaultHasher::new();
114 OrdF64(a).hash(&mut hasher);
115 Ok(Value::Int(hasher.finish() as i64))
116}
117
118fn float_display(args: &[Value]) -> Result<Value, RuntimeError> {
121 let a = expect_float(args, 0, "display")?;
122 Ok(Value::String(BockString::new(format!("{a}"))))
123}
124
125fn float_abs(args: &[Value]) -> Result<Value, RuntimeError> {
128 let a = expect_float(args, 0, "abs")?;
129 Ok(Value::Float(OrdF64(a.abs())))
130}
131
132fn float_floor(args: &[Value]) -> Result<Value, RuntimeError> {
133 let a = expect_float(args, 0, "floor")?;
134 Ok(Value::Float(OrdF64(a.floor())))
135}
136
137fn float_ceil(args: &[Value]) -> Result<Value, RuntimeError> {
138 let a = expect_float(args, 0, "ceil")?;
139 Ok(Value::Float(OrdF64(a.ceil())))
140}
141
142fn float_round(args: &[Value]) -> Result<Value, RuntimeError> {
143 let a = expect_float(args, 0, "round")?;
144 Ok(Value::Float(OrdF64(a.round())))
145}
146
147fn float_to_int(args: &[Value]) -> Result<Value, RuntimeError> {
148 let a = expect_float(args, 0, "to_int")?;
149 if a.is_nan() || a.is_infinite() {
150 return Err(RuntimeError::TypeError(
151 "cannot convert NaN or Infinity to Int".to_string(),
152 ));
153 }
154 Ok(Value::Int(a as i64))
155}
156
157fn float_sqrt(args: &[Value]) -> Result<Value, RuntimeError> {
158 let a = expect_float(args, 0, "sqrt")?;
159 Ok(Value::Float(OrdF64(a.sqrt())))
160}
161
162fn float_is_nan(args: &[Value]) -> Result<Value, RuntimeError> {
163 let a = expect_float(args, 0, "is_nan")?;
164 Ok(Value::Bool(a.is_nan()))
165}
166
167fn float_is_infinite(args: &[Value]) -> Result<Value, RuntimeError> {
168 let a = expect_float(args, 0, "is_infinite")?;
169 Ok(Value::Bool(a.is_infinite()))
170}
171
172fn float_min(args: &[Value]) -> Result<Value, RuntimeError> {
173 let a = expect_float(args, 0, "min")?;
174 let b = expect_float(args, 1, "min")?;
175 Ok(Value::Float(OrdF64(a.min(b))))
176}
177
178fn float_max(args: &[Value]) -> Result<Value, RuntimeError> {
179 let a = expect_float(args, 0, "max")?;
180 let b = expect_float(args, 1, "max")?;
181 Ok(Value::Float(OrdF64(a.max(b))))
182}
183
184fn float_clamp(args: &[Value]) -> Result<Value, RuntimeError> {
185 let a = expect_float(args, 0, "clamp")?;
186 let lo = expect_float(args, 1, "clamp")?;
187 let hi = expect_float(args, 2, "clamp")?;
188 Ok(Value::Float(OrdF64(a.clamp(lo, hi))))
189}
190
191#[cfg(test)]
194mod tests {
195 use super::*;
196
197 fn reg() -> BuiltinRegistry {
198 let mut r = BuiltinRegistry::new();
199 register(&mut r);
200 r
201 }
202
203 fn f(v: f64) -> Value {
204 Value::Float(OrdF64(v))
205 }
206
207 #[test]
208 fn add_ok() {
209 let r = reg();
210 let result = r.call(TypeTag::Float, "add", &[f(1.5), f(2.5)]);
211 assert_eq!(result.unwrap().unwrap(), f(4.0));
212 }
213
214 #[test]
215 fn sub_ok() {
216 let r = reg();
217 let result = r.call(TypeTag::Float, "sub", &[f(5.0), f(2.0)]);
218 assert_eq!(result.unwrap().unwrap(), f(3.0));
219 }
220
221 #[test]
222 fn mul_ok() {
223 let r = reg();
224 let result = r.call(TypeTag::Float, "mul", &[f(3.0), f(4.0)]);
225 assert_eq!(result.unwrap().unwrap(), f(12.0));
226 }
227
228 #[test]
229 fn div_ok() {
230 let r = reg();
231 let result = r.call(TypeTag::Float, "div", &[f(10.0), f(4.0)]);
232 assert_eq!(result.unwrap().unwrap(), f(2.5));
233 }
234
235 #[test]
236 fn pow_ok() {
237 let r = reg();
238 let result = r.call(TypeTag::Float, "pow", &[f(2.0), f(3.0)]);
239 assert_eq!(result.unwrap().unwrap(), f(8.0));
240 }
241
242 #[test]
243 fn negate_ok() {
244 let r = reg();
245 let result = r.call(TypeTag::Float, "negate", &[f(3.5)]);
246 assert_eq!(result.unwrap().unwrap(), f(-3.5));
247 }
248
249 #[test]
250 fn compare_less() {
251 let r = reg();
252 let result = r.call(TypeTag::Float, "compare", &[f(1.0), f(2.0)]);
253 assert_eq!(
254 result.unwrap().unwrap(),
255 Value::ordering(std::cmp::Ordering::Less)
256 );
257 }
258
259 #[test]
260 fn display_float() {
261 let r = reg();
262 let result = r.call(TypeTag::Float, "display", &[f(3.5)]);
263 assert_eq!(
264 result.unwrap().unwrap(),
265 Value::String(BockString::new("3.5"))
266 );
267 }
268
269 #[test]
270 fn abs_negative() {
271 let r = reg();
272 let result = r.call(TypeTag::Float, "abs", &[f(-42.5)]);
273 assert_eq!(result.unwrap().unwrap(), f(42.5));
274 }
275
276 #[test]
277 fn floor_ok() {
278 let r = reg();
279 let result = r.call(TypeTag::Float, "floor", &[f(3.7)]);
280 assert_eq!(result.unwrap().unwrap(), f(3.0));
281 }
282
283 #[test]
284 fn ceil_ok() {
285 let r = reg();
286 let result = r.call(TypeTag::Float, "ceil", &[f(3.2)]);
287 assert_eq!(result.unwrap().unwrap(), f(4.0));
288 }
289
290 #[test]
291 fn round_ok() {
292 let r = reg();
293 let result = r.call(TypeTag::Float, "round", &[f(3.5)]);
294 assert_eq!(result.unwrap().unwrap(), f(4.0));
295 }
296
297 #[test]
298 fn to_int_ok() {
299 let r = reg();
300 let result = r.call(TypeTag::Float, "to_int", &[f(42.9)]);
301 assert_eq!(result.unwrap().unwrap(), Value::Int(42));
302 }
303
304 #[test]
305 fn to_int_nan_error() {
306 let r = reg();
307 let result = r.call(TypeTag::Float, "to_int", &[f(f64::NAN)]);
308 assert!(result.unwrap().is_err());
309 }
310
311 #[test]
312 fn sqrt_ok() {
313 let r = reg();
314 let result = r.call(TypeTag::Float, "sqrt", &[f(9.0)]);
315 assert_eq!(result.unwrap().unwrap(), f(3.0));
316 }
317
318 #[test]
319 fn is_nan_true() {
320 let r = reg();
321 let result = r.call(TypeTag::Float, "is_nan", &[f(f64::NAN)]);
322 assert_eq!(result.unwrap().unwrap(), Value::Bool(true));
323 }
324
325 #[test]
326 fn is_infinite_true() {
327 let r = reg();
328 let result = r.call(TypeTag::Float, "is_infinite", &[f(f64::INFINITY)]);
329 assert_eq!(result.unwrap().unwrap(), Value::Bool(true));
330 }
331
332 #[test]
333 fn min_ok() {
334 let r = reg();
335 let result = r.call(TypeTag::Float, "min", &[f(3.0), f(7.0)]);
336 assert_eq!(result.unwrap().unwrap(), f(3.0));
337 }
338
339 #[test]
340 fn max_ok() {
341 let r = reg();
342 let result = r.call(TypeTag::Float, "max", &[f(3.0), f(7.0)]);
343 assert_eq!(result.unwrap().unwrap(), f(7.0));
344 }
345
346 #[test]
347 fn clamp_ok() {
348 let r = reg();
349 let result = r.call(TypeTag::Float, "clamp", &[f(15.0), f(0.0), f(10.0)]);
350 assert_eq!(result.unwrap().unwrap(), f(10.0));
351 }
352
353 #[test]
354 fn hash_code_deterministic() {
355 let r = reg();
356 let h1 = r
357 .call(TypeTag::Float, "hash_code", &[f(3.5)])
358 .unwrap()
359 .unwrap();
360 let h2 = r
361 .call(TypeTag::Float, "hash_code", &[f(3.5)])
362 .unwrap()
363 .unwrap();
364 assert_eq!(h1, h2);
365 }
366}