hmt-0.15: Music/Theory/Z12.hs
{-# Language GeneralizedNewtypeDeriving #-}
module Music.Theory.Z12 where
import Data.List {- base -}
-- | Z12 are modulo 12 integers.
--
-- > map signum [-1,0::Z12,1] == [1,0,1]
-- > map abs [-1,0::Z12,1] == [11,0,1]
newtype Z12 = Z12 Int deriving (Eq,Ord,Integral,Real)
-- | Cyclic 'Enum' instance for Z12.
--
-- > pred (0::Z12) == 11
-- > succ (11::Z12) == 0
-- > [9::Z12 .. 3] == [9,10,11,0,1,2,3]
-- > [9::Z12,11 .. 3] == [9,11,1,3]
instance Enum Z12 where
pred = subtract 1
succ = (+) 1
toEnum = fromIntegral
fromEnum = fromIntegral
enumFromThenTo n m o =
let m' = m + (m - n)
in if m' == o then [n,m,o] else n : enumFromThenTo m m' o
enumFromTo n m =
let n' = succ n
in if n' == m then [n,m] else n : enumFromTo n' m
-- | 'Bounded' instance for Z12.
--
-- > [minBound::Z12 .. maxBound] == [0::Z12 .. 11]
instance Bounded Z12 where
minBound = Z12 0
maxBound = Z12 11
-- | The Z12 modulo (ie. @12@) as a 'Z12' value. This is required
-- when lifting generalised @Z@ functions to 'Z12'. It is /not/ the
-- same as writing @12::Z12@.
--
-- > z12_modulo == Z12 12
-- > z12_modulo /= 12
-- > (12::Z12) == 0
-- > show z12_modulo == "(Z12 12)"
z12_modulo :: Z12
z12_modulo = Z12 12
-- | Basis for Z12 show instance.
--
-- > map show [-1,0::Z12,1,z12_modulo] == ["11","0","1","(Z12 12)"]
z12_showsPrec :: Int -> Z12 -> ShowS
z12_showsPrec p (Z12 i) =
let x = showsPrec p i
in if i < 0 || i > 11
then showString "(Z12 " . x . showString ")"
else x
instance Show Z12 where showsPrec = z12_showsPrec
-- | Lift unary function over integers to Z12.
--
-- > lift_unary_Z12 (negate) 7 == 5
lift_unary_Z12 :: (Int -> Int) -> Z12 -> Z12
lift_unary_Z12 f (Z12 a) = Z12 (f a `mod` 12)
-- | Lift unary function over integers to Z12.
--
-- > map (lift_binary_Z12 (+) 4) [1,5,6] == [5,9,10]
lift_binary_Z12 :: (Int -> Int -> Int) -> Z12 -> Z12 -> Z12
lift_binary_Z12 f (Z12 a) (Z12 b) = Z12 (mod (a `f` b) 12)
-- | Raise an error if the internal 'Z12' value is negative.
check_negative :: (Int -> Int) -> Z12 -> Z12
check_negative f (Z12 n) =
if n < 0
then error "check_negative: negative Z12"
else Z12 (f n)
instance Num Z12 where
(+) = lift_binary_Z12 (+)
(-) = lift_binary_Z12 (-)
(*) = lift_binary_Z12 (*)
negate = lift_unary_Z12 negate
fromInteger n = Z12 (fromInteger n `mod` 12)
signum = check_negative signum
abs = check_negative abs
-- | Convert integral to 'Z12'.
--
-- > map to_Z12 [-9,-3,0,13] == [3,9,0,1]
to_Z12 :: Integral i => i -> Z12
to_Z12 = fromIntegral
-- | Convert 'Z12' to integral.
from_Z12 :: Integral i => Z12 -> i
from_Z12 = fromIntegral
-- | Z12 not in set.
--
-- > complement [0,2,4,5,7,9,11] == [1,3,6,8,10]
complement :: [Z12] -> [Z12]
complement = (\\) [0 .. 11]