Here is a bit of Haskell code from my utility library that calculates the Pisano period for any given modulus with reasonable efficiency. All the intelligence is in the pisano* functions. The rest are just my common utility functions I had to throw in to make it work. Math.NumberTheory.Primes.Factorisation is from the excellent arithmoi package available from hackage at [https://hackage.haskell.org/package/arithmoi] and easily installed via cabal.
import qualified Data.Array.Unboxed as ArrayUnboxed
import qualified Data.Set as Set
import qualified Math.NumberTheory.Primes.Factorisation as MNPF
factorise :: Integral a => a -> [(a,Int)]
{-# INLINE factorise #-}
factorise n = map (\(a,b) -> (fromIntegral a,b)) $ MNPF.factorise $ fromIntegral n
divisors :: Integral a => a -> [a]
{-# INLINE divisors #-}
divisors n = map fromIntegral $ Set.toList $ MNPF.divisors $ fromIntegral n
modFibonacciPair :: (Bits b, Num b, Integral b, Integral a) => a -> b -> (a,a)
{-# SPECIALIZE modFibonacciPair :: Int -> Integer -> (Int,Int) #-}
{-# SPECIALIZE modFibonacciPair :: Integer -> Integer -> (Integer,Integer) #-}
{-# SPECIALIZE modFibonacciPair :: Int -> Int -> (Int,Int) #-}
{-# SPECIALIZE modFibonacciPair :: Integer -> Int -> (Integer,Integer) #-}
modFibonacciPair m n
| n==0 = (0,1)
| even n = (rm1,r0)
| otherwise = (r0,rp1)
where
(fk,fkp1) = modFibonacciPair m (quot n 2)
rm1 = (fk*(2*fkp1-fk)) `mod` m
r0 = (fk*fk+fkp1*fkp1) `mod` m
rp1 = (fkp1*(2*fk+fkp1)) `mod` m
pisanoSmall :: ArrayUnboxed.Array Int Word8
pisanoSmall = ArrayUnboxed.listArray (1,47) [1,3,8,6,20,24,16,12,24,60,10,24,28,48,40,24,36,24,18,60,16,30,48,24,100,84,72,48,14,120,30,48,40,36,80,24,76,18,56,60,40,48,88,30,120,48,32,24,112]
pisanoPrimePeriod :: Integral a => a -> a
{-# SPECIALIZE pisanoPrimePeriod :: Int -> Int #-}
{-# SPECIALIZE pisanoPrimePeriod :: Integer -> Integer #-}
pisanoPrimePeriod p
| fromIntegral p<pb = error "pisanoPrimePeriod:: undefined for non-positive arguments"
| fromIntegral p<=pe = fromIntegral $ (ArrayUnboxed.!) pisanoSmall $ fromIntegral p
| otherwise = fromIntegral $ head $ filter (\ d -> modFibonacciPair (fromIntegral p) d==(0,1)) $ sort $ ds
where
(pb,pe) = ArrayUnboxed.bounds $ pisanoSmall
p5 = p `mod` 5
ds = if p5==1 || p5==4
then divisors $ toInteger (p-1)
else map (2*) $ divisors $ toInteger (p+1)
pisanoPeriod :: Integral a => a -> a
{-# SPECIALIZE pisanoPeriod :: Int -> Int #-}
{-# SPECIALIZE pisanoPeriod :: Integer -> Integer #-}
pisanoPeriod n
| fromIntegral n<pb = error "pisanoPeriod:: undefined for non-positive arguments"
| fromIntegral n<=pe = fromIntegral $ (ArrayUnboxed.!) pisanoSmall $ fromIntegral n
| otherwise = fromIntegral $ foldl' lcm 1 [(if a>1 then p^(a-1) else 1)*pisanoPrimePeriod p|(p,a)<-factorise $ fromIntegral n]
where
(pb,pe) = ArrayUnboxed.bounds $ pisanoSmall