|
| 1 | +//! Helper CLI utility for common tasks. |
| 2 | +
|
| 3 | +#![cfg_attr(f16_enabled, feature(f16))] |
| 4 | +#![cfg_attr(f128_enabled, feature(f128))] |
| 5 | + |
| 6 | +use std::any::type_name; |
| 7 | +use std::env; |
| 8 | +use std::str::FromStr; |
| 9 | + |
| 10 | +#[cfg(feature = "build-mpfr")] |
| 11 | +use az::Az; |
| 12 | +#[cfg(feature = "build-mpfr")] |
| 13 | +use libm_test::mpfloat::MpOp; |
| 14 | +use libm_test::{MathOp, TupleCall}; |
| 15 | + |
| 16 | +const USAGE: &str = "\ |
| 17 | +usage: |
| 18 | +
|
| 19 | +cargo run -p util -- <SUBCOMMAND> |
| 20 | +
|
| 21 | +SUBCOMMAND: |
| 22 | + eval <BASIS> <OP> inputs... |
| 23 | + Evaulate the expression with a given basis. This can be useful for |
| 24 | + running routines with a debugger, or quickly checking input. Examples: |
| 25 | + * eval musl sinf 1.234 # print the results of musl sinf(1.234f32) |
| 26 | + * eval mpfr pow 1.234 2.432 # print the results of mpfr pow(1.234, 2.432) |
| 27 | +"; |
| 28 | + |
| 29 | +fn main() { |
| 30 | + let args = env::args().collect::<Vec<_>>(); |
| 31 | + let str_args = args.iter().map(|s| s.as_str()).collect::<Vec<_>>(); |
| 32 | + |
| 33 | + match &str_args.as_slice()[1..] { |
| 34 | + ["eval", basis, op, inputs @ ..] => do_eval(basis, op, inputs), |
| 35 | + _ => { |
| 36 | + println!("{USAGE}\nunrecognized input `{str_args:?}`"); |
| 37 | + std::process::exit(1); |
| 38 | + } |
| 39 | + } |
| 40 | +} |
| 41 | + |
| 42 | +macro_rules! handle_call { |
| 43 | + ( |
| 44 | + fn_name: $fn_name:ident, |
| 45 | + CFn: $CFn:ty, |
| 46 | + RustFn: $RustFn:ty, |
| 47 | + RustArgs: $RustArgs:ty, |
| 48 | + attrs: [$($attr:meta),*], |
| 49 | + extra: ($basis:ident, $op:ident, $inputs:ident), |
| 50 | + fn_extra: $musl_fn:expr, |
| 51 | + ) => { |
| 52 | + $(#[$attr])* |
| 53 | + if $op == stringify!($fn_name) { |
| 54 | + type Op = libm_test::op::$fn_name::Routine; |
| 55 | + |
| 56 | + let input = <$RustArgs>::parse($inputs); |
| 57 | + let libm_fn: <Op as MathOp>::RustFn = libm::$fn_name; |
| 58 | + |
| 59 | + let output = match $basis { |
| 60 | + "libm" => input.call(libm_fn), |
| 61 | + #[cfg(feature = "build-musl")] |
| 62 | + "musl" => { |
| 63 | + let musl_fn: <Op as MathOp>::CFn = |
| 64 | + $musl_fn.unwrap_or_else(|| panic!("no musl function for {}", $op)); |
| 65 | + input.call(musl_fn) |
| 66 | + } |
| 67 | + #[cfg(feature = "build-mpfr")] |
| 68 | + "mpfr" => { |
| 69 | + let mut mp = <Op as MpOp>::new_mp(); |
| 70 | + Op::run(&mut mp, input) |
| 71 | + } |
| 72 | + _ => panic!("unrecognized or disabled basis '{}'", $basis), |
| 73 | + }; |
| 74 | + println!("{output:?}"); |
| 75 | + return; |
| 76 | + } |
| 77 | + }; |
| 78 | +} |
| 79 | + |
| 80 | +/// Evaluate the specified operation with a given basis. |
| 81 | +fn do_eval(basis: &str, op: &str, inputs: &[&str]) { |
| 82 | + libm_macros::for_each_function! { |
| 83 | + callback: handle_call, |
| 84 | + emit_types: [CFn, RustFn, RustArgs], |
| 85 | + extra: (basis, op, inputs), |
| 86 | + fn_extra: match MACRO_FN_NAME { |
| 87 | + copysignf16 | copysignf128 | fabsf16 | fabsf128 => None, |
| 88 | + _ => Some(musl_math_sys::MACRO_FN_NAME) |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + panic!("no operation matching {op}"); |
| 93 | +} |
| 94 | + |
| 95 | +/// Parse a tuple from a space-delimited string. |
| 96 | +trait ParseTuple { |
| 97 | + fn parse(input: &[&str]) -> Self; |
| 98 | +} |
| 99 | + |
| 100 | +macro_rules! impl_parse_tuple { |
| 101 | + ($ty:ty) => { |
| 102 | + impl ParseTuple for ($ty,) { |
| 103 | + fn parse(input: &[&str]) -> Self { |
| 104 | + assert_eq!(input.len(), 1, "expected a single argument, got {input:?}"); |
| 105 | + (parse(input, 0),) |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + impl ParseTuple for ($ty, $ty) { |
| 110 | + fn parse(input: &[&str]) -> Self { |
| 111 | + assert_eq!(input.len(), 2, "expected two arguments, got {input:?}"); |
| 112 | + (parse(input, 0), parse(input, 1)) |
| 113 | + } |
| 114 | + } |
| 115 | + |
| 116 | + impl ParseTuple for ($ty, i32) { |
| 117 | + fn parse(input: &[&str]) -> Self { |
| 118 | + assert_eq!(input.len(), 2, "expected two arguments, got {input:?}"); |
| 119 | + (parse(input, 0), parse(input, 1)) |
| 120 | + } |
| 121 | + } |
| 122 | + |
| 123 | + impl ParseTuple for (i32, $ty) { |
| 124 | + fn parse(input: &[&str]) -> Self { |
| 125 | + assert_eq!(input.len(), 2, "expected two arguments, got {input:?}"); |
| 126 | + (parse(input, 0), parse(input, 1)) |
| 127 | + } |
| 128 | + } |
| 129 | + |
| 130 | + impl ParseTuple for ($ty, $ty, $ty) { |
| 131 | + fn parse(input: &[&str]) -> Self { |
| 132 | + assert_eq!(input.len(), 2, "expected three arguments, got {input:?}"); |
| 133 | + (parse(input, 0), parse(input, 1), parse(input, 3)) |
| 134 | + } |
| 135 | + } |
| 136 | + }; |
| 137 | +} |
| 138 | + |
| 139 | +#[allow(unused_macros)] |
| 140 | +#[cfg(feature = "build-mpfr")] |
| 141 | +macro_rules! impl_parse_tuple_via_rug { |
| 142 | + ($ty:ty) => { |
| 143 | + impl ParseTuple for ($ty,) { |
| 144 | + fn parse(input: &[&str]) -> Self { |
| 145 | + assert_eq!(input.len(), 1, "expected a single argument, got {input:?}"); |
| 146 | + (parse_rug(input, 0),) |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + impl ParseTuple for ($ty, $ty) { |
| 151 | + fn parse(input: &[&str]) -> Self { |
| 152 | + assert_eq!(input.len(), 2, "expected two arguments, got {input:?}"); |
| 153 | + (parse_rug(input, 0), parse_rug(input, 1)) |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + impl ParseTuple for ($ty, i32) { |
| 158 | + fn parse(input: &[&str]) -> Self { |
| 159 | + assert_eq!(input.len(), 2, "expected two arguments, got {input:?}"); |
| 160 | + (parse_rug(input, 0), parse(input, 1)) |
| 161 | + } |
| 162 | + } |
| 163 | + |
| 164 | + impl ParseTuple for (i32, $ty) { |
| 165 | + fn parse(input: &[&str]) -> Self { |
| 166 | + assert_eq!(input.len(), 2, "expected two arguments, got {input:?}"); |
| 167 | + (parse(input, 0), parse_rug(input, 1)) |
| 168 | + } |
| 169 | + } |
| 170 | + |
| 171 | + impl ParseTuple for ($ty, $ty, $ty) { |
| 172 | + fn parse(input: &[&str]) -> Self { |
| 173 | + assert_eq!(input.len(), 2, "expected three arguments, got {input:?}"); |
| 174 | + (parse_rug(input, 0), parse_rug(input, 1), parse_rug(input, 3)) |
| 175 | + } |
| 176 | + } |
| 177 | + }; |
| 178 | +} |
| 179 | + |
| 180 | +// Fallback for when Rug is not built. |
| 181 | +#[allow(unused_macros)] |
| 182 | +#[cfg(not(feature = "build-mpfr"))] |
| 183 | +macro_rules! impl_parse_tuple_via_rug { |
| 184 | + ($ty:ty) => { |
| 185 | + impl ParseTuple for ($ty,) { |
| 186 | + fn parse(_input: &[&str]) -> Self { |
| 187 | + panic!("parsing this type requires the `build-mpfr` feature") |
| 188 | + } |
| 189 | + } |
| 190 | + |
| 191 | + impl ParseTuple for ($ty, $ty) { |
| 192 | + fn parse(_input: &[&str]) -> Self { |
| 193 | + panic!("parsing this type requires the `build-mpfr` feature") |
| 194 | + } |
| 195 | + } |
| 196 | + |
| 197 | + impl ParseTuple for ($ty, i32) { |
| 198 | + fn parse(_input: &[&str]) -> Self { |
| 199 | + panic!("parsing this type requires the `build-mpfr` feature") |
| 200 | + } |
| 201 | + } |
| 202 | + |
| 203 | + impl ParseTuple for (i32, $ty) { |
| 204 | + fn parse(_input: &[&str]) -> Self { |
| 205 | + panic!("parsing this type requires the `build-mpfr` feature") |
| 206 | + } |
| 207 | + } |
| 208 | + |
| 209 | + impl ParseTuple for ($ty, $ty, $ty) { |
| 210 | + fn parse(_input: &[&str]) -> Self { |
| 211 | + panic!("parsing this type requires the `build-mpfr` feature") |
| 212 | + } |
| 213 | + } |
| 214 | + }; |
| 215 | +} |
| 216 | + |
| 217 | +impl_parse_tuple!(f32); |
| 218 | +impl_parse_tuple!(f64); |
| 219 | + |
| 220 | +#[cfg(f16_enabled)] |
| 221 | +impl_parse_tuple_via_rug!(f16); |
| 222 | +#[cfg(f128_enabled)] |
| 223 | +impl_parse_tuple_via_rug!(f128); |
| 224 | + |
| 225 | +/// Try to parse the number, printing a nice message on failure. |
| 226 | +fn parse<F: FromStr>(input: &[&str], idx: usize) -> F { |
| 227 | + let s = input[idx]; |
| 228 | + s.parse().unwrap_or_else(|_| panic!("invalid {} input '{s}'", type_name::<F>())) |
| 229 | +} |
| 230 | + |
| 231 | +/// Try to parse the float type going via `rug`, for `f16` and `f128` which don't yet implement |
| 232 | +/// `FromStr`. |
| 233 | +#[cfg(feature = "build-mpfr")] |
| 234 | +fn parse_rug<F: libm_test::Float>(input: &[&str], idx: usize) -> F |
| 235 | +where |
| 236 | + rug::Float: az::Cast<F>, |
| 237 | +{ |
| 238 | + let s = input[idx]; |
| 239 | + let x = |
| 240 | + rug::Float::parse(s).unwrap_or_else(|_| panic!("invalid {} input '{s}'", type_name::<F>())); |
| 241 | + let x = rug::Float::with_val(F::BITS, x); |
| 242 | + x.az() |
| 243 | +} |
0 commit comments