|
| 1 | +(* TEST |
| 2 | + flags = "-extension layouts_alpha" |
| 3 | +*) |
| 4 | + |
| 5 | +module Float_u = Stdlib__Float_u |
| 6 | + |
| 7 | +(* Constant seed for repeatable random-testing properties *) |
| 8 | +let () = Random.init 1234 |
| 9 | + |
| 10 | +type 'a result = { |
| 11 | + actual : 'a; |
| 12 | + expected : 'a; |
| 13 | + equal : 'a -> 'a -> bool; |
| 14 | + to_string : 'a -> string |
| 15 | +} |
| 16 | + |
| 17 | +let float_result ~actual ~expected = { |
| 18 | + actual; |
| 19 | + expected; |
| 20 | + equal = Float.equal; |
| 21 | + to_string = Float.to_string; |
| 22 | +} |
| 23 | + |
| 24 | +let bool_result ~actual ~expected = { |
| 25 | + actual; |
| 26 | + expected; |
| 27 | + equal = Bool.equal; |
| 28 | + to_string = Bool.to_string; |
| 29 | +} |
| 30 | + |
| 31 | +let int_result ~actual ~expected = { |
| 32 | + actual; |
| 33 | + expected; |
| 34 | + equal = Int.equal; |
| 35 | + to_string = Int.to_string; |
| 36 | +} |
| 37 | + |
| 38 | +let string_result ~actual ~expected = { |
| 39 | + actual; |
| 40 | + expected; |
| 41 | + equal = String.equal; |
| 42 | + to_string = fun x -> x; |
| 43 | +} |
| 44 | + |
| 45 | +let fpclass_to_string = function |
| 46 | + FP_normal -> "FP_normal" |
| 47 | + | FP_subnormal -> "FP_subnormal" |
| 48 | + | FP_zero -> "FP_zero" |
| 49 | + | FP_infinite -> "FP_infinite" |
| 50 | + | FP_nan -> "FP_nan" |
| 51 | + |
| 52 | +let fpclass_result ~actual ~expected = { |
| 53 | + actual; |
| 54 | + expected; |
| 55 | + equal = (=); |
| 56 | + to_string = fpclass_to_string; |
| 57 | +} |
| 58 | + |
| 59 | +let interesting_floats = |
| 60 | + [ 0.; 1.; -1.; Float.max_float; Float.min_float; Float.epsilon; |
| 61 | + Float.nan; Float.infinity; Float.neg_infinity ] |
| 62 | + |
| 63 | +let interesting_ints = [ 0; 1; -1; Int.max_int; Int.min_int ] |
| 64 | + |
| 65 | +let default_min = -10000. |
| 66 | +let default_max = 10000. |
| 67 | + |
| 68 | +let floats_in_range ~num min max = |
| 69 | + (* Generating well-distributed random floats in a range is obviously hard. |
| 70 | + The "in a range" part is important because many float functions are only |
| 71 | + defined on certain ranges. I'm not trying very hard, here - this should |
| 72 | + only be used with min and max that aren't at the outer limits of the float |
| 73 | + range. *) |
| 74 | + let float_in_range () = |
| 75 | + let f = Random.float 1000. in |
| 76 | + let f = f *. ((max -. min) /. 1000.) in |
| 77 | + f +. min |
| 78 | + in |
| 79 | + List.init num (fun _ -> float_in_range ()) |
| 80 | + |
| 81 | +let float_inputs ~range ~num = |
| 82 | + let min, max = |
| 83 | + match range with |
| 84 | + | None -> default_min, default_max |
| 85 | + | Some (min, max) -> min, max |
| 86 | + in |
| 87 | + let input = floats_in_range ~num min max in |
| 88 | + let input = |
| 89 | + if Option.is_none range then interesting_floats @ input else input |
| 90 | + in |
| 91 | + input |
| 92 | + |
| 93 | +let string_inputs ~num = |
| 94 | + List.map Float.to_string (float_inputs ~range:None ~num) |
| 95 | + |
| 96 | +let int_inputs ~num = |
| 97 | + let gen_int _ = (Random.full_int Int.max_int) - (Int.max_int / 2) in |
| 98 | + interesting_ints @ List.init num gen_int |
| 99 | + |
| 100 | +let passed { actual; expected; equal; _ } = equal actual expected |
| 101 | + |
| 102 | +let test inputs input_to_string name prop = |
| 103 | + let test x = |
| 104 | + let {expected; actual; to_string} as result = prop x in |
| 105 | + if not (passed result) |
| 106 | + then |
| 107 | + Printf.printf "Test failed: %s. Input = %s; expected = %s; actual = %s\n" |
| 108 | + name (input_to_string x) (to_string expected) (to_string actual) |
| 109 | + in |
| 110 | + List.iter test inputs |
| 111 | + |
| 112 | +(* zips that truncate *) |
| 113 | +let rec zip l1 l2 = |
| 114 | + match l1, l2 with |
| 115 | + | x1 :: l1, x2 :: l2 -> (x1, x2) :: zip l1 l2 |
| 116 | + | _ -> [] |
| 117 | + |
| 118 | +let rec zip3 l1 l2 l3 = |
| 119 | + match l1, l2, l3 with |
| 120 | + | x1 :: l1, x2 :: l2, x3 :: l3 -> (x1, x2, x3) :: zip3 l1 l2 l3 |
| 121 | + | _ -> [] |
| 122 | + |
| 123 | +(* These run a property on inputs and check the result *) |
| 124 | +let test_unary ?range name prop = |
| 125 | + let inputs = float_inputs ~range ~num:10 in |
| 126 | + let input_to_string = Float.to_string in |
| 127 | + test inputs input_to_string name prop |
| 128 | + |
| 129 | +let test_unary_int ?range name prop = |
| 130 | + let inputs = int_inputs ~num:10 in |
| 131 | + let input_to_string = Int.to_string in |
| 132 | + test inputs input_to_string name prop |
| 133 | + |
| 134 | +let test_unary_string ?range name prop = |
| 135 | + let inputs = string_inputs ~num:10 in |
| 136 | + let input_to_string x = x in |
| 137 | + test inputs input_to_string name prop |
| 138 | + |
| 139 | +let test_binary ?range name prop = |
| 140 | + let input1 = float_inputs ~range ~num:20 in |
| 141 | + let input2 = List.rev (float_inputs ~range ~num:20) in |
| 142 | + let inputs = zip input1 (List.rev input2) in |
| 143 | + let input_to_string (f1,f2) = Printf.sprintf "(%f, %f)" f1 f2 in |
| 144 | + test inputs input_to_string name prop |
| 145 | + |
| 146 | +let test_binary_float_int ?range name prop = |
| 147 | + let input1 = float_inputs ~range ~num:20 in |
| 148 | + let input2 = List.rev (int_inputs ~num:20) in |
| 149 | + let inputs = zip input1 (List.rev input2) in |
| 150 | + let input_to_string (f1,f2) = Printf.sprintf "(%f, %d)" f1 f2 in |
| 151 | + test inputs input_to_string name prop |
| 152 | + |
| 153 | +let test_ternary ?range name prop = |
| 154 | + let input1 = float_inputs ~range ~num:30 in |
| 155 | + let input2 = float_inputs ~range ~num:30 in |
| 156 | + let input3 = List.rev (float_inputs ~range ~num:30) in |
| 157 | + let inputs = zip3 input1 input2 input3 in |
| 158 | + let input_to_string (f1, f2, f3) = Printf.sprintf "(%f, %f, %f)" f1 f2 f3 in |
| 159 | + test inputs input_to_string name prop |
| 160 | + |
| 161 | +(* These make the property to be tested for various arities and types *) |
| 162 | +let mk1 expected_f actual_f arg = |
| 163 | + let expected = expected_f arg in |
| 164 | + let actual = Float_u.to_float (actual_f (Float_u.of_float arg)) in |
| 165 | + float_result ~actual ~expected |
| 166 | + |
| 167 | +let mk2 expected_f actual_f (arg1, arg2) = |
| 168 | + let expected = expected_f arg1 arg2 in |
| 169 | + let actual = |
| 170 | + Float_u.to_float |
| 171 | + (actual_f (Float_u.of_float arg1) (Float_u.of_float arg2)) |
| 172 | + in |
| 173 | + float_result ~actual ~expected |
| 174 | + |
| 175 | +let mk3 expected_f actual_f (arg1, arg2, arg3) = |
| 176 | + let expected = expected_f arg1 arg2 arg3 in |
| 177 | + let actual = |
| 178 | + Float_u.to_float |
| 179 | + (actual_f (Float_u.of_float arg1) (Float_u.of_float arg2) |
| 180 | + (Float_u.of_float arg3)) |
| 181 | + in |
| 182 | + float_result ~actual ~expected |
| 183 | + |
| 184 | +let mk_float_X result expected_f actual_f arg = |
| 185 | + let expected = expected_f arg in |
| 186 | + let actual = actual_f (Float_u.of_float arg) in |
| 187 | + result ~actual ~expected |
| 188 | + |
| 189 | +let mk_X_float expected_f actual_f arg = |
| 190 | + let expected = expected_f arg in |
| 191 | + let actual = Float_u.to_float (actual_f arg) in |
| 192 | + float_result ~actual ~expected |
| 193 | + |
| 194 | +let mk_float_X_float expected_f actual_f (arg1, arg2) = |
| 195 | + let expected = expected_f arg1 arg2 in |
| 196 | + let actual = Float_u.to_float (actual_f (Float_u.of_float arg1) arg2) in |
| 197 | + float_result ~actual ~expected |
| 198 | + |
| 199 | +let mk_float_float_X result expected_f actual_f (arg1, arg2) = |
| 200 | + let expected = expected_f arg1 arg2 in |
| 201 | + let actual = actual_f (Float_u.of_float arg1) (Float_u.of_float arg2) in |
| 202 | + result ~actual ~expected |
| 203 | + |
| 204 | +let () = |
| 205 | + test_unary "neg" (mk1 Float.neg Float_u.neg); |
| 206 | + test_binary "add" (mk2 Float.add Float_u.add); |
| 207 | + test_binary "sub" (mk2 Float.sub Float_u.sub); |
| 208 | + test_binary "mul" (mk2 Float.mul Float_u.mul); |
| 209 | + test_binary "div" (mk2 Float.div Float_u.div); |
| 210 | + test_ternary "fma" (mk3 Float.fma Float_u.fma); |
| 211 | + test_binary "rem" (mk2 Float.rem Float_u.rem); |
| 212 | + test_unary "succ" (mk1 Float.succ Float_u.succ); |
| 213 | + test_unary "pred" (mk1 Float.pred Float_u.pred); |
| 214 | + test_unary "abs" (mk1 Float.abs Float_u.abs); |
| 215 | + test_unary "is_finite" |
| 216 | + (mk_float_X bool_result Float.is_finite Float_u.is_finite); |
| 217 | + test_unary "is_nan" (mk_float_X bool_result Float.is_nan Float_u.is_nan); |
| 218 | + test_unary "is_integer" |
| 219 | + (mk_float_X bool_result Float.is_integer Float_u.is_integer); |
| 220 | + test_unary_int "of_int" (mk_X_float Float.of_int Float_u.of_int); |
| 221 | + test_unary "to_int" (mk_float_X int_result Float.to_int Float_u.to_int); |
| 222 | + test_unary_string "of_string" (mk_X_float Float.of_string Float_u.of_string); |
| 223 | + test_unary "to_string" |
| 224 | + (mk_float_X string_result Float.to_string Float_u.to_string); |
| 225 | + test_unary "classify_float" |
| 226 | + (mk_float_X fpclass_result Float.classify_float Float_u.classify_float); |
| 227 | + test_binary "pow" (mk2 Float.pow Float_u.pow); |
| 228 | + test_unary "sqt" (mk1 Float.sqrt Float_u.sqrt); |
| 229 | + test_unary "cbrt" (mk1 Float.cbrt Float_u.cbrt); |
| 230 | + test_unary "exp" (mk1 Float.exp Float_u.exp); |
| 231 | + test_unary "exp2" (mk1 Float.exp2 Float_u.exp2); |
| 232 | + test_unary "log" (mk1 Float.log Float_u.log); |
| 233 | + test_unary "log10" (mk1 Float.log10 Float_u.log10); |
| 234 | + test_unary "log2" (mk1 Float.log2 Float_u.log2); |
| 235 | + test_unary "log1p" (mk1 Float.log1p Float_u.log1p); |
| 236 | + test_unary "cos" (mk1 Float.cos Float_u.cos); |
| 237 | + test_unary "sin" (mk1 Float.sin Float_u.sin); |
| 238 | + test_unary "tan" (mk1 Float.tan Float_u.tan); |
| 239 | + test_unary "acos" ~range:(-1.0, 1.0) (mk1 Float.acos Float_u.acos); |
| 240 | + test_unary "asin" ~range:(-1.0, 1.0) (mk1 Float.asin Float_u.asin); |
| 241 | + test_unary "atan" (mk1 Float.atan Float_u.atan); |
| 242 | + test_binary "atan2" (mk2 Float.atan2 Float_u.atan2); |
| 243 | + test_binary "hypot" (mk2 Float.hypot Float_u.hypot); |
| 244 | + test_unary "cosh" (mk1 Float.cosh Float_u.cosh); |
| 245 | + test_unary "sinh" (mk1 Float.sinh Float_u.sinh); |
| 246 | + test_unary "tanh" (mk1 Float.tanh Float_u.tanh); |
| 247 | + test_unary "acosh" ~range:(1.0, Float.infinity) |
| 248 | + (mk1 Float.acosh Float_u.acosh); |
| 249 | + test_unary "asinh" (mk1 Float.asinh Float_u.asinh); |
| 250 | + test_unary "atanh" ~range:(-1.0, 1.0) (mk1 Float.atanh Float_u.atanh); |
| 251 | + test_unary "erf" (mk1 Float.erf Float_u.erf); |
| 252 | + test_unary "erfc" (mk1 Float.erfc Float_u.erfc); |
| 253 | + test_unary "trunk" (mk1 Float.trunc Float_u.trunc); |
| 254 | + test_unary "round" (mk1 Float.round Float_u.round); |
| 255 | + test_unary "ceil" (mk1 Float.ceil Float_u.ceil); |
| 256 | + test_unary "floor" (mk1 Float.floor Float_u.floor); |
| 257 | + test_binary "next_after" (mk2 Float.next_after Float_u.next_after); |
| 258 | + test_binary "copy_sign" (mk2 Float.copy_sign Float_u.copy_sign); |
| 259 | + test_unary "sign_bit" |
| 260 | + (mk_float_X bool_result Float.sign_bit Float_u.sign_bit); |
| 261 | + test_binary_float_int "ldexp" (mk_float_X_float Float.ldexp Float_u.ldexp); |
| 262 | + test_binary "compare" |
| 263 | + (mk_float_float_X int_result Float.compare Float_u.compare); |
| 264 | + test_binary "equal" |
| 265 | + (mk_float_float_X bool_result Float.equal Float_u.equal); |
| 266 | + test_binary "min" (mk2 Float.min Float_u.min); |
| 267 | + test_binary "max" (mk2 Float.max Float_u.max); |
| 268 | + test_binary "min_num" (mk2 Float.min_num Float_u.min_num); |
| 269 | + test_binary "max_num" (mk2 Float.max_num Float_u.max_num); |
0 commit comments