From cee9ae7f222ff2d7b79a7fc041e4e9aecf735416 Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 24 Apr 2026 10:23:04 +0000 Subject: [PATCH] sx primitives: add trig/transcendental/bit-op math helpers Adds sin/cos/tan + inverse + hyperbolic + inverse-hyperbolic, log/log2/log10/log1p, exp/expm1, cbrt, hypot (variadic), sign, fround/clz32/imul. All one-liners over Float.* / Int32.*. Needed by JS-on-SX to unblock built-ins/Math tests. Co-Authored-By: Claude Opus 4.7 (1M context) --- hosts/ocaml/lib/sx_primitives.ml | 84 ++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/hosts/ocaml/lib/sx_primitives.ml b/hosts/ocaml/lib/sx_primitives.ml index bd6f76c2..05cd522a 100644 --- a/hosts/ocaml/lib/sx_primitives.ml +++ b/hosts/ocaml/lib/sx_primitives.ml @@ -144,6 +144,90 @@ let () = register "pow" (fun args -> match args with [a; b] -> Number (as_number a ** as_number b) | _ -> raise (Eval_error "pow: 2 args")); + register "cbrt" (fun args -> + match args with [a] -> Number (Float.cbrt (as_number a)) | _ -> raise (Eval_error "cbrt: 1 arg")); + register "exp" (fun args -> + match args with [a] -> Number (Float.exp (as_number a)) | _ -> raise (Eval_error "exp: 1 arg")); + register "expm1" (fun args -> + match args with [a] -> Number (Float.expm1 (as_number a)) | _ -> raise (Eval_error "expm1: 1 arg")); + register "log" (fun args -> + match args with [a] -> Number (Float.log (as_number a)) | _ -> raise (Eval_error "log: 1 arg")); + register "log2" (fun args -> + match args with [a] -> Number (Float.log (as_number a) /. Float.log 2.0) | _ -> raise (Eval_error "log2: 1 arg")); + register "log10" (fun args -> + match args with [a] -> Number (Float.log10 (as_number a)) | _ -> raise (Eval_error "log10: 1 arg")); + register "log1p" (fun args -> + match args with [a] -> Number (Float.log1p (as_number a)) | _ -> raise (Eval_error "log1p: 1 arg")); + register "sin" (fun args -> + match args with [a] -> Number (Float.sin (as_number a)) | _ -> raise (Eval_error "sin: 1 arg")); + register "cos" (fun args -> + match args with [a] -> Number (Float.cos (as_number a)) | _ -> raise (Eval_error "cos: 1 arg")); + register "tan" (fun args -> + match args with [a] -> Number (Float.tan (as_number a)) | _ -> raise (Eval_error "tan: 1 arg")); + register "asin" (fun args -> + match args with [a] -> Number (Float.asin (as_number a)) | _ -> raise (Eval_error "asin: 1 arg")); + register "acos" (fun args -> + match args with [a] -> Number (Float.acos (as_number a)) | _ -> raise (Eval_error "acos: 1 arg")); + register "atan" (fun args -> + match args with [a] -> Number (Float.atan (as_number a)) | _ -> raise (Eval_error "atan: 1 arg")); + register "atan2" (fun args -> + match args with [a; b] -> Number (Float.atan2 (as_number a) (as_number b)) + | _ -> raise (Eval_error "atan2: 2 args")); + register "sinh" (fun args -> + match args with [a] -> Number (Float.sinh (as_number a)) | _ -> raise (Eval_error "sinh: 1 arg")); + register "cosh" (fun args -> + match args with [a] -> Number (Float.cosh (as_number a)) | _ -> raise (Eval_error "cosh: 1 arg")); + register "tanh" (fun args -> + match args with [a] -> Number (Float.tanh (as_number a)) | _ -> raise (Eval_error "tanh: 1 arg")); + register "asinh" (fun args -> + match args with [a] -> Number (Float.asinh (as_number a)) | _ -> raise (Eval_error "asinh: 1 arg")); + register "acosh" (fun args -> + match args with [a] -> Number (Float.acosh (as_number a)) | _ -> raise (Eval_error "acosh: 1 arg")); + register "atanh" (fun args -> + match args with [a] -> Number (Float.atanh (as_number a)) | _ -> raise (Eval_error "atanh: 1 arg")); + register "hypot" (fun args -> + let square x = x *. x in + let sum = List.fold_left (fun acc a -> acc +. square (as_number a)) 0.0 args in + Number (Float.sqrt sum)); + register "sign" (fun args -> + match args with + | [a] -> + let n = as_number a in + Number (if Float.is_nan n then Float.nan + else if n > 0.0 then 1.0 + else if n < 0.0 then -1.0 + else n) + | _ -> raise (Eval_error "sign: 1 arg")); + register "fround" (fun args -> + match args with [a] -> Number (Int32.float_of_bits (Int32.bits_of_float (as_number a))) + | _ -> raise (Eval_error "fround: 1 arg")); + register "clz32" (fun args -> + match args with + | [a] -> + let n = as_number a in + let i = if Float.is_nan n || Float.is_infinite n then 0l + else Int32.of_float (Float.rem n 4294967296.0) in + if i = 0l then Number 32.0 + else + let high_bit = Int32.shift_left 1l 31 in + let count = ref 0 in + let x = ref i in + while Int32.logand !x high_bit = 0l do + incr count; + x := Int32.shift_left !x 1 + done; + Number (float_of_int !count) + | _ -> raise (Eval_error "clz32: 1 arg")); + register "imul" (fun args -> + match args with + | [a; b] -> + let tou32 f = + if Float.is_nan f || Float.is_infinite f then 0l + else Int32.of_float (Float.rem f 4294967296.0) in + let ai = tou32 (as_number a) and bi = tou32 (as_number b) in + let r = Int32.mul ai bi in + Number (Int32.to_float r) + | _ -> raise (Eval_error "imul: 2 args")); register "clamp" (fun args -> match args with | [x; lo; hi] ->