module Data.Digit.Natural ( _NaturalDigits , naturalToDigits , digitsToNatural ) where import Prelude (Int, error, fromIntegral, maxBound, (*), (+), (-), (>), (^)) import Control.Category ((.)) import Control.Lens (Prism', ifoldrM, prism', ( # )) import Data.Foldable (length) import Data.Function (($)) import Data.Functor (fmap, (<$>)) import Data.Semigroup ((<>)) import Data.List (replicate) import Data.List.NonEmpty (NonEmpty ((:|))) import qualified Data.List.NonEmpty as NE import Data.Maybe (Maybe (..)) import Data.Digit.Decimal import Data.Digit.Integral (integralDecimal) import Numeric.Natural (Natural) import Data.Scientific (toDecimalDigits) -- | -- -- >>> _NaturalDigits # 0 -- DecDigit0 :| [] -- -- >>> _NaturalDigits # 1 -- DecDigit1 :| [] -- -- >>> _NaturalDigits # 922 -- DecDigit9 :| [DecDigit2,DecDigit2] -- -- >>> (DecDigit9 :| [DecDigit2,DecDigit2]) ^? _NaturalDigits -- Just 922 -- -- >>> (DecDigit1 :| []) ^? _NaturalDigits -- Just 1 -- -- prop> \x -> digitsToNatural ( naturalToDigits x ) == Just x -- _NaturalDigits :: Prism' (NonEmpty DecDigit) Natural _NaturalDigits = prism' naturalToDigits digitsToNatural -- | -- -- >>> naturalDigits 0 -- DecDigit0 :| [] -- -- >>> naturalDigits 9 -- DecDigit9 :| [] -- -- >>> naturalDigits 393 -- DecDigit3 :| [DecDigit9,DecDigit3] -- naturalToDigits :: Natural -> NonEmpty DecDigit naturalToDigits n = case toDecimalDigits $ fromIntegral n of -- toDecimalDigits :: n -> ([n],n) -- toDecimalDigits 0 = ([0],0) -- toDecimalDigits (-0) = ([0],0) -- toDecimalDigits (-1) = ([-1],1) ([], _ ) -> error "Data.Scientific.toDecimalDigits is no longer correct!" (x:xs, eXP) -> g x :| (g <$> xs) <> t (x:xs) eXP where t allDigs eXP = replicate (eXP - length allDigs) (d0 # ()) -- EWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW! -- But you can't reach this point unless you have a non-zero absolute integral value. So... I dunno. g 0 = d0 # () g 1 = d1 # () g 2 = d2 # () g 3 = d3 # () g 4 = d4 # () g 5 = d5 # () g 6 = d6 # () g 7 = d7 # () g 8 = d8 # () g 9 = d9 # () g _ = error "The universe now has more than ten digits." -- | Create a number from a list of digits with the integer bounds of the machine. -- -- >>> digitsToNatural (DecDigit3 :| [DecDigit4]) -- Just 34 -- -- >>> digitsToNatural (DecDigit0 :| []) -- Just 0 -- -- >>> digitsToNatural (naturalToDigits (maxBound :: Natural)) -- Just 9223372036854775807 -- -- >>> digitsToNatural (naturalToDigits $ (maxBound :: Natural) + 1) -- Nothing digitsToNatural :: NonEmpty DecDigit -> Maybe Natural digitsToNatural = fmap fromIntegral . ifoldrM f 0 . NE.reverse where f :: Int -> DecDigit -> Int -> Maybe Int f i d curr = let next = (integralDecimal # d) * (10 ^ i) in if curr > maxBound - next then Nothing else Just (curr + next)