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