#!/usr/bin/ruby

var S = sqrt(1.25)+0.5;
var T = sqrt(1.25)-0.5;
var W = S+T;    #=> sqrt(5);

func fib(n) {
    pow(S, n) - pow(-T, n) / W -> round;
}

func is_fib (i, fib) {
    (((fib * W) + pow(-T, i)).log(S) -> roundf(-i)) == i;
}

#
## log(n*sqrt(5) + (((1-sqrt(5))/2) ^ ((log(n)+(log(5))/2) / (log(1+sqrt(5))-log(2))))) / (log(1+sqrt(5))-log(2))
#
func fib_pos1(n) {
    W*n + pow(-T, log(5 * n**2) / (W+1)) -> log(S).round
}

#
## (log((2/(sqrt(5)-1))^(log((1+sqrt(5))/(5 * n^2)))+sqrt(5)*n))/(log(1/2 (1+sqrt(5))))
#
func fib_pos2(n) {
    W*n + pow(2 / (W-1), log((1+W) / (5 * n**2))) -> log((1+W) / 2) -> round
}

#
## (log(n*sqrt(5) + (((1-sqrt(5))/2) ^ (log(n * sqrt(5)) / log((1+sqrt(5))/2))))) / log((1+sqrt(5))/2)
#
func fib_pos3(n) {
    ((n * W) + pow(-T, (((n * W)).log(S)))).log(S).round;
}

#
## log((W*n + ((-T)**((log(n) + log(5)/2) / S)))) / log(S)
#
func fib_pos4(n) {
    (log((S+T)*n + ((0.5 * (1-(S+T)))**((log(n) + (log(5)/2)) / (log(1+(S+T))-log(2)))))) /
                                (log(1+(S+T))-log(2)) -> round;
}

#
## log(n*sqrt(5)) / log(PHI)
#
func fib_pos5(n) {
    n * sqrt(5) -> log(S).round
}

var fib_pos_funcs = [fib_pos1, fib_pos2, fib_pos3, fib_pos4, fib_pos5];

func is_prob_fib(n) {
    fib(fib_pos_funcs.rand()(n)) == n;
}

[
    [12,  144,                   true],
    [12,  143,                   false],
    [12,  145,                   false],
    [13,  233,                   true],
    [49,  1337,                  false],
    [32,  2178309,               true],
    [100, 354224848179261915074, false],
    [100, 354224848179261915076, false],
    [100, 354224848179261915075, true],
].each { |group|

    var(pos, num, bool) = group...;

    is_fib(pos, num) == bool || die "Validation error (1)!";
    is_prob_fib(num) == bool || die "Validation error (2)!";

    assert_eq(fib_pos_funcs.rand()(num), fib_pos_funcs.rand()(num))
    assert_eq(fib_pos_funcs.rand()(num), pos) if bool

    "%21s is on position %3s in the fibonacci sequence: %s\n" \
        -> printf(num, pos, bool);
}