{-# LANGUAGE CPP #-}
module Math.NumberTheory.Recurrences.Linear
( factorial
, fibonacci
, fibonacciPair
, lucas
, lucasPair
, generalLucas
) where
#include "MachDeps.h"
import Data.Bits
import Numeric.Natural
factorial :: (Num a, Enum a) => [a]
factorial = scanl (*) 1 [1..]
{-# SPECIALIZE factorial :: [Int] #-}
{-# SPECIALIZE factorial :: [Word] #-}
{-# SPECIALIZE factorial :: [Integer] #-}
{-# SPECIALIZE factorial :: [Natural] #-}
fibonacci :: Num a => Int -> a
fibonacci = fst . fibonacciPair
{-# SPECIALIZE fibonacci :: Int -> Int #-}
{-# SPECIALIZE fibonacci :: Int -> Word #-}
{-# SPECIALIZE fibonacci :: Int -> Integer #-}
{-# SPECIALIZE fibonacci :: Int -> Natural #-}
fibonacciPair :: Num a => Int -> (a, a)
fibonacciPair n
| n < 0 = let (f,g) = fibonacciPair (-(n+1)) in if testBit n 0 then (g, -f) else (-g, f)
| n == 0 = (0, 1)
| otherwise = look (WORD_SIZE_IN_BITS - 2)
where
look k
| testBit n k = go (k-1) 0 1
| otherwise = look (k-1)
go k g f
| k < 0 = (f, f+g)
| testBit n k = go (k-1) (f*(f+shiftL1 g)) ((f+g)*shiftL1 f + g*g)
| otherwise = go (k-1) (f*f+g*g) (f*(f+shiftL1 g))
{-# SPECIALIZE fibonacciPair :: Int -> (Int, Int) #-}
{-# SPECIALIZE fibonacciPair :: Int -> (Word, Word) #-}
{-# SPECIALIZE fibonacciPair :: Int -> (Integer, Integer) #-}
{-# SPECIALIZE fibonacciPair :: Int -> (Natural, Natural) #-}
lucas :: Num a => Int -> a
lucas = fst . lucasPair
{-# SPECIALIZE lucas :: Int -> Int #-}
{-# SPECIALIZE lucas :: Int -> Word #-}
{-# SPECIALIZE lucas :: Int -> Integer #-}
{-# SPECIALIZE lucas :: Int -> Natural #-}
lucasPair :: Num a => Int -> (a, a)
lucasPair n
| n < 0 = let (f,g) = lucasPair (-(n+1)) in if testBit n 0 then (-g, f) else (g, -f)
| n == 0 = (2, 1)
| otherwise = look (WORD_SIZE_IN_BITS - 2)
where
look k
| testBit n k = go (k-1) 0 1
| otherwise = look (k-1)
go k g f
| k < 0 = (shiftL1 g + f,g+3*f)
| otherwise = go (k-1) g' f'
where
(f',g')
| testBit n k = (shiftL1 (f*(f+g)) + g*g,f*(shiftL1 g + f))
| otherwise = (f*(shiftL1 g + f),f*f+g*g)
{-# SPECIALIZE lucasPair :: Int -> (Int, Int) #-}
{-# SPECIALIZE lucasPair :: Int -> (Word, Word) #-}
{-# SPECIALIZE lucasPair :: Int -> (Integer, Integer) #-}
{-# SPECIALIZE lucasPair :: Int -> (Natural, Natural) #-}
generalLucas :: Num a => a -> a -> Int -> (a, a, a, a)
generalLucas p q k
| k < 0 = error "generalLucas: negative index"
| k == 0 = (0,1,2,p)
| otherwise = look (WORD_SIZE_IN_BITS - 2)
where
look i
| testBit k i = go (i-1) 1 p p q
| otherwise = look (i-1)
go i un un1 vn qn
| i < 0 = (un, un1, vn, p*un1 - shiftL1 (q*un))
| testBit k i = go (i-1) (un1*vn-qn) ((p*un1-q*un)*vn - p*qn) ((p*un1 - (2*q)*un)*vn - p*qn) (qn*qn*q)
| otherwise = go (i-1) (un*vn) (un1*vn-qn) (vn*vn - 2*qn) (qn*qn)
{-# SPECIALIZE generalLucas :: Int -> Int -> Int -> (Int, Int, Int, Int) #-}
{-# SPECIALIZE generalLucas :: Word -> Word -> Int -> (Word, Word, Word, Word) #-}
{-# SPECIALIZE generalLucas :: Integer -> Integer -> Int -> (Integer, Integer, Integer, Integer) #-}
{-# SPECIALIZE generalLucas :: Natural -> Natural -> Int -> (Natural, Natural, Natural, Natural) #-}
shiftL1 :: Num a => a -> a
shiftL1 n = n + n