{-# Language DataKinds #-}
{- | Z12

Z12 are modulo 12 integers.

> map signum [-1,0::Z12,1] == [1,0,1]
> map abs [-1,0::Z12,1] == [11,0,1]

Aspects of the 'Enum' instance are cyclic.

> pred (0::Z12) == 11
> succ (11::Z12) == 0

'Bounded' works

> [minBound::Z12 .. maxBound] == [0::Z12 .. 11]

-}
module Music.Theory.Z12 where

import Data.Char {- base -}
import Data.List {- base -}
import qualified Data.Modular as M {- modular-arithmetic -}
import qualified GHC.TypeLits as L {- base -}

import qualified Music.Theory.List as T {- hmt -}

-- | 'Mod' 'Int'.
type Z n = M.Mod Int n

-- | 'Z' 12.
--
-- > map negate [0::Z12 .. 0xB] == [0,0xB,0xA,9,8,7,6,5,4,3,2,1]
-- > map (+ 5) [0::Z12 .. 11] == [5,6,7,8,9,0xA,0xB,0,1,2,3,4]
type Z12 = M.Mod Int 12

-- | Cyclic form of 'enumFromThenTo'.
--
-- > [9::Z12,11 .. 3] == []
-- > enumFromThenTo_cyc (9::Z12) 11 3 == [9,11,1,3]
enumFromThenTo_cyc :: L.KnownNat n => Z n -> Z n -> Z n -> [Z n]
enumFromThenTo_cyc n m o =
    let m' = m + (m - n)
    in case compare m' o of
         LT -> n : enumFromThenTo_cyc m m' o
         EQ -> [n,m,o]
         GT -> [n,m]

-- | Cyclic form of 'enumFromTo'.
--
-- > [9::Z12 .. 3] == []
-- > enumFromTo_cyc (9::Z12) 3 == [9,10,11,0,1,2,3]
enumFromTo_cyc :: L.KnownNat n => Z n -> Z n -> [Z n]
enumFromTo_cyc n m =
    let n' = succ n
    in if n' == m then [n,m] else n : enumFromTo_cyc n' m

{-
-}

-- | Convert integral to 'Z12'.
--
-- > map to_Z12 [-9,-3,0,13] == [3,9,0,1]
to_Z12 :: Integral i => i -> Z12
to_Z12 = M.toMod . fromIntegral

int_to_Z12 :: Int -> Z12
int_to_Z12 = to_Z12

-- | Convert 'Z12' to integral.
from_Z12 :: Integral i => Z12 -> i
from_Z12 = fromIntegral . M.unMod

int_from_Z12 :: Z12 -> Int
int_from_Z12 = from_Z12

-- | Z12 not in set.
--
-- > complement [0,2,4,5,7,9,11] == [1,3,6,8,10]
complement :: [Z12] -> [Z12]
complement = (\\) [0 .. 11]

-- | Z12 to character (10 -> A, 11 -> B).
--
-- > map z12_to_char [0 .. 11] == "0123456789AB"
z12_to_char :: Z12 -> Char
z12_to_char = toUpper . intToDigit . M.unMod

-- | Z12 to character (10 -> A, 11 -> B).
--
-- > map char_to_z12 "0123456789AB" == [0..11]
char_to_z12 :: Char -> Z12
char_to_z12 = to_Z12 . digitToInt

-- | Unordered set notation (braces).
--
-- > z12_set_pp [0,1,3] == "{013}"
z12_set_pp :: [Z12] -> String
z12_set_pp = T.bracket ('{','}') . map z12_to_char

-- | Ordered sequence notation (angle brackets).
--
-- > z12_seq_pp [0,1,3] == "<013>"
z12_seq_pp :: [Z12] -> String
z12_seq_pp = T.bracket ('<','>') . map z12_to_char

-- | Ordered vector notation (square brackets).
--
-- > z12_vec_pp [0,1,3] == "[013]"
z12_vec_pp :: [Z12] -> String
z12_vec_pp = T.bracket ('[',']') . map z12_to_char