-- | Serial (ordered) pitch-class operations on 'Z12'.
module Music.Theory.Z12.SRO where

import Data.List
import qualified Music.Theory.List as T
import Music.Theory.Z12

-- | Transpose /p/ by /n/.
--
-- > tn 4 [1,5,6] == [5,9,10]
tn :: Z12 -> [Z12] -> [Z12]
tn n = fmap (+ n)

-- | Invert /p/ about /n/.
--
-- > invert 6 [4,5,6] == [8,7,6]
-- > invert 0 [0,1,3] == [0,11,9]
invert :: Z12 -> [Z12] -> [Z12]
invert n = fmap (\p -> n - (p - n))

-- | Composition of 'invert' about @0@ and 'tn'.
--
-- > tni 4 [1,5,6] == [3,11,10]
-- > (invert 0 . tn  4) [1,5,6] == [7,3,2]
tni :: Z12 -> [Z12] -> [Z12]
tni n = tn n . invert 0

-- | Modulo 12 multiplication
--
-- > mn 11 [0,1,4,9] == tni 0 [0,1,4,9]
mn :: Z12 -> [Z12] -> [Z12]
mn n = fmap (* n)

-- | M5, ie. 'mn' @5@.
--
-- > m5 [0,1,3] == [0,5,3]
m5 :: [Z12] -> [Z12]
m5 = mn 5

-- | T-related sequences of /p/.
--
-- > length (t_related [0,3,6,9]) == 12
t_related :: [Z12] -> [[Z12]]
t_related p = fmap (`tn` p) [0..11]

-- | T\/I-related sequences of /p/.
--
-- > length (ti_related [0,1,3]) == 24
-- > length (ti_related [0,3,6,9]) == 24
-- > ti_related [0] == map return [0..11]
ti_related :: [Z12] -> [[Z12]]
ti_related p = nub (t_related p ++ t_related (invert 0 p))

-- | R\/T\/I-related sequences of /p/.
--
-- > length (rti_related [0,1,3]) == 48
-- > length (rti_related [0,3,6,9]) == 24
rti_related :: [Z12] -> [[Z12]]
rti_related p = let q = ti_related p in nub (q ++ map reverse q)

-- | T\/M\/I-related sequences of /p/.
tmi_related :: [Z12] -> [[Z12]]
tmi_related p = let q = ti_related p in nub (q ++ map m5 q)

-- | R\/T\/M\/I-related sequences of /p/.
rtmi_related :: [Z12] -> [[Z12]]
rtmi_related p = let q = tmi_related p in nub (q ++ map reverse q)

-- | r\/R\/T\/M\/I-related sequences of /p/.
rrtmi_related :: [Z12] -> [[Z12]]
rrtmi_related p = nub (concatMap rtmi_related (T.rotations p))

-- * Sequence operations

-- | Variant of 'tn', transpose /p/ so first element is /n/.
--
-- > tn_to 5 [0,1,3] == [5,6,8]
tn_to :: Z12 -> [Z12] -> [Z12]
tn_to n p =
    case p of
      [] -> []
      x:xs -> n : tn (n - x) xs

-- | Variant of 'invert', inverse about /n/th element.
--
-- > map (invert_ix 0) [[0,1,3],[3,4,6]] == [[0,11,9],[3,2,0]]
-- > map (invert_ix 1) [[0,1,3],[3,4,6]] == [[2,1,11],[5,4,2]]
invert_ix :: Int -> [Z12] -> [Z12]
invert_ix n p = invert (p!!n) p

-- | The standard t-matrix of /p/.
--
-- > tmatrix [0,1,3] == [[0,1,3]
-- >                    ,[11,0,2]
-- >                    ,[9,10,0]]
tmatrix :: [Z12] -> [[Z12]]
tmatrix p = map (`tn` p) (tn_to 0 (invert_ix 0 p))