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\", \"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                "elliptic_pi" => two_arg_fun(
759                    quote!(zenith_float::ExactNum::elliptic_pi_complete),
760                    expr,
761                    2,
762                    err,
763                    cc,
764                    true,
765                ),
766                "elliptic_pi_inc" => three_arg_fun(
767                    quote!(zenith_float::ExactNum::elliptic_pi),
768                    expr,
769                    2,
770                    err,
771                    cc,
772                    true,
773                ),
774                "legendre_p" => legendre_p_fun(expr, 2, err, cc),
775                "legendre_p_assoc" => legendre_p_assoc_fun(expr, 2, err, cc),
776                "hypergeom_2f1" => four_arg_fun(
777                    quote!(zenith_float::ExactNum::hypergeom_2f1),
778                    expr,
779                    EXPONENT_BIT_SIZE + 1,
780                    err,
781                    cc,
782                ),
783                "betainc" => three_arg_fun(
784                    quote!(zenith_float::ExactNum::betainc),
785                    expr,
786                    EXPONENT_BIT_SIZE + 1,
787                    err,
788                    cc,
789                    true,
790                ),
791                "normal_pdf" => three_arg_fun(
792                    quote!(zenith_float::ExactNum::normal_pdf),
793                    expr,
794                    EXPONENT_BIT_SIZE + 1,
795                    err,
796                    cc,
797                    true,
798                ),
799                "normal_cdf" => three_arg_fun(
800                    quote!(zenith_float::ExactNum::normal_cdf),
801                    expr,
802                    EXPONENT_BIT_SIZE + 1,
803                    err,
804                    cc,
805                    true,
806                ),
807                "gamma_pdf" => three_arg_fun(
808                    quote!(zenith_float::ExactNum::gamma_pdf),
809                    expr,
810                    EXPONENT_BIT_SIZE + 1,
811                    err,
812                    cc,
813                    true,
814                ),
815                "beta_pdf" => three_arg_fun(
816                    quote!(zenith_float::ExactNum::beta_pdf),
817                    expr,
818                    EXPONENT_BIT_SIZE + 1,
819                    err,
820                    cc,
821                    true,
822                ),
823                "poisson_pmf" => two_arg_fun(
824                    quote!(zenith_float::ExactNum::poisson_pmf),
825                    expr,
826                    EXPONENT_BIT_SIZE + 1,
827                    err,
828                    cc,
829                    true,
830                ),
831                "binomial_pmf" => three_arg_fun(
832                    quote!(zenith_float::ExactNum::binomial_pmf),
833                    expr,
834                    EXPONENT_BIT_SIZE + 1,
835                    err,
836                    cc,
837                    true,
838                ),
839                "chi_squared_cdf" => two_arg_fun(
840                    quote!(zenith_float::ExactNum::chi_squared_cdf),
841                    expr,
842                    EXPONENT_BIT_SIZE + 1,
843                    err,
844                    cc,
845                    true,
846                ),
847                "student_t_pdf" => two_arg_fun(
848                    quote!(zenith_float::ExactNum::student_t_pdf),
849                    expr,
850                    EXPONENT_BIT_SIZE + 1,
851                    err,
852                    cc,
853                    true,
854                ),
855                "ldexp" => ldexp_fun(expr, 2, err, cc, false),
856                "scalb" => ldexp_fun(expr, 2, err, cc, true),
857                "logb" => one_arg_fun(
858                    quote!(zenith_float::ExactNum::logb),
859                    expr,
860                    1,
861                    err,
862                    cc,
863                    false,
864                ),
865                _ => return Err(Error::new(expr.span(), errmes)),
866            }?;
867
868            return Ok(ts);
869        }
870    }
871    Err(Error::new(expr.span(), errmes))
872}
873
874fn traverse_group(
875    expr: &ExprGroup,
876    err: &mut Vec<usize>,
877    cc: &mut Consts,
878) -> Result<TokenStream, Error> {
879    traverse_expr(&expr.expr, err, cc)
880}
881
882fn traverse_lit(expr: &ExprLit, cc: &mut Consts) -> Result<TokenStream, Error> {
883    let span = expr.span();
884
885    match &expr.lit {
886        Lit::Str(v) => str_to_exact_num_expr(&v.value(), span, cc),
887        Lit::Int(v) => str_to_exact_num_expr(v.base10_digits(), span, cc),
888        Lit::Float(v) => str_to_exact_num_expr(v.base10_digits(), span, cc),
889        _ => Err(Error::new(
890            expr.span(),
891            "unexpected literal. Only string, integer, or floating point literals are supported.",
892        )),
893    }
894}
895
896fn traverse_paren(
897    expr: &ExprParen,
898    err: &mut Vec<usize>,
899    cc: &mut Consts,
900) -> Result<TokenStream, Error> {
901    traverse_expr(&expr.expr, err, cc)
902}
903
904fn traverse_path(expr: &ExprPath) -> Result<TokenStream, Error> {
905    Ok(if expr.path.is_ident("pi") {
906        quote!({ cc.pi(p_wrk, zenith_float::RoundingMode::None) })
907    } else if expr.path.is_ident("e") {
908        quote!({ cc.e(p_wrk, zenith_float::RoundingMode::None) })
909    } else if expr.path.is_ident("ln_2") {
910        quote!({ cc.ln_2(p_wrk, zenith_float::RoundingMode::None) })
911    } else if expr.path.is_ident("ln_10") {
912        quote!({ cc.ln_10(p_wrk, zenith_float::RoundingMode::None) })
913    } else if expr.path.is_ident("sqrt2") {
914        quote!({ cc.sqrt2(p_wrk, zenith_float::RoundingMode::None) })
915    } else if expr.path.is_ident("phi") {
916        quote!({ cc.phi(p_wrk, zenith_float::RoundingMode::None) })
917    } else if expr.path.is_ident("euler_gamma") {
918        quote!({ cc.euler_gamma(p_wrk, zenith_float::RoundingMode::None) })
919    } else {
920        quote!({
921            let mut arg = zenith_float::ExactNum::from_ext((#expr).clone(), p_wrk, zenith_float::RoundingMode::ToEven, cc);
922            arg.set_inexact(false);
923            arg = zenith_float::macro_util::check_exponent_range(arg, emin, emax);
924            arg
925        })
926    })
927}
928
929fn traverse_unary(
930    expr: &ExprUnary,
931    err: &mut Vec<usize>,
932    cc: &mut Consts,
933) -> Result<TokenStream, Error> {
934    let op_expr = traverse_expr(&expr.expr, err, cc)?;
935
936    match expr.op {
937        UnOp::Neg(_) => Ok(quote!(zenith_float::ExactNum::neg(&(#op_expr)))),
938        _ => Err(Error::new(
939            expr.span(),
940            "unexpected unary operator. Only \"-\" is allowed.",
941        )),
942    }
943}
944
945fn traverse_expr(expr: &Expr, err: &mut Vec<usize>, cc: &mut Consts) -> Result<TokenStream, Error> {
946    match expr {
947        Expr::Binary(e) => traverse_binary(e, err,cc),
948        Expr::Call(e) => traverse_call(e, err,cc),
949        Expr::Group(e) => traverse_group(e, err,cc),
950        Expr::Lit(e) => traverse_lit(e, cc),
951        Expr::Paren(e) => traverse_paren(e, err, cc),
952        Expr::Path(e) => traverse_path(e),
953        Expr::Unary(e) => traverse_unary(e, err, cc),
954        _ => 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.")),
955    }
956}
957
958// Docs for the macro are in the zenith-float crate.
959
960#[proc_macro]
961#[allow(missing_docs)]
962pub fn expr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
963    let pmi = syn::parse_macro_input!(input as MacroInput);
964
965    let MacroInput { expr, ctx } = pmi;
966
967    let mut err = Vec::new();
968
969    let mut cc = Consts::new().expect("Failed to initialize constant cache.");
970
971    let expr = traverse_expr(&expr, &mut err, &mut cc).unwrap_or_else(|e| e.to_compile_error());
972
973    let err_sz = err.len();
974
975    let ret = quote!({
976        use zenith_float::FromExt;
977        use zenith_float::ctx::Contextable;
978
979        let mut ctx = &mut (#ctx);
980        let p: usize = ctx.precision();
981        let rm = ctx.rounding_mode();
982        let emin = ctx.emin();
983        let emax = ctx.emax();
984        let cc = ctx.consts();
985
986        let mut p_rnd = p + zenith_float::WORD_BIT_SIZE;
987        let mut errs: [usize; #err_sz] = [#(#err, )*];
988
989        loop {
990            let p_wrk = p_rnd.saturating_add(errs.iter().sum());
991
992            let mut ret: zenith_float::ExactNum = (#expr).into();
993
994            if let Err(err) = ret.set_precision(p, rm) {
995                ret = zenith_float::ExactNum::nan(Some(err));
996            }
997
998            break zenith_float::macro_util::check_exponent_range(ret, emin, emax);
999        }
1000    });
1001
1002    ret.into()
1003}
1004
1005/// Complex `expr!`: same context and working-precision loop, `ExactComplex` leaves, cancellation on both parts.
1006#[proc_macro]
1007#[allow(missing_docs)]
1008pub fn cexpr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1009    cplx::cexpr(input)
1010}
1011
1012/// Compile-time decimal float literal.
1013///
1014/// Parses a string literal at compile time and expands to an exact `ExactNum`.
1015/// Use via the `zenith-float` crate: `use zenith_float::exact`.
1016#[proc_macro]
1017pub fn exact(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1018    let lit = syn::parse_macro_input!(input as syn::LitStr);
1019    str_to_exact_num_literal(&lit.value(), lit.span())
1020        .unwrap_or_else(|e| e.to_compile_error())
1021        .into()
1022}
1023
1024/// Alias for [`exact`], matching dashu-float naming.
1025#[proc_macro]
1026pub fn fbig(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1027    exact(input)
1028}