Task: Fibonacci matrix-exponentiation




Go: package main

import (
    "fmt"
    big "github.com/ncw/gmp"
    "time"
)

type vector = []*big.Int
type matrix []vector

var (
    zero = new(big.Int)
    one  = big.NewInt(1)
)

func (m1 matrix) mul(m2 matrix) matrix {
    rows1, cols1 := len(m1), len(m1[0])
    rows2, cols2 := len(m2), len(m2[0])
    if cols1 != rows2 {
        panic("Matrices cannot be multiplied.")
    }
    result := make(matrix, rows1)
    temp := new(big.Int)
    for i := 0; i < rows1; i++ {
        result[i] = make(vector, cols2)
        for j := 0; j < cols2; j++ {
            result[i][j] = new(big.Int)
            for k := 0; k < rows2; k++ {
                temp.Mul(m1[i][k], m2[k][j])
                result[i][j].Add(result[i][j], temp)
            }
        }
    }
    return result
}

func identityMatrix(n uint64) matrix {
    if n < 1 {
        panic("Size of identity matrix can't be less than 1")
    }
    ident := make(matrix, n)
    for i := uint64(0); i < n; i++ {
        ident[i] = make(vector, n)
        for j := uint64(0); j < n; j++ {
            ident[i][j] = new(big.Int)
            if i == j {
                ident[i][j].Set(one)
            }
        }
    }
    return ident
}

func (m matrix) pow(n *big.Int) matrix {
    le := len(m)
    if le != len(m[0]) {
        panic("Not a square matrix")
    }
    switch {
    case n.Cmp(zero) == -1:
        panic("Negative exponents not supported")
    case n.Cmp(zero) == 0:
        return identityMatrix(uint64(le))
    case n.Cmp(one) == 0:
        return m
    }
    pow := identityMatrix(uint64(le))
    base := m
    e := new(big.Int).Set(n)
    temp := new(big.Int)
    for e.Cmp(zero) > 0 {
        temp.And(e, one)
        if temp.Cmp(one) == 0 {
            pow = pow.mul(base)
        }
        e.Rsh(e, 1)
        base = base.mul(base)
    }
    return pow
}

func fibonacci(n *big.Int) *big.Int {
    if n.Cmp(zero) == 0 {
        return zero
    }
    m := matrix{{one, one}, {one, zero}}
    m = m.pow(n.Sub(n, one))
    return m[0][0]
}

func commatize(n uint64) string {
    s := fmt.Sprintf("%d", n)
    le := len(s)
    for i := le - 3; i >= 1; i -= 3 {
        s = s[0:i] + "," + s[i:]
    }
    return s
}

func main() {
    start := time.Now()
    n := new(big.Int)
    for i := uint64(10); i <= 1e7; i *= 10 {
        n.SetUint64(i)
        s := fibonacci(n).String()
        fmt.Printf("The digits of the %sth Fibonacci number (%s) are:\n",
            commatize(i), commatize(uint64(len(s))))
        if len(s) > 20 {
            fmt.Printf("  First 20 : %s\n", s[0:20])
            if len(s) < 40 {
                fmt.Printf("  Final %-2d : %s\n", len(s)-20, s[20:])
            } else {
                fmt.Printf("  Final 20 : %s\n", s[len(s)-20:])
            }
        } else {
            fmt.Printf("  All %-2d   : %s\n", len(s), s)
        }
        fmt.Println()
    }

    sfxs := []string{"nd", "th"}
    for i, e := range []uint{16, 32} {
        n.Lsh(one, e)
        s := fibonacci(n).String()
        fmt.Printf("The digits of the 2^%d%s Fibonacci number (%s) are:\n", e, sfxs[i],
            commatize(uint64(len(s))))
        fmt.Printf("  First 20 : %s\n", s[0:20])
        fmt.Printf("  Final 20 : %s\n", s[len(s)-20:])
        fmt.Println()
    }

    fmt.Printf("Took %s\n\n", time.Since(start))
}




Python: class Head():
    def __init__(self, lo, hi=None, shift=0):
        if hi is None: hi = lo

        d = hi - lo
        ds, ls, hs = str(d), str(lo), str(hi)

        if d and len(ls) > len(ds):
            assert(len(ls) - len(ds) + 1 > 21)
            lo = int(str(lo)[:len(ls) - len(ds) + 1])
            hi = int(str(hi)[:len(hs) - len(ds) + 1]) + 1
            shift += len(ds) - 1
        elif len(ls) > 100:
            lo = int(str(ls)[:100])
            hi = lo + 1
            shift = len(ls) - 100

        self.lo, self.hi, self.shift = lo, hi, shift

    def __mul__(self, other):
        lo = self.lo*other.lo
        hi = self.hi*other.hi
        shift = self.shift + other.shift

        return Head(lo, hi, shift)

    def __add__(self, other):
        if self.shift < other.shift:
            return other + self

        sh = self.shift - other.shift
        if sh >= len(str(other.hi)):
            return Head(self.lo, self.hi, self.shift)

        ls = str(other.lo)
        hs = str(other.hi)

        lo = self.lo + int(ls[:len(ls)-sh])
        hi = self.hi + int(hs[:len(hs)-sh])

        return Head(lo, hi, self.shift)

    def __repr__(self):
        return str(self.hi)[:20]

class Tail():
    def __init__(self, v):
        self.v = int(f'{v:020d}'[-20:])

    def __add__(self, other):
        return Tail(self.v + other.v)

    def __mul__(self, other):
        return Tail(self.v*other.v)

    def __repr__(self):
        return f'{self.v:020d}'[-20:]
        
def mul(a, b):
    return a[0]*b[0] + a[1]*b[1], a[0]*b[1] + a[1]*b[2], a[1]*b[1] + a[2]*b[2]

def fibo(n, cls):
    n -= 1
    zero, one = cls(0), cls(1)
    m = (one, one, zero)
    e = (one, zero, one)

    while n:
        if n&1: e = mul(m, e)
        m = mul(m, m)
        n >>= 1

    return f'{e[0]}'

for i in range(2, 10):
    n = 10**i
    print(f'10^{i} :', fibo(n, Head), '...', fibo(n, Tail))

for i in range(3, 8):
    n = 2**i
    s = f'2^{n}'
    print(f'{s:5s}:', fibo(2**n, Head), '...', fibo(2**n, Tail))