{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

module Taiwan.ID.Digit
  ( Digit (..)
  , fromChar
  , toChar
  , generate
  , FromChar
  , FromNat
  , ToNat
  ) where

import Control.Monad.Random
  ( MonadRandom )
import Data.Finitary
  ( Finitary )
import GHC.Generics
  ( Generic )
import GHC.TypeNats
  ( Nat )
import Taiwan.ID.Utilities
  ( maybeFinitary, randomFinitary )
import Text.Read
  ( Read (readPrec), readMaybe )

import Prelude hiding
  ( fromIntegral )

import qualified Prelude

-- | Represents a single decimal digit in the range @0@ to @9@.
--
data Digit
  = D0 | D1 | D2 | D3 | D4 | D5 | D6 | D7 | D8 | D9
  deriving stock (Digit
Digit -> Digit -> Bounded Digit
forall a. a -> a -> Bounded a
$cminBound :: Digit
minBound :: Digit
$cmaxBound :: Digit
maxBound :: Digit
Bounded, Int -> Digit
Digit -> Int
Digit -> [Digit]
Digit -> Digit
Digit -> Digit -> [Digit]
Digit -> Digit -> Digit -> [Digit]
(Digit -> Digit)
-> (Digit -> Digit)
-> (Int -> Digit)
-> (Digit -> Int)
-> (Digit -> [Digit])
-> (Digit -> Digit -> [Digit])
-> (Digit -> Digit -> [Digit])
-> (Digit -> Digit -> Digit -> [Digit])
-> Enum Digit
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
$csucc :: Digit -> Digit
succ :: Digit -> Digit
$cpred :: Digit -> Digit
pred :: Digit -> Digit
$ctoEnum :: Int -> Digit
toEnum :: Int -> Digit
$cfromEnum :: Digit -> Int
fromEnum :: Digit -> Int
$cenumFrom :: Digit -> [Digit]
enumFrom :: Digit -> [Digit]
$cenumFromThen :: Digit -> Digit -> [Digit]
enumFromThen :: Digit -> Digit -> [Digit]
$cenumFromTo :: Digit -> Digit -> [Digit]
enumFromTo :: Digit -> Digit -> [Digit]
$cenumFromThenTo :: Digit -> Digit -> Digit -> [Digit]
enumFromThenTo :: Digit -> Digit -> Digit -> [Digit]
Enum, Digit -> Digit -> Bool
(Digit -> Digit -> Bool) -> (Digit -> Digit -> Bool) -> Eq Digit
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Digit -> Digit -> Bool
== :: Digit -> Digit -> Bool
$c/= :: Digit -> Digit -> Bool
/= :: Digit -> Digit -> Bool
Eq, (forall x. Digit -> Rep Digit x)
-> (forall x. Rep Digit x -> Digit) -> Generic Digit
forall x. Rep Digit x -> Digit
forall x. Digit -> Rep Digit x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Digit -> Rep Digit x
from :: forall x. Digit -> Rep Digit x
$cto :: forall x. Rep Digit x -> Digit
to :: forall x. Rep Digit x -> Digit
Generic, Eq Digit
Eq Digit =>
(Digit -> Digit -> Ordering)
-> (Digit -> Digit -> Bool)
-> (Digit -> Digit -> Bool)
-> (Digit -> Digit -> Bool)
-> (Digit -> Digit -> Bool)
-> (Digit -> Digit -> Digit)
-> (Digit -> Digit -> Digit)
-> Ord Digit
Digit -> Digit -> Bool
Digit -> Digit -> Ordering
Digit -> Digit -> Digit
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Digit -> Digit -> Ordering
compare :: Digit -> Digit -> Ordering
$c< :: Digit -> Digit -> Bool
< :: Digit -> Digit -> Bool
$c<= :: Digit -> Digit -> Bool
<= :: Digit -> Digit -> Bool
$c> :: Digit -> Digit -> Bool
> :: Digit -> Digit -> Bool
$c>= :: Digit -> Digit -> Bool
>= :: Digit -> Digit -> Bool
$cmax :: Digit -> Digit -> Digit
max :: Digit -> Digit -> Digit
$cmin :: Digit -> Digit -> Digit
min :: Digit -> Digit -> Digit
Ord)
  deriving anyclass Eq Digit
KnownNat (Cardinality Digit)
(Eq Digit, KnownNat (Cardinality Digit)) =>
(Finite (Cardinality Digit) -> Digit)
-> (Digit -> Finite (Cardinality Digit))
-> ((1 <= Cardinality Digit) => Digit)
-> ((1 <= Cardinality Digit) => Digit)
-> (Digit -> Maybe Digit)
-> (Digit -> Maybe Digit)
-> Finitary Digit
(1 <= Cardinality Digit) => Digit
Finite (Cardinality Digit) -> Digit
Digit -> Maybe Digit
Digit -> Finite (Cardinality Digit)
forall a.
(Eq a, KnownNat (Cardinality a)) =>
(Finite (Cardinality a) -> a)
-> (a -> Finite (Cardinality a))
-> ((1 <= Cardinality a) => a)
-> ((1 <= Cardinality a) => a)
-> (a -> Maybe a)
-> (a -> Maybe a)
-> Finitary a
$cfromFinite :: Finite (Cardinality Digit) -> Digit
fromFinite :: Finite (Cardinality Digit) -> Digit
$ctoFinite :: Digit -> Finite (Cardinality Digit)
toFinite :: Digit -> Finite (Cardinality Digit)
$cstart :: (1 <= Cardinality Digit) => Digit
start :: (1 <= Cardinality Digit) => Digit
$cend :: (1 <= Cardinality Digit) => Digit
end :: (1 <= Cardinality Digit) => Digit
$cprevious :: Digit -> Maybe Digit
previous :: Digit -> Maybe Digit
$cnext :: Digit -> Maybe Digit
next :: Digit -> Maybe Digit
Finitary

-- | Arithmetic modulo 10.
--
instance Num Digit where
  Digit
a + :: Digit -> Digit -> Digit
+ Digit
b = Int -> Digit
forall i. Integral i => i -> Digit
fromIntegral (Digit -> Int
forall a. Enum a => a -> Int
fromEnum Digit
a Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Digit -> Int
forall a. Enum a => a -> Int
fromEnum Digit
b)
  Digit
a * :: Digit -> Digit -> Digit
* Digit
b = Int -> Digit
forall i. Integral i => i -> Digit
fromIntegral (Digit -> Int
forall a. Enum a => a -> Int
fromEnum Digit
a Int -> Int -> Int
forall a. Num a => a -> a -> a
* Digit -> Int
forall a. Enum a => a -> Int
fromEnum Digit
b)
  Digit
a - :: Digit -> Digit -> Digit
- Digit
b = Int -> Digit
forall i. Integral i => i -> Digit
fromIntegral (Digit -> Int
forall a. Enum a => a -> Int
fromEnum Digit
a Int -> Int -> Int
forall a. Num a => a -> a -> a
- Digit -> Int
forall a. Enum a => a -> Int
fromEnum Digit
b)
  abs :: Digit -> Digit
abs Digit
a = Digit
a
  fromInteger :: Integer -> Digit
fromInteger = Integer -> Digit
forall i. Integral i => i -> Digit
fromIntegral
  signum :: Digit -> Digit
signum Digit
D0 = Digit
D0
  signum Digit
__ = Digit
D1

instance Read Digit where readPrec :: ReadPrec Digit
readPrec = Integer -> Digit
forall a. Num a => Integer -> a
fromInteger (Integer -> Digit) -> ReadPrec Integer -> ReadPrec Digit
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReadPrec Integer
forall a. Read a => ReadPrec a
readPrec

instance Show Digit where show :: Digit -> String
show = Int -> String
forall a. Show a => a -> String
show (Int -> String) -> (Digit -> Int) -> Digit -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Digit -> Int
forall a. Enum a => a -> Int
fromEnum

-- | Attempts to parse a 'Digit' from a character.
--
-- The 'Char' must be a decimal digit in the range @0@ to @9@.
--
fromChar :: Char -> Maybe Digit
fromChar :: Char -> Maybe Digit
fromChar Char
c = String -> Maybe Natural
forall a. Read a => String -> Maybe a
readMaybe [Char
c] Maybe Natural -> (Natural -> Maybe Digit) -> Maybe Digit
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Natural -> Maybe Digit
forall a. Finitary a => Natural -> Maybe a
maybeFinitary

-- | Converts a 'Digit' to a decimal digit character.
--
toChar :: Digit -> Char
toChar :: Digit -> Char
toChar Digit
digit = case Digit -> String
forall a. Show a => a -> String
show Digit
digit of
  [Char
c] -> Char
c
  String
_ -> String -> Char
forall a. HasCallStack => String -> a
error String
"toChar"

-- | Creates a 'Digit' from an integral number (modulo 10).
--
fromIntegral :: Integral i => i -> Digit
fromIntegral :: forall i. Integral i => i -> Digit
fromIntegral i
i = Int -> Digit
forall a. Enum a => Int -> a
toEnum (i -> Int
forall a b. (Integral a, Num b) => a -> b
Prelude.fromIntegral (i
i i -> i -> i
forall a. Integral a => a -> a -> a
`mod` i
10))

-- | Generates a random digit.
--
generate :: MonadRandom m => m Digit
generate :: forall (m :: * -> *). MonadRandom m => m Digit
generate = m Digit
forall (m :: * -> *) a.
(MonadRandom m, Finitary a, 1 <= Cardinality a) =>
m a
randomFinitary

type family FromChar (c :: Char) :: Maybe Digit where
  FromChar '0' = Just D0
  FromChar '1' = Just D1
  FromChar '2' = Just D2
  FromChar '3' = Just D3
  FromChar '4' = Just D4
  FromChar '5' = Just D5
  FromChar '6' = Just D6
  FromChar '7' = Just D7
  FromChar '8' = Just D8
  FromChar '9' = Just D9
  FromChar _   = Nothing

type family FromNat (n :: Nat) :: Maybe Digit where
  FromNat 0 = Just D0
  FromNat 1 = Just D1
  FromNat 2 = Just D2
  FromNat 3 = Just D3
  FromNat 4 = Just D4
  FromNat 5 = Just D5
  FromNat 6 = Just D6
  FromNat 7 = Just D7
  FromNat 8 = Just D8
  FromNat 9 = Just D9
  FromNat _ = Nothing

type family ToNat (d :: Digit) :: Nat where
  ToNat D0 = 0
  ToNat D1 = 1
  ToNat D2 = 2
  ToNat D3 = 3
  ToNat D4 = 4
  ToNat D5 = 5
  ToNat D6 = 6
  ToNat D7 = 7
  ToNat D8 = 8
  ToNat D9 = 9