Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pow and root functions #106

Open
porky11 opened this issue Jun 5, 2022 · 1 comment
Open

Add pow and root functions #106

porky11 opened this issue Jun 5, 2022 · 1 comment

Comments

@porky11
Copy link

porky11 commented Jun 5, 2022

These should be easy to implement.

I created some implementations, but I don't want to check, which of them should be functions, methods or trait implementations.
I made all of them trait implementations for now.
If the value can not

Here are the implementations:

// Pow by ratio
// I only tested this function; I use it in my code
fn pow_ratio<I: PrimInt + Roots>(a: Ratio<I>, b: Ratio<u32>) -> Option<Ratio<I>> {
    let (an, ad) = a.into();
    let (bn, bd) = b.into();

    let pown = an.pow(bn);
    let powd = ad.pow(bn);

    let n = pown.nth_root(bd);
    let d = powd.nth_root(bd);

    // If there does not exist a rational power, return none
    if n.pow(bd) != pown || d.pow(bd) != powd {
        None
    } else {
        Some(Ratio::new(n, d))
    }
}

// Pow by integer
fn pow_integer<I: PrimInt>(a: Ratio<I>, b: u32) -> Ratio<I> {
    let (an, ad) = a.into();

    let pown = an.pow(b);
    let powd = ad.pow(b);

    Ratio::new(n, d)
}

// Root by integer
fn root_integer<I: PrimInt + Roots>(a: Ratio<I>, b: u32) -> Option<Ratio<I>> {
    // add checks if b is zero and either return none or some result for more expressiveness

    let (an, ad) = a.into();

    let n = an.nth_root(b);
    let d = ad.nth_root(b);

    // If there does not exist a rational root, return none
    if n.pow(b) != pown || d.pow(b) != powd {
        None
    } else {
        Some(Ratio::new(n, d))
    }
}

// Root by ratio
fn root_ratio<I: PrimInt + Roots>(a: Ratio<I>, b: Ratio<u32>) -> Option<Ratio<I>> {
    // add checks if b is zero and either return none or some result for more expressiveness

    pow_ratio(a, b.inv())
}

// Square root
fn sqrt<I: PrimInt + Roots>(a: Ratio<I>, b: Ratio<u32>) -> Option<Ratio<I>> {
    // use a version of this function without the zero check for argument b
    root_integer(a, 2)
}

// Cubic root
fn cbrt<I: PrimInt + Roots>(a: Ratio<I>, b: Ratio<u32>) -> Option<Ratio<I>> {
    // use a version of this function without the zero check for argument b
    root_integer(a, 3)
}

pow_integerand root_integer could be implemented using pow_ratio and root_ratio by supplying constant values.
The compiler should be able to optimize, at least if the functions are declared as inline.

Should all of these functions be added anyway?

@cuviper
Copy link
Member

cuviper commented Jun 23, 2022

The pow_integer case should already be covered by Ratio::pow and the many variations of impl Pow.

I'm more hesitant about the roots for pow_ratio. Numerical functions that return an Option are usually called checked_, but that also usually refers to overflowing the type range. Maybe there's another prefix that would convey "exact roots only", but nothing comes to mind.

Sometimes people may want a close approximation instead, like √2 ≌ 99/70, as discussed in #35.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants