DoxigAlpha

powi

Returns the power of x raised by the integer y (x^y).

Errors:

  • Overflow: Integer overflow or Infinity
  • Underflow: Absolute value of result smaller than 1

Edge case rules ordered by precedence:

  • powi(T, x, 0) = 1 unless T is i1, i0, u0
  • powi(T, 0, x) = 0 when x > 0
  • powi(T, 0, x) = Overflow
  • powi(T, 1, y) = 1
  • powi(T, -1, y) = -1 for y an odd integer
  • powi(T, -1, y) = 1 unless T is i1, i0, u0
  • powi(T, -1, y) = Overflow
  • powi(T, x, y) = Overflow when y >= @bitSizeOf(x)
  • powi(T, x, y) = Underflow when y < 0

Function parameters

Parameters

#
T:type
x:T
y:T

Returns the power of x raised by the integer y (x^y).

Functions

#
powi
Returns the power of x raised by the integer y (x^y).

Source

Implementation

#
pub fn powi(comptime T: type, x: T, y: T) (error{
    Overflow,
    Underflow,
}!T) {
    const bit_size = @typeInfo(T).int.bits;

    // `y & 1 == 0` won't compile when `does_one_overflow`.
    const does_one_overflow = math.maxInt(T) < 1;
    const is_y_even = !does_one_overflow and y & 1 == 0;

    if (x == 1 or y == 0 or (x == -1 and is_y_even)) {
        if (does_one_overflow) {
            return error.Overflow;
        } else {
            return 1;
        }
    }

    if (x == -1) {
        return -1;
    }

    if (x == 0) {
        if (y > 0) {
            return 0;
        } else {
            // Infinity/NaN, not overflow in strict sense
            return error.Overflow;
        }
    }
    // x >= 2 or x <= -2 from this point
    if (y >= bit_size) {
        return error.Overflow;
    }
    if (y < 0) {
        return error.Underflow;
    }

    // invariant :
    // return value = powi(T, base, exp) * acc;

    var base = x;
    var exp = y;
    var acc: T = if (does_one_overflow) unreachable else 1;

    while (exp > 1) {
        if (exp & 1 == 1) {
            const ov = @mulWithOverflow(acc, base);
            if (ov[1] != 0) return error.Overflow;
            acc = ov[0];
        }

        exp >>= 1;

        const ov = @mulWithOverflow(base, base);
        if (ov[1] != 0) return error.Overflow;
        base = ov[0];
    }

    if (exp == 1) {
        const ov = @mulWithOverflow(acc, base);
        if (ov[1] != 0) return error.Overflow;
        acc = ov[0];
    }

    return acc;
}