--
module Main where
import System.Environment (getArgs)
--
fib0 :: Integer -> Integer
fib0 = fib' 0 1
where
fib' x0 x1 n
| n == 0 = x0
| otherwise = fib' x1 (x0 + x1) (n - 1)
--
data Matrix2x2 = Matrix
{ x00 :: Integer, x01 :: Integer
, x10 :: Integer, x11 :: Integer
}
instance Semigroup Matrix2x2 where
Matrix l00 l01 l10 l11 <> Matrix r00 r01 r10 r11 =
Matrix
{ x00 = l00 * r00 + l01 * r10, x01 = l00 * r01 + l01 * r11
, x10 = l10 * r00 + l11 * r10, x11 = l10 * r01 + l11 * r11
}
instance Monoid Matrix2x2 where
mempty =
Matrix
{ x00 = 1, x01 = 0
, x10 = 0, x11 = 1
}
--
--
mtimes :: Monoid a => Integer -> a -> a
mtimes n x
| n == 0 = mempty
| n > 0 = stimes n x
| otherwise = error "mtimes: negative argument"
stimes :: Semigroup a => Integer -> a -> a
stimes n x
| n == 1 = x
| even n = stimes (n `div` 2) (x <> x)
| otherwise = stimes (n - 1) x <> x
fib :: Integer -> Integer
fib n = x01 (mtimes n matrix)
where
matrix =
Matrix
{ x00 = 0, x01 = 1
, x10 = 1, x11 = 1
}
main :: IO ()
main = do (inp : _) <- getArgs
let arg = read inp
let res = fib arg
let out = "fib " <> show arg <> " = " <> show res
putStrLn out
--
--
t1 = (== 0) $ fib (10^6)
t2 = (== 0) $ fib0 (10^6)