aiken-lang / stdlib

The Aiken Standard Library
https://aiken-lang.github.io/stdlib
Apache License 2.0
41 stars 21 forks source link

Raising Rational Fractions to Integer Powers #88

Open mach-diamond opened 3 weeks ago

mach-diamond commented 3 weeks ago

I use this function to exponentiate rational fractions. Simply: (a/b)^c

Any chance we can add this into the library?

/// Integer Power: a rational fraction, x, raised to the integer power y.
///
/// ```aiken
/// expect Some(x) = rational.new(50, 2500)
/// let y = 3
///
/// Some(rational.int_power(x, y)) == rational.new(1, 125000)
/// ```
pub fn int_power(x: Rational, y: Int) -> Rational {

  let Rational { numerator: a, denominator: b } = x
  if y > 0 {
    Rational { numerator: math.pow(a, y), denominator: math.pow(b, y) }
  } else if y < 0 {
    Rational { numerator: math.pow(b, math.abs(y)), denominator: math.pow(a, math.abs(y)) }
  } else {
    Rational { numerator: 1, denominator: 1 }
  }
}

With some tests:


test int_power_neg() {
  expect Some(x) = rational.new(50, 2500)
  let y = -3
  expect Some(z) = rational.new(125000, 1)
  rational.reduce(rational.int_power(x, y)) == z
}
test int_power_pos() {
  expect Some(x) = rational.new(50, 2500)
  let y = 3
  expect Some(z) = rational.new(1, 125000)
  rational.reduce(rational.int_power(x, y)) == z
}
test int_power_zero() {
  expect Some(x) = rational.new(50, 2500)
  let y = 0
  expect Some(z) = rational.new(1, 1)
  rational.reduce(rational.int_power(x, y)) == z
}
logicalmechanism commented 3 weeks ago

You need some additional checks to make this work. If y is negative and a is zero then you are allowing 1/0 to occur and if a is zero and y is zero then you are saying 0^0 is 1.

KtorZ commented 3 weeks ago

As a side note: I don't see strong reasons not to add such a function. Though I agree with @logicalmechanism, the test coverage is pretty thin. We could also leverage prop tests here.

mach-diamond commented 2 weeks ago

Ah yes, nice catch. I actually had a more robust version of this with more test cases, but I unwisely let it live in the rational.ak file. And after bumping the std-lib it got overwritten and lost it lol. And enough time had past where I mostly forgot what I did. Hence the motivation to get this in there natively. I have some pockets of time in next 2 weeks I can use to improve this.