#!/usr/bin/ruby

#
## Tests for ilog(), ilog2() and ilog10().
#

for n in (1..100) {

    assert_eq(ilog(3**n, 3), n)
    assert_eq(ilog2(2**n), n)

    assert_eq(ilog10(10**n), n)
    assert_eq(ilog10(10**n + 1), n)
    assert_eq(ilog10(10**n - 1), n-1)

    assert_eq(ilog(3**n), log(3**n).int)
    assert_eq(ilog2(exp(n)), log2(exp(n)).int)
    assert_eq(ilog(exp(n), 3), log(exp(n), 3).int)

    assert_eq(ilog(100**n, 100), n)
    assert_eq(ilog(100**n + 1, 100), n)
    assert_eq(ilog(100**n - 1, 100), n-1)
}

assert_eq(gather {
    var x = 0
    { take(x.next_pow!(3)) } * 5
}, [1, 3, 9, 27, 81])

assert_eq(gather {
    var x = 0
    { take(x.next_pow!(4)) } * 5
}, [1, 4, 16, 64, 256])

assert_eq(gather {
    var x = 0
    { take(x.next_pow!(2)) } * 5
}, [1, 2, 4, 8, 16])

assert_eq(2**4096     -> next_pow(2), 2**4097)
assert_eq(2**4096 - 1 -> next_pow(2), 2**4096)
assert_eq(2**4096 + 1 -> next_pow(2), 2**4097)

assert_eq(3**2000     -> next_pow(3), 3**2001)
assert_eq(3**2000 - 1 -> next_pow(3), 3**2000)
assert_eq(3**2000 + 1 -> next_pow(3), 3**2001)

assert_eq(
    @(1..10).map_reduce{|a| a.next_pow(2) },
    0..9 -> map {|n| 2**n }
)

assert_eq(99**4096     -> prev_pow(99), 99**4095)
assert_eq(99**4096 - 1 -> prev_pow(99), 99**4095)
assert_eq(99**4096 + 1 -> prev_pow(99), 99**4096)

assert_eq(102**2000     -> prev_pow(102), 102**1999)
assert_eq(102**2000 - 1 -> prev_pow(102), 102**1999)
assert_eq(102**2000 + 1 -> prev_pow(102), 102**2000)

assert_eq(
    @(3..10).map_reduce{|a| a.next_pow(3) }.map { .prev_pow(3) },
    0..7 -> map {|n| 3**n },
)

assert_eq(4.prev_pow(3), 3)
assert_eq(3.prev_pow(3), 1)
assert_eq(2.prev_pow(3), 1)
assert(1.prev_pow(3).is_nan)

assert_eq(0.next_pow(3), 1)
assert_eq(1.next_pow(3), 3)
assert_eq(2.next_pow(3), 3)
assert_eq(3.next_pow(3), 9)

assert_eq(ilog(2147483646, 1319909920001892772), 0)
assert_eq(ilog(1319909920001892772, 2147483646), 1)

assert_eq(ilog(2**64, 2**32 - 1), 2)
assert_eq(ilog(2**64 - 1, 2**32), 1)
assert_eq(ilog(2**64, 2**32 + 1), 1)

assert_eq(ilog(2**64, 2**32), 2)
assert_eq(ilog(2**64 - 1, 2**32 - 1), 2)

assert_eq(ilog(18446744073709551614, 2**32 - 1), 2)
assert_eq(ilog(18446744073709551614, 2**32), 1)

assert_eq({ .ilog2 }.map(1..20),    { .log2.floor }.map(1..20))
assert_eq({ .ilog(3) }.map(1..20),  { .log(3).floor }.map(1..20))
assert_eq({ .ilog10 }.map(1..20),   { .log10.floor }.map(1..20))

assert_eq(ilog(2, 2), 1)

# Run some random tests
for n in (1..66), k in (1..66) {

    var a = irand(1, 1<<n)
    var b = irand(2, 1<<k)

    try  {
        var x = a.ilog(b)
        var y = a.log(b).floor

        if (ipow(b, x) == a) {    # perfect power
            ## ok
        }
        else {
            assert_eq(x, y)
        }
    }
    catch {|m|
        die "Failed for ilog(#{a}, #{b}) with error: #{m}"
    }
}

say "** Test passed!"