cas_compute/funcs/
power.rs

1//! Functions related to powers, exponentiation, and roots.
2
3use cas_attrs::builtin;
4use crate::consts::{I, TAU, TEN};
5use crate::primitive::{complex, float};
6use rug::{ops::Pow as _, Complex, Float};
7
8/// Builds a function whose `rug` implementation has the same name as the given function.
9macro_rules! simple {
10    ($($name:ident $upname:ident; $doc:literal),* $(,)?) => {
11        $(
12            #[doc = $doc]
13            #[derive(Debug)]
14            pub struct $upname;
15
16            #[cfg_attr(feature = "numerical", builtin)]
17            impl $upname {
18                pub fn eval_static(n: Complex) -> Complex {
19                    n.$name()
20                }
21            }
22        )*
23    };
24}
25
26/// Returns `arg[0] * 10 ^ arg[1]`. This provides a convenient way to express numbers in scientific
27/// notation.
28#[derive(Debug)]
29pub struct Scientific;
30
31#[cfg_attr(feature = "numerical", builtin)]
32impl Scientific {
33    pub fn eval_static(a: Complex, b: Complex) -> Complex {
34        a * complex(&*TEN).pow(b)
35    }
36}
37
38/// The logarithm function to an arbitrary base, `log(x, base = 10)`.
39#[derive(Debug)]
40pub struct Log;
41
42#[cfg_attr(feature = "numerical", builtin)]
43impl Log {
44    pub fn eval_static(n: Complex, base: Option<Complex>) -> Complex {
45        let base = base.unwrap_or(complex(&*TEN));
46        n.ln() / base.ln()
47    }
48}
49
50/// Basic power function, `pow(x, y)`.
51#[derive(Debug)]
52pub struct Pow;
53
54#[cfg_attr(feature = "numerical", builtin)]
55impl Pow {
56    pub fn eval_static(x: Complex, y: Complex) -> Complex {
57        x.pow(y)
58    }
59}
60
61/// The cube root function, `cbrt(x)`.
62///
63/// This function returns the principal cube root of `x`.
64#[derive(Debug)]
65pub struct Cbrt;
66
67#[cfg_attr(feature = "numerical", builtin)]
68impl Cbrt {
69    pub fn eval_static(n: Complex) -> Complex {
70        let one_third = float(3.0).recip();
71        if n.real().is_sign_positive() {
72            n.pow(one_third)
73        } else {
74            // alternate form for negative numbers which chooses the branch closest to the real axis
75            let (abs, arg) = (
76                complex(n.abs_ref()).into_real_imag().0,
77                n.arg().into_real_imag().0,
78            );
79            let lhs = abs.cbrt();
80            let rhs = complex(one_third * (arg + &*TAU) * &*I).exp();
81            lhs * rhs
82        }
83    }
84}
85
86/// Returns the `n`th root of `x`.
87#[derive(Debug)]
88pub struct Root;
89
90#[cfg_attr(feature = "numerical", builtin)]
91impl Root {
92    pub fn eval_static(x: Complex, n: Complex) -> Complex {
93        x.pow(n.recip())
94    }
95}
96
97/// Returns the hypothenuse of a right triangle with sides `a` and `b`.
98#[derive(Debug)]
99pub struct Hypot;
100
101#[cfg_attr(feature = "numerical", builtin)]
102impl Hypot {
103    pub fn eval_static(a: Float, b: Float) -> Float {
104        a.hypot(&b)
105    }
106}
107
108simple! {
109    exp Exp; "The exponential function, `e ^ x`.",
110    ln Ln; "The natural logarithm, `ln(x)`.",
111    sqrt Sqrt; "The square root function, `sqrt(x)`.",
112}