Refactor toDecimal to extract a helper function for arbitrary periodic sequences.

This commit is contained in:
jdmcdona 2015-09-14 11:54:58 -05:00
parent f3f4b456ab
commit fff599ee09
1 changed files with 21 additions and 10 deletions

View File

@ -1,4 +1,5 @@
{-# LANGUAGE BangPatterns, ScopedTypeVariables, FlexibleContexts, StandaloneDeriving #-} {-# LANGUAGE BangPatterns, ScopedTypeVariables, FlexibleContexts #-}
{-# LANGUAGE LambdaCase, StandaloneDeriving #-}
module Euler module Euler
( Digit ( Digit
@ -18,6 +19,8 @@ module Euler
, RangeIx(..) , RangeIx(..)
, divisors , divisors
, properDivisors , properDivisors
, runPeriodicState
, unfoldPeriodicState
, toDecimal , toDecimal
, fromDecimal , fromDecimal
, toDigits , toDigits
@ -29,6 +32,9 @@ module Euler
, module Control.Applicative , module Control.Applicative
, module Control.Arrow , module Control.Arrow
, module Control.Monad , module Control.Monad
, module Control.Monad.ST
, module Control.Monad.State
, module Control.Monad.Writer
, module Data.Function , module Data.Function
, module Data.List , module Data.List
, module Data.Ratio , module Data.Ratio
@ -40,6 +46,7 @@ import Control.Applicative
import Control.Arrow import Control.Arrow
import Control.Monad import Control.Monad
import Control.Monad.ST import Control.Monad.ST
import Control.Monad.State
import Control.Monad.Writer import Control.Monad.Writer
import Data.Array.ST import Data.Array.ST
import Data.Array.Unboxed import Data.Array.Unboxed
@ -124,20 +131,24 @@ properDivisors :: Integral a => a -> [a]
properDivisors n | n < 1 = [] properDivisors n | n < 1 = []
properDivisors n = nub $ 1 : concat [ [m, q] | m <- takeWhile (\x -> x^2 <= n) [2..], let (q, r) = n `divMod` m, r == 0 ] properDivisors n = nub $ 1 : concat [ [m, q] | m <- takeWhile (\x -> x^2 <= n) [2..], let (q, r) = n `divMod` m, r == 0 ]
runPeriodicState :: Eq s => State s (Maybe a) -> s -> (([a], [a]), s)
runPeriodicState m s0 = runState (go []) s0
where
go ps = get >>= \s -> case break (\(s', _) -> s' == s) ps of
(as, []) -> m >>= maybe (return (map snd as, [])) (\x -> go $ ps ++ [(s, x)])
(as, bs) -> return (map snd as, map snd bs)
unfoldPeriodicState :: Eq s => State s (Maybe a) -> s -> ([a], [a])
unfoldPeriodicState m s0 = fst $ runPeriodicState m s0
toDecimal :: Rational -> Decimal toDecimal :: Rational -> Decimal
toDecimal rat = Decimal q ps rs toDecimal rat = Decimal q ps rs
where where
(n, m) = (numerator rat, denominator rat) (n, m) = (numerator rat, denominator rat)
(q, r) = n `divMod` m (q, r) = n `divMod` m
(ps, rs) = toDecimal' r m (ps, rs) = flip unfoldPeriodicState r $ get >>= \case
0 -> return Nothing
toDecimal' :: Integer -> Integer -> ([Digit], [Digit]) x -> Just . fromIntegral <$> state (const $ (10 * x) `divMod` m)
toDecimal' n m = first (map fst) $ second (map fst) $ collect [] xs
where
xs = [ first fromInteger ((10*x) `divMod` m)
| x <- takeWhile (/= 0) $ (n:) $ map snd xs ]
collect ps [] = (ps, [])
collect ps (x:xs) = if x `elem` ps then break (== x) ps else collect (ps ++ [x]) xs
fromDecimal :: Decimal -> Rational fromDecimal :: Decimal -> Rational
fromDecimal (Decimal n ps rs) = (n % 1) + (p + r) * (1 % 10 ^ length ps) fromDecimal (Decimal n ps rs) = (n % 1) + (p + r) * (1 % 10 ^ length ps)