Skip to main content

zenith_float_macro/
lib.rs

1//! `expr!` procedural macro for zenith-float.
2//!
3//! Depend on the `zenith-float` crate and use `zenith_float::expr`. This crate is not a direct dependency for applications.
4
5#![allow(missing_docs)]
6#![deny(unused)]
7#![deny(clippy::suspicious)]
8
9mod cplx;
10mod util;
11
12use proc_macro2::TokenStream;
13use quote::quote;
14use syn::{
15    parse::Parse, spanned::Spanned, BinOp, Error, Expr, ExprBinary, ExprCall, ExprGroup, ExprLit,
16    ExprParen, ExprPath, ExprUnary, Lit, Token, UnOp,
17};
18use util::{check_arg_num, str_to_exact_num_expr, str_to_exact_num_literal};
19use zenith_float_num::{Consts, EXPONENT_BIT_SIZE};
20
21// Speculative error estimation.
22// This error is added upfront, before actual error is known.
23// It helps to avoid additional recalculations due to changing error estimation.
24pub(crate) const SPEC_ADD_ERR: usize = 32;
25
26pub(crate) struct MacroInput {
27    pub(crate) expr: Expr,
28    pub(crate) ctx: Expr,
29}
30
31impl Parse for MacroInput {
32    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
33        let expr = input.parse()?;
34        input.parse::<Token![,]>()?;
35
36        let ctx = input.parse()?;
37
38        Ok(MacroInput { expr, ctx })
39    }
40}
41
42fn traverse_binary(
43    expr: &ExprBinary,
44    err: &mut Vec<usize>,
45    cc: &mut Consts,
46) -> Result<TokenStream, Error> {
47    let left_expr = traverse_expr(&expr.left, err, cc)?;
48    let right_expr = traverse_expr(&expr.right, err, cc)?;
49
50    let errs_id = err.len();
51
52    let ts = match expr.op {
53        BinOp::Add(_) => {
54            err.push(2);
55            quote!({
56                let arg1 = #left_expr;
57                let arg2 = #right_expr;
58                let ret = zenith_float::ExactNum::add(&arg1, &arg2, p_wrk, zenith_float::RoundingMode::None);
59                if arg1.inexact() || arg2.inexact() {
60                    if let (Some(e1), Some(e2), Some(e3)) = (arg1.exponent(), arg2.exponent(), ret.exponent()) {
61                        if (e1 as isize - e2 as isize).abs() <= 1 && arg1.sign() != arg2.sign() {
62                            let newerr = (e1.max(e2) as isize - e3 as isize).unsigned_abs() + 1;
63                            if errs[#errs_id] < newerr {
64                                errs[#errs_id] = newerr;
65                                continue;
66                            }
67                        }
68                    }
69                }
70                ret
71            })
72        }
73        BinOp::Sub(_) => {
74            err.push(2);
75            quote!({
76                let arg1 = #left_expr;
77                let arg2 = #right_expr;
78                let ret = zenith_float::ExactNum::sub(&arg1, &arg2, p_wrk, zenith_float::RoundingMode::None);
79                if arg1.inexact() || arg2.inexact() {
80                    if let (Some(e1), Some(e2), Some(e3)) = (arg1.exponent(), arg2.exponent(), ret.exponent()) {
81                        if (e1 as isize - e2 as isize).abs() <= 1 && arg1.sign() == arg2.sign() {
82                            let newerr = (e1.max(e2) as isize - e3 as isize).unsigned_abs() + 1;
83                            if errs[#errs_id] < newerr {
84                                errs[#errs_id] = newerr;
85                                continue;
86                            }
87                        }
88                    }
89                }
90                ret
91            })
92        }
93        BinOp::Mul(_) => {
94            err.push(3);
95            quote!(
96                zenith_float::ExactNum::mul(&(#left_expr), &(#right_expr), p_wrk, zenith_float::RoundingMode::None))
97        }
98        BinOp::Div(_) => {
99            err.push(3);
100            quote!(zenith_float::ExactNum::div(&(#left_expr), &(#right_expr), p_wrk, zenith_float::RoundingMode::None))
101        }
102        BinOp::Rem(_) => {
103            quote!(zenith_float::ExactNum::rem(&(#left_expr), &(#right_expr)))
104        }
105        _ => return Err(Error::new(
106            expr.span(),
107            "unexpected binary operator. Only \"+\", \"-\", \"*\", \"/\", and \"%\" are allowed.",
108        )),
109    };
110
111    Ok(ts)
112}
113
114fn one_arg_fun(
115    fun: TokenStream,
116    expr: &ExprCall,
117    initial_err: usize,
118    err: &mut Vec<usize>,
119    cc: &mut Consts,
120    use_cc: bool,
121) -> Result<TokenStream, Error> {
122    check_arg_num(1, expr)?;
123
124    let arg = traverse_expr(&expr.args[0], err, cc)?;
125    err.push(initial_err);
126
127    let ret = if use_cc {
128        quote!(#fun(&(#arg), p_wrk, zenith_float::RoundingMode::None, cc))
129    } else {
130        quote!(#fun(&(#arg), p_wrk, zenith_float::RoundingMode::None))
131    };
132
133    Ok(ret)
134}
135
136fn two_arg_fun(
137    fun: TokenStream,
138    expr: &ExprCall,
139    initial_err: usize,
140    err: &mut Vec<usize>,
141    cc: &mut Consts,
142    use_cc: bool,
143) -> Result<TokenStream, Error> {
144    check_arg_num(2, expr)?;
145
146    let arg1 = traverse_expr(&expr.args[0], err, cc)?;
147    let arg2 = traverse_expr(&expr.args[1], err, cc)?;
148    err.push(initial_err);
149
150    let ret = if use_cc {
151        quote!(#fun(&(#arg1), &(#arg2), p_wrk, zenith_float::RoundingMode::None, cc))
152    } else {
153        quote!(#fun(&(#arg1), &(#arg2), p_wrk, zenith_float::RoundingMode::None))
154    };
155
156    Ok(ret)
157}
158
159fn three_arg_fun(
160    fun: TokenStream,
161    expr: &ExprCall,
162    initial_err: usize,
163    err: &mut Vec<usize>,
164    cc: &mut Consts,
165    use_cc: bool,
166) -> Result<TokenStream, Error> {
167    check_arg_num(3, expr)?;
168
169    let arg1 = traverse_expr(&expr.args[0], err, cc)?;
170    let arg2 = traverse_expr(&expr.args[1], err, cc)?;
171    let arg3 = traverse_expr(&expr.args[2], err, cc)?;
172    err.push(initial_err);
173
174    let ret = if use_cc {
175        quote!(#fun(&(#arg1), &(#arg2), &(#arg3), p_wrk, zenith_float::RoundingMode::None, cc))
176    } else {
177        quote!(#fun(&(#arg1), &(#arg2), &(#arg3), p_wrk, zenith_float::RoundingMode::None))
178    };
179    Ok(ret)
180}
181
182fn root_fun(
183    expr: &ExprCall,
184    initial_err: usize,
185    err: &mut Vec<usize>,
186    cc: &mut Consts,
187) -> Result<TokenStream, Error> {
188    check_arg_num(2, expr)?;
189
190    let arg = traverse_expr(&expr.args[0], err, cc)?;
191    let n = &expr.args[1];
192    err.push(initial_err);
193
194    Ok(quote!(zenith_float::ExactNum::nth_root(
195        &(#arg),
196        #n as usize,
197        p_wrk,
198        zenith_float::RoundingMode::None
199    )))
200}
201
202fn ldexp_fun(
203    expr: &ExprCall,
204    initial_err: usize,
205    err: &mut Vec<usize>,
206    cc: &mut Consts,
207    scalb: bool,
208) -> Result<TokenStream, Error> {
209    check_arg_num(2, expr)?;
210
211    let arg = traverse_expr(&expr.args[0], err, cc)?;
212    let n = &expr.args[1];
213    err.push(initial_err);
214
215    let fun = if scalb {
216        quote!(zenith_float::ExactNum::scalb)
217    } else {
218        quote!(zenith_float::ExactNum::ldexp)
219    };
220
221    Ok(quote!(#fun(
222        &(#arg),
223        #n as zenith_float::Exponent,
224        p_wrk,
225        zenith_float::RoundingMode::None
226    )))
227}
228
229fn bessel_j_fun(
230    expr: &ExprCall,
231    initial_err: usize,
232    err: &mut Vec<usize>,
233    cc: &mut Consts,
234) -> Result<TokenStream, Error> {
235    check_arg_num(2, expr)?;
236
237    let arg = traverse_expr(&expr.args[0], err, cc)?;
238    let n = &expr.args[1];
239    err.push(initial_err);
240
241    Ok(quote!(zenith_float::ExactNum::bessel_j(
242        &(#arg),
243        #n as usize,
244        p_wrk,
245        zenith_float::RoundingMode::None,
246        cc
247    )))
248}
249
250fn four_arg_fun(
251    fun: TokenStream,
252    expr: &ExprCall,
253    initial_err: usize,
254    err: &mut Vec<usize>,
255    cc: &mut Consts,
256) -> Result<TokenStream, Error> {
257    check_arg_num(4, expr)?;
258    let arg1 = traverse_expr(&expr.args[0], err, cc)?;
259    let arg2 = traverse_expr(&expr.args[1], err, cc)?;
260    let arg3 = traverse_expr(&expr.args[2], err, cc)?;
261    let arg4 = traverse_expr(&expr.args[3], err, cc)?;
262    err.push(initial_err);
263    Ok(quote!(#fun(
264        &(#arg1),
265        &(#arg2),
266        &(#arg3),
267        &(#arg4),
268        p_wrk,
269        zenith_float::RoundingMode::None,
270        cc
271    )))
272}
273
274fn legendre_p_fun(
275    expr: &ExprCall,
276    initial_err: usize,
277    err: &mut Vec<usize>,
278    cc: &mut Consts,
279) -> Result<TokenStream, Error> {
280    check_arg_num(2, expr)?;
281    let arg = traverse_expr(&expr.args[0], err, cc)?;
282    let n = &expr.args[1];
283    err.push(initial_err);
284    Ok(quote!(zenith_float::ExactNum::legendre_p(
285        &(#arg),
286        #n as u32,
287        p_wrk,
288        zenith_float::RoundingMode::None
289    )))
290}
291
292fn legendre_p_assoc_fun(
293    expr: &ExprCall,
294    initial_err: usize,
295    err: &mut Vec<usize>,
296    cc: &mut Consts,
297) -> Result<TokenStream, Error> {
298    check_arg_num(3, expr)?;
299    let arg = traverse_expr(&expr.args[0], err, cc)?;
300    let n = &expr.args[1];
301    let m = &expr.args[2];
302    err.push(initial_err);
303    Ok(quote!(zenith_float::ExactNum::assoc_legendre_p(
304        &(#arg),
305        #n as u32,
306        #m as i32,
307        p_wrk,
308        zenith_float::RoundingMode::None
309    )))
310}
311
312fn one_arg_fun_errcheck(
313    fun: TokenStream,
314    expr: &ExprCall,
315    initial_err: usize,
316    err: &mut Vec<usize>,
317    errcheck: TokenStream,
318    cc: &mut Consts,
319) -> Result<TokenStream, Error> {
320    check_arg_num(1, expr)?;
321
322    let arg = traverse_expr(&expr.args[0], err, cc)?;
323    let errs_id = err.len();
324    err.push(initial_err);
325
326    Ok(quote!({
327        let arg = #arg;
328
329        let newerr = zenith_float::macro_util::compute_added_err(#errcheck);
330        if errs[#errs_id] < newerr {
331            errs[#errs_id] = newerr;
332            continue;
333        }
334
335        #fun(&arg, p_wrk, zenith_float::RoundingMode::None, cc)
336    }))
337}
338
339fn trig_fun(
340    fun: TokenStream,
341    expr: &ExprCall,
342    initial_err: usize,
343    err: &mut Vec<usize>,
344    errfun: TokenStream,
345    cc: &mut Consts,
346) -> Result<TokenStream, Error> {
347    check_arg_num(1, expr)?;
348
349    let arg = traverse_expr(&expr.args[0], err, cc)?;
350    let errs_id = err.len();
351    err.push(initial_err);
352
353    Ok(quote!({
354        let arg = zenith_float::macro_util::check_exponent_range(#arg, emin, emax);
355
356        let newerr = zenith_float::macro_util::compute_added_err(zenith_float::macro_util::ErrAlgo::Trig(&arg, p_wrk, #errfun, cc, emin));
357        if errs[#errs_id] < newerr {
358            errs[#errs_id] = newerr;
359            continue;
360        }
361
362        #fun(&arg, p_wrk, zenith_float::RoundingMode::None, cc)
363    }))
364}
365
366fn two_arg_fun_errcheck(
367    fun: TokenStream,
368    expr: &ExprCall,
369    initial_err: usize,
370    err: &mut Vec<usize>,
371    errcheck: TokenStream,
372    cc: &mut Consts,
373) -> Result<TokenStream, Error> {
374    check_arg_num(2, expr)?;
375
376    let arg1 = traverse_expr(&expr.args[0], err, cc)?;
377    let arg2 = traverse_expr(&expr.args[1], err, cc)?;
378
379    let errs_id = err.len();
380
381    err.push(initial_err);
382
383    Ok(quote!({
384        let arg1 = #arg1;
385        let arg2 = #arg2;
386
387        let newerr = zenith_float::macro_util::compute_added_err(#errcheck);
388        if errs[#errs_id] < newerr {
389            errs[#errs_id] = newerr;
390            continue;
391        }
392
393        #fun(&arg1, &arg2, p_wrk, zenith_float::RoundingMode::None, cc)
394    }))
395}
396
397fn traverse_call(
398    expr: &ExprCall,
399    err: &mut Vec<usize>,
400    cc: &mut Consts,
401) -> Result<TokenStream, Error> {
402    let errmes = "unexpected function name. Only \"recip\", \"sqrt\", \"cbrt\", \"root\", \"ln\", \"log2\", \"log10\", \"log\", \"log1p\", \"exp\", \"exp2\", \"exp10\", \"expm1\", \"pow\", \"rem_pi\", \"sin\", \"cos\", \"tan\", \"asin\", \"acos\", \"atan\", \"atan2\", \"hypot\", \"fma\", \"mul_add\", \"sinh\", \"cosh\", \"tanh\", \"asinh\", \"acosh\", \"atanh\", \"erf\", \"erfc\", \"gamma\", \"ln_gamma\", \"digamma\", \"gammainc\", \"gammainc_upper\", \"ei\", \"si\", \"ci\", \"li\", \"fresnel_s\", \"fresnel_c\", \"ai\", \"bi\", \"bessel_j\", \"bessel_j_nu\", \"bessel_y\", \"bessel_i\", \"bessel_k\", \"elliptic_k\", \"elliptic_e\", \"elliptic_e_inc\", \"elliptic_f\", \"elliptic_pi\", \"elliptic_pi_inc\", \"jacobi_am\", \"jacobi_sn\", \"jacobi_cn\", \"jacobi_dn\", \"jacobi_cd\", \"jacobi_ns\", \"jacobi_nc\", \"jacobi_nd\", \"jacobi_sc\", \"jacobi_sd\", \"jacobi_cs\", \"jacobi_ds\", \"jacobi_dc\", \"legendre_p\", \"legendre_p_assoc\", \"hypergeom_2f1\", \"betainc\", \"normal_pdf\", \"normal_cdf\", \"gamma_pdf\", \"beta_pdf\", \"poisson_pmf\", \"binomial_pmf\", \"chi_squared_cdf\", \"student_t_pdf\", \"ldexp\", \"scalb\", \"logb\" are allowed.";
403
404    if let Expr::Path(fun) = expr.func.as_ref() {
405        if let Some(fname) = fun.path.get_ident() {
406            let ts = match fname.to_string().as_str() {
407                "recip" => one_arg_fun(
408                    quote!(zenith_float::ExactNum::reciprocal),
409                    expr,
410                    2,
411                    err,
412                    cc,
413                    false,
414                ),
415                "sqrt" => one_arg_fun(
416                    quote!(zenith_float::ExactNum::sqrt),
417                    expr,
418                    1,
419                    err,
420                    cc,
421                    false,
422                ),
423                "cbrt" => one_arg_fun(
424                    quote!(zenith_float::ExactNum::cbrt),
425                    expr,
426                    1,
427                    err,
428                    cc,
429                    false,
430                ),
431                "root" => root_fun(expr, 1, err, cc),
432                "ln" => one_arg_fun_errcheck(
433                    quote!(zenith_float::ExactNum::ln),
434                    expr,
435                    SPEC_ADD_ERR,
436                    err,
437                    quote!(zenith_float::macro_util::ErrAlgo::Log(&arg, 2, emin)),
438                    cc,
439                ),
440                "log2" => one_arg_fun_errcheck(
441                    quote!(zenith_float::ExactNum::log2),
442                    expr,
443                    SPEC_ADD_ERR,
444                    err,
445                    quote!(zenith_float::macro_util::ErrAlgo::Log(&arg, 3, emin)),
446                    cc,
447                ),
448                "log10" => one_arg_fun_errcheck(
449                    quote!(zenith_float::ExactNum::log10),
450                    expr,
451                    SPEC_ADD_ERR,
452                    err,
453                    quote!(zenith_float::macro_util::ErrAlgo::Log(&arg, 6, emin)),
454                    cc,
455                ),
456                "log" => two_arg_fun_errcheck(
457                    quote!(zenith_float::ExactNum::log),
458                    expr,
459                    SPEC_ADD_ERR,
460                    err,
461                    quote!(zenith_float::macro_util::ErrAlgo::Log2(&arg2, &arg1, emin)),
462                    cc,
463                ),
464                "log1p" => one_arg_fun(
465                    quote!(zenith_float::ExactNum::log1p),
466                    expr,
467                    SPEC_ADD_ERR,
468                    err,
469                    cc,
470                    true,
471                ),
472                "exp" => one_arg_fun(
473                    quote!(zenith_float::ExactNum::exp),
474                    expr,
475                    EXPONENT_BIT_SIZE + 1,
476                    err,
477                    cc,
478                    true,
479                ),
480                "exp2" => one_arg_fun(
481                    quote!(zenith_float::ExactNum::exp2),
482                    expr,
483                    EXPONENT_BIT_SIZE + 1,
484                    err,
485                    cc,
486                    true,
487                ),
488                "exp10" => one_arg_fun(
489                    quote!(zenith_float::ExactNum::exp10),
490                    expr,
491                    EXPONENT_BIT_SIZE + 1,
492                    err,
493                    cc,
494                    true,
495                ),
496                "expm1" => one_arg_fun(
497                    quote!(zenith_float::ExactNum::expm1),
498                    expr,
499                    EXPONENT_BIT_SIZE + 1,
500                    err,
501                    cc,
502                    true,
503                ),
504                "pow" => two_arg_fun_errcheck(
505                    quote!(zenith_float::ExactNum::pow),
506                    expr,
507                    EXPONENT_BIT_SIZE + SPEC_ADD_ERR,
508                    err,
509                    quote!(zenith_float::macro_util::ErrAlgo::Pow(&arg1, &arg2, emin)),
510                    cc,
511                ),
512                "rem_pi" => one_arg_fun(
513                    quote!(zenith_float::ExactNum::rem_pi),
514                    expr,
515                    SPEC_ADD_ERR,
516                    err,
517                    cc,
518                    true,
519                ),
520                "sin" => trig_fun(
521                    quote!(zenith_float::ExactNum::sin),
522                    expr,
523                    SPEC_ADD_ERR,
524                    err,
525                    quote!(zenith_float::macro_util::TrigFun::Sin),
526                    cc,
527                ),
528                "cos" => trig_fun(
529                    quote!(zenith_float::ExactNum::cos),
530                    expr,
531                    SPEC_ADD_ERR,
532                    err,
533                    quote!(zenith_float::macro_util::TrigFun::Cos),
534                    cc,
535                ),
536                "tan" => trig_fun(
537                    quote!(zenith_float::ExactNum::tan),
538                    expr,
539                    SPEC_ADD_ERR,
540                    err,
541                    quote!(zenith_float::macro_util::TrigFun::Tan),
542                    cc,
543                ),
544                "asin" => one_arg_fun_errcheck(
545                    quote!(zenith_float::ExactNum::asin),
546                    expr,
547                    SPEC_ADD_ERR / 2,
548                    err,
549                    quote!(zenith_float::macro_util::ErrAlgo::Asin(&arg, emin)),
550                    cc,
551                ),
552                "acos" => one_arg_fun_errcheck(
553                    quote!(zenith_float::ExactNum::acos),
554                    expr,
555                    SPEC_ADD_ERR / 2,
556                    err,
557                    quote!(zenith_float::macro_util::ErrAlgo::Acos(&arg, emin)),
558                    cc,
559                ),
560                "atan" => one_arg_fun(quote!(zenith_float::ExactNum::atan), expr, 2, err, cc, true),
561                "atan2" => two_arg_fun(
562                    quote!(zenith_float::ExactNum::atan2),
563                    expr,
564                    2,
565                    err,
566                    cc,
567                    true,
568                ),
569                "hypot" => two_arg_fun(
570                    quote!(zenith_float::ExactNum::hypot),
571                    expr,
572                    2,
573                    err,
574                    cc,
575                    false,
576                ),
577                "fma" => {
578                    three_arg_fun(quote!(zenith_float::ExactNum::fma), expr, 2, err, cc, false)
579                }
580                "mul_add" => three_arg_fun(
581                    quote!(zenith_float::ExactNum::mul_add),
582                    expr,
583                    2,
584                    err,
585                    cc,
586                    false,
587                ),
588                "sinh" => one_arg_fun(
589                    quote!(zenith_float::ExactNum::sinh),
590                    expr,
591                    EXPONENT_BIT_SIZE + 1,
592                    err,
593                    cc,
594                    true,
595                ),
596                "cosh" => one_arg_fun(
597                    quote!(zenith_float::ExactNum::cosh),
598                    expr,
599                    EXPONENT_BIT_SIZE + 1,
600                    err,
601                    cc,
602                    true,
603                ),
604                "tanh" => one_arg_fun(quote!(zenith_float::ExactNum::tanh), expr, 2, err, cc, true),
605                "asinh" => one_arg_fun(
606                    quote!(zenith_float::ExactNum::asinh),
607                    expr,
608                    2,
609                    err,
610                    cc,
611                    true,
612                ),
613                "acosh" => one_arg_fun_errcheck(
614                    quote!(zenith_float::ExactNum::acosh),
615                    expr,
616                    SPEC_ADD_ERR,
617                    err,
618                    quote!(zenith_float::macro_util::ErrAlgo::Acosh(&arg, emin)),
619                    cc,
620                ),
621                "atanh" => one_arg_fun_errcheck(
622                    quote!(zenith_float::ExactNum::atanh),
623                    expr,
624                    SPEC_ADD_ERR,
625                    err,
626                    quote!(zenith_float::macro_util::ErrAlgo::Atanh(&arg, emin)),
627                    cc,
628                ),
629                "erf" => one_arg_fun(quote!(zenith_float::ExactNum::erf), expr, 2, err, cc, true),
630                "erfc" => one_arg_fun(quote!(zenith_float::ExactNum::erfc), expr, 2, err, cc, true),
631                "gamma" => one_arg_fun(
632                    quote!(zenith_float::ExactNum::gamma),
633                    expr,
634                    EXPONENT_BIT_SIZE + 1,
635                    err,
636                    cc,
637                    true,
638                ),
639                "ln_gamma" => one_arg_fun(
640                    quote!(zenith_float::ExactNum::ln_gamma),
641                    expr,
642                    EXPONENT_BIT_SIZE + 1,
643                    err,
644                    cc,
645                    true,
646                ),
647                "digamma" => one_arg_fun(
648                    quote!(zenith_float::ExactNum::digamma),
649                    expr,
650                    EXPONENT_BIT_SIZE + 1,
651                    err,
652                    cc,
653                    true,
654                ),
655                "gammainc" => two_arg_fun(
656                    quote!(zenith_float::ExactNum::gammainc),
657                    expr,
658                    EXPONENT_BIT_SIZE + 1,
659                    err,
660                    cc,
661                    true,
662                ),
663                "gammainc_upper" => two_arg_fun(
664                    quote!(zenith_float::ExactNum::gammainc_upper),
665                    expr,
666                    EXPONENT_BIT_SIZE + 1,
667                    err,
668                    cc,
669                    true,
670                ),
671                "ei" => one_arg_fun(quote!(zenith_float::ExactNum::ei), expr, 2, err, cc, true),
672                "si" => one_arg_fun(quote!(zenith_float::ExactNum::si), expr, 2, err, cc, true),
673                "ci" => one_arg_fun(quote!(zenith_float::ExactNum::ci), expr, 2, err, cc, true),
674                "li" => one_arg_fun(quote!(zenith_float::ExactNum::li), expr, 2, err, cc, true),
675                "fresnel_s" => one_arg_fun(
676                    quote!(zenith_float::ExactNum::fresnel_s),
677                    expr,
678                    2,
679                    err,
680                    cc,
681                    true,
682                ),
683                "fresnel_c" => one_arg_fun(
684                    quote!(zenith_float::ExactNum::fresnel_c),
685                    expr,
686                    2,
687                    err,
688                    cc,
689                    true,
690                ),
691                "ai" => one_arg_fun(quote!(zenith_float::ExactNum::ai), expr, 2, err, cc, true),
692                "bi" => one_arg_fun(quote!(zenith_float::ExactNum::bi), expr, 2, err, cc, true),
693                "bessel_j" => bessel_j_fun(expr, 2, err, cc),
694                "bessel_j_nu" => two_arg_fun(
695                    quote!(zenith_float::ExactNum::bessel_j_nu),
696                    expr,
697                    2,
698                    err,
699                    cc,
700                    true,
701                ),
702                "bessel_y" => two_arg_fun(
703                    quote!(zenith_float::ExactNum::bessel_y),
704                    expr,
705                    2,
706                    err,
707                    cc,
708                    true,
709                ),
710                "bessel_i" => two_arg_fun(
711                    quote!(zenith_float::ExactNum::bessel_i),
712                    expr,
713                    2,
714                    err,
715                    cc,
716                    true,
717                ),
718                "bessel_k" => two_arg_fun(
719                    quote!(zenith_float::ExactNum::bessel_k),
720                    expr,
721                    2,
722                    err,
723                    cc,
724                    true,
725                ),
726                "elliptic_k" => one_arg_fun(
727                    quote!(zenith_float::ExactNum::elliptic_k),
728                    expr,
729                    2,
730                    err,
731                    cc,
732                    true,
733                ),
734                "elliptic_e" => one_arg_fun(
735                    quote!(zenith_float::ExactNum::elliptic_e_complete),
736                    expr,
737                    2,
738                    err,
739                    cc,
740                    true,
741                ),
742                "elliptic_e_inc" => two_arg_fun(
743                    quote!(zenith_float::ExactNum::elliptic_e),
744                    expr,
745                    2,
746                    err,
747                    cc,
748                    true,
749                ),
750                "elliptic_f" => two_arg_fun(
751                    quote!(zenith_float::ExactNum::elliptic_f),
752                    expr,
753                    2,
754                    err,
755                    cc,
756                    true,
757                ),
758                "jacobi_am" => two_arg_fun(
759                    quote!(zenith_float::ExactNum::jacobi_am),
760                    expr,
761                    2,
762                    err,
763                    cc,
764                    true,
765                ),
766                "jacobi_sn" => two_arg_fun(
767                    quote!(zenith_float::ExactNum::jacobi_sn),
768                    expr,
769                    2,
770                    err,
771                    cc,
772                    true,
773                ),
774                "jacobi_cn" => two_arg_fun(
775                    quote!(zenith_float::ExactNum::jacobi_cn),
776                    expr,
777                    2,
778                    err,
779                    cc,
780                    true,
781                ),
782                "jacobi_dn" => two_arg_fun(
783                    quote!(zenith_float::ExactNum::jacobi_dn),
784                    expr,
785                    2,
786                    err,
787                    cc,
788                    true,
789                ),
790                "jacobi_cd" => two_arg_fun(
791                    quote!(zenith_float::ExactNum::jacobi_cd),
792                    expr,
793                    2,
794                    err,
795                    cc,
796                    true,
797                ),
798                "jacobi_ns" => two_arg_fun(
799                    quote!(zenith_float::ExactNum::jacobi_ns),
800                    expr,
801                    2,
802                    err,
803                    cc,
804                    true,
805                ),
806                "jacobi_nc" => two_arg_fun(
807                    quote!(zenith_float::ExactNum::jacobi_nc),
808                    expr,
809                    2,
810                    err,
811                    cc,
812                    true,
813                ),
814                "jacobi_nd" => two_arg_fun(
815                    quote!(zenith_float::ExactNum::jacobi_nd),
816                    expr,
817                    2,
818                    err,
819                    cc,
820                    true,
821                ),
822                "jacobi_sc" => two_arg_fun(
823                    quote!(zenith_float::ExactNum::jacobi_sc),
824                    expr,
825                    2,
826                    err,
827                    cc,
828                    true,
829                ),
830                "jacobi_sd" => two_arg_fun(
831                    quote!(zenith_float::ExactNum::jacobi_sd),
832                    expr,
833                    2,
834                    err,
835                    cc,
836                    true,
837                ),
838                "jacobi_cs" => two_arg_fun(
839                    quote!(zenith_float::ExactNum::jacobi_cs),
840                    expr,
841                    2,
842                    err,
843                    cc,
844                    true,
845                ),
846                "jacobi_ds" => two_arg_fun(
847                    quote!(zenith_float::ExactNum::jacobi_ds),
848                    expr,
849                    2,
850                    err,
851                    cc,
852                    true,
853                ),
854                "jacobi_dc" => two_arg_fun(
855                    quote!(zenith_float::ExactNum::jacobi_dc),
856                    expr,
857                    2,
858                    err,
859                    cc,
860                    true,
861                ),
862                "elliptic_pi" => two_arg_fun(
863                    quote!(zenith_float::ExactNum::elliptic_pi_complete),
864                    expr,
865                    2,
866                    err,
867                    cc,
868                    true,
869                ),
870                "elliptic_pi_inc" => three_arg_fun(
871                    quote!(zenith_float::ExactNum::elliptic_pi),
872                    expr,
873                    2,
874                    err,
875                    cc,
876                    true,
877                ),
878                "legendre_p" => legendre_p_fun(expr, 2, err, cc),
879                "legendre_p_assoc" => legendre_p_assoc_fun(expr, 2, err, cc),
880                "hypergeom_2f1" => four_arg_fun(
881                    quote!(zenith_float::ExactNum::hypergeom_2f1),
882                    expr,
883                    EXPONENT_BIT_SIZE + 1,
884                    err,
885                    cc,
886                ),
887                "betainc" => three_arg_fun(
888                    quote!(zenith_float::ExactNum::betainc),
889                    expr,
890                    EXPONENT_BIT_SIZE + 1,
891                    err,
892                    cc,
893                    true,
894                ),
895                "normal_pdf" => three_arg_fun(
896                    quote!(zenith_float::ExactNum::normal_pdf),
897                    expr,
898                    EXPONENT_BIT_SIZE + 1,
899                    err,
900                    cc,
901                    true,
902                ),
903                "normal_cdf" => three_arg_fun(
904                    quote!(zenith_float::ExactNum::normal_cdf),
905                    expr,
906                    EXPONENT_BIT_SIZE + 1,
907                    err,
908                    cc,
909                    true,
910                ),
911                "gamma_pdf" => three_arg_fun(
912                    quote!(zenith_float::ExactNum::gamma_pdf),
913                    expr,
914                    EXPONENT_BIT_SIZE + 1,
915                    err,
916                    cc,
917                    true,
918                ),
919                "beta_pdf" => three_arg_fun(
920                    quote!(zenith_float::ExactNum::beta_pdf),
921                    expr,
922                    EXPONENT_BIT_SIZE + 1,
923                    err,
924                    cc,
925                    true,
926                ),
927                "poisson_pmf" => two_arg_fun(
928                    quote!(zenith_float::ExactNum::poisson_pmf),
929                    expr,
930                    EXPONENT_BIT_SIZE + 1,
931                    err,
932                    cc,
933                    true,
934                ),
935                "binomial_pmf" => three_arg_fun(
936                    quote!(zenith_float::ExactNum::binomial_pmf),
937                    expr,
938                    EXPONENT_BIT_SIZE + 1,
939                    err,
940                    cc,
941                    true,
942                ),
943                "chi_squared_cdf" => two_arg_fun(
944                    quote!(zenith_float::ExactNum::chi_squared_cdf),
945                    expr,
946                    EXPONENT_BIT_SIZE + 1,
947                    err,
948                    cc,
949                    true,
950                ),
951                "student_t_pdf" => two_arg_fun(
952                    quote!(zenith_float::ExactNum::student_t_pdf),
953                    expr,
954                    EXPONENT_BIT_SIZE + 1,
955                    err,
956                    cc,
957                    true,
958                ),
959                "ldexp" => ldexp_fun(expr, 2, err, cc, false),
960                "scalb" => ldexp_fun(expr, 2, err, cc, true),
961                "logb" => one_arg_fun(
962                    quote!(zenith_float::ExactNum::logb),
963                    expr,
964                    1,
965                    err,
966                    cc,
967                    false,
968                ),
969                _ => return Err(Error::new(expr.span(), errmes)),
970            }?;
971
972            return Ok(ts);
973        }
974    }
975    Err(Error::new(expr.span(), errmes))
976}
977
978fn traverse_group(
979    expr: &ExprGroup,
980    err: &mut Vec<usize>,
981    cc: &mut Consts,
982) -> Result<TokenStream, Error> {
983    traverse_expr(&expr.expr, err, cc)
984}
985
986fn traverse_lit(expr: &ExprLit, cc: &mut Consts) -> Result<TokenStream, Error> {
987    let span = expr.span();
988
989    match &expr.lit {
990        Lit::Str(v) => str_to_exact_num_expr(&v.value(), span, cc),
991        Lit::Int(v) => str_to_exact_num_expr(v.base10_digits(), span, cc),
992        Lit::Float(v) => str_to_exact_num_expr(v.base10_digits(), span, cc),
993        _ => Err(Error::new(
994            expr.span(),
995            "unexpected literal. Only string, integer, or floating point literals are supported.",
996        )),
997    }
998}
999
1000fn traverse_paren(
1001    expr: &ExprParen,
1002    err: &mut Vec<usize>,
1003    cc: &mut Consts,
1004) -> Result<TokenStream, Error> {
1005    traverse_expr(&expr.expr, err, cc)
1006}
1007
1008fn traverse_path(expr: &ExprPath) -> Result<TokenStream, Error> {
1009    Ok(if expr.path.is_ident("pi") {
1010        quote!({ cc.pi(p_wrk, zenith_float::RoundingMode::None) })
1011    } else if expr.path.is_ident("e") {
1012        quote!({ cc.e(p_wrk, zenith_float::RoundingMode::None) })
1013    } else if expr.path.is_ident("ln_2") {
1014        quote!({ cc.ln_2(p_wrk, zenith_float::RoundingMode::None) })
1015    } else if expr.path.is_ident("ln_10") {
1016        quote!({ cc.ln_10(p_wrk, zenith_float::RoundingMode::None) })
1017    } else if expr.path.is_ident("sqrt2") {
1018        quote!({ cc.sqrt2(p_wrk, zenith_float::RoundingMode::None) })
1019    } else if expr.path.is_ident("phi") {
1020        quote!({ cc.phi(p_wrk, zenith_float::RoundingMode::None) })
1021    } else if expr.path.is_ident("euler_gamma") {
1022        quote!({ cc.euler_gamma(p_wrk, zenith_float::RoundingMode::None) })
1023    } else {
1024        quote!({
1025            let mut arg = zenith_float::ExactNum::from_ext((#expr).clone(), p_wrk, zenith_float::RoundingMode::ToEven, cc);
1026            arg.set_inexact(false);
1027            arg = zenith_float::macro_util::check_exponent_range(arg, emin, emax);
1028            arg
1029        })
1030    })
1031}
1032
1033fn traverse_unary(
1034    expr: &ExprUnary,
1035    err: &mut Vec<usize>,
1036    cc: &mut Consts,
1037) -> Result<TokenStream, Error> {
1038    let op_expr = traverse_expr(&expr.expr, err, cc)?;
1039
1040    match expr.op {
1041        UnOp::Neg(_) => Ok(quote!(zenith_float::ExactNum::neg(&(#op_expr)))),
1042        _ => Err(Error::new(
1043            expr.span(),
1044            "unexpected unary operator. Only \"-\" is allowed.",
1045        )),
1046    }
1047}
1048
1049fn traverse_expr(expr: &Expr, err: &mut Vec<usize>, cc: &mut Consts) -> Result<TokenStream, Error> {
1050    match expr {
1051        Expr::Binary(e) => traverse_binary(e, err,cc),
1052        Expr::Call(e) => traverse_call(e, err,cc),
1053        Expr::Group(e) => traverse_group(e, err,cc),
1054        Expr::Lit(e) => traverse_lit(e, cc),
1055        Expr::Paren(e) => traverse_paren(e, err, cc),
1056        Expr::Path(e) => traverse_path(e),
1057        Expr::Unary(e) => traverse_unary(e, err, cc),
1058        _ => Err(Error::new(expr.span(), "unexpected expression. Only operators \"+\", \"-\", \"*\", \"/\", \"%\", functions \"recip\", \"sqrt\", \"cbrt\", \"ln\", \"log2\", \"log10\", \"log\", \"exp\", \"pow\", \"sin\", \"cos\", \"tan\", \"asin\", \"acos\", \"atan\", \"sinh\", \"cosh\", \"tanh\", \"asinh\", \"acosh\", \"atanh\", literals and variables, and grouping with parentheses are supported.")),
1059    }
1060}
1061
1062// Docs for the macro are in the zenith-float crate.
1063
1064#[proc_macro]
1065#[allow(missing_docs)]
1066pub fn expr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1067    let pmi = syn::parse_macro_input!(input as MacroInput);
1068
1069    let MacroInput { expr, ctx } = pmi;
1070
1071    let mut err = Vec::new();
1072
1073    let mut cc = Consts::new().expect("Failed to initialize constant cache.");
1074
1075    let expr = traverse_expr(&expr, &mut err, &mut cc).unwrap_or_else(|e| e.to_compile_error());
1076
1077    let err_sz = err.len();
1078
1079    let ret = quote!({
1080        use zenith_float::FromExt;
1081        use zenith_float::ctx::Contextable;
1082
1083        let mut ctx = &mut (#ctx);
1084        let p: usize = ctx.precision();
1085        let rm = ctx.rounding_mode();
1086        let emin = ctx.emin();
1087        let emax = ctx.emax();
1088        let cc = ctx.consts();
1089
1090        let mut p_rnd = p + zenith_float::WORD_BIT_SIZE;
1091        let mut errs: [usize; #err_sz] = [#(#err, )*];
1092
1093        loop {
1094            let p_wrk = p_rnd.saturating_add(errs.iter().sum());
1095
1096            let mut ret: zenith_float::ExactNum = (#expr).into();
1097
1098            if let Err(err) = ret.set_precision(p, rm) {
1099                ret = zenith_float::ExactNum::nan(Some(err));
1100            }
1101
1102            break zenith_float::macro_util::check_exponent_range(ret, emin, emax);
1103        }
1104    });
1105
1106    ret.into()
1107}
1108
1109/// Complex `expr!`: same context and working-precision loop, `ExactComplex` leaves, cancellation on both parts.
1110#[proc_macro]
1111#[allow(missing_docs)]
1112pub fn cexpr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1113    cplx::cexpr(input)
1114}
1115
1116/// Compile-time decimal float literal.
1117///
1118/// Parses a string literal at compile time and expands to an exact `ExactNum`.
1119/// Use via the `zenith-float` crate: `use zenith_float::exact`.
1120#[proc_macro]
1121pub fn exact(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1122    let lit = syn::parse_macro_input!(input as syn::LitStr);
1123    str_to_exact_num_literal(&lit.value(), lit.span())
1124        .unwrap_or_else(|e| e.to_compile_error())
1125        .into()
1126}
1127
1128/// Alias for [`exact`], matching dashu-float naming.
1129#[proc_macro]
1130pub fn fbig(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1131    exact(input)
1132}