Fibonacci matrix-exponentiation

Computing the n-th Fibonacci number, using matrix-exponentiation (this function is also built-in):

func fibonacci(n) {
    ([[1,1],[1,0]]**n)[0][1]
}

say 15.of(fibonacci)    #=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]

First and last 20 digits of the n-th Fibonacci number:

for n in (16, 32) {

    var f = fibonacci(2**n)

    with (20) {|k|
        var a = (f // 10**(f.ilog10 - k + 1))
        var b = (f % 10**k)
        say "F(2^#{n}) = #{a} ... #{b}"
    }
}

Output:

F(2^16) = 73199214460290552832 ... 97270190955307463227
F(2^32) = 61999319689381859818 ... 39623735538208076347

More efficient approach, using Binet's formula for computing the first k digits, combined with the built-in method fibmod(n,m) for computing the last k digits:

func binet_approx(n) {
    const PHI =  (1.25.sqrt + 0.5)
    const IHP = -(1.25.sqrt - 0.5)
    (log(PHI)*n - log(PHI-IHP))
}

func nth_fib_first_k_digits(n,k) {
    var f = binet_approx(n)
    floor(exp(f - log(10)*(floor(f / log(10) + 1))) * 10**k)
}

func nth_fib_last_k_digits(n,k) {
    fibmod(n, 10**k)
}

for n in (16, 32, 64) {
    var first20 = nth_fib_first_k_digits(2**n, 20)
    var last20  = nth_fib_last_k_digits(2**n, 20)
    say "F(2^#{n}) = #{first20} ... #{last20}"
}

Output:

F(2^16) = 73199214460290552832 ... 97270190955307463227
F(2^32) = 61999319689381859818 ... 39623735538208076347
F(2^64) = 11175807536929528424 ... 17529800348089840187