Given a Fibonacci number, give the previous Fibonacci number. If the number given is not a Fibonacci number, return -1.
From rendezvous with cassidoo, June 20 2022
defmodule Fib do
@golden_ratio (1 + :math.sqrt(5)) / 2
def prev(fib) do
if fib?(fib), do: round(fib / @golden_ratio), else: -1
end
defp fib?(n) do
# Binet's formula
x = 5 * n ** 2
square?(x + 4) or square?(x - 4)
end
defp square?(n) do
x = :math.sqrt(n)
floor(x) == x
end
end
ExUnit.start(auto_run: false)
defmodule FibTest do
use ExUnit.Case, async: false
describe "prev/1" do
test "returns 0 if n is 0" do
assert Fib.prev(0) == 0
end
test "returns 1 if n is 1" do
assert Fib.prev(1) == 1
end
test "returns previous fibonacci number if n is a fibonacci number" do
[2, 3, 5, 8, 13, 21, 34, 55, 89, 144]
|> Enum.reverse()
|> Enum.chunk_every(2, 1, :discard)
|> Enum.map(&List.to_tuple/1)
|> Enum.each(fn {n, prev_n} -> assert Fib.prev(n) == prev_n end)
end
test "returns -1 if n is NOT a fibonacci number" do
[4, 6, 7, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 22]
|> Enum.each(fn n -> assert Fib.prev(n) == -1 end)
end
end
end
ExUnit.run()