packages feed

hmt-0.11: Music/Theory/Prime.hs

-- | Forte and Rahn prime form operations.
module Music.Theory.Prime (cmp_prime
                          ,forte_cmp,forte_prime
                          ,rahn_cmp,rahn_prime
                          ,encode_prime) where

import Data.Bits
import Data.List
import Music.Theory.PitchClass

-- | Prime form rule requiring comparator.
cmp_prime :: (Integral a) => ([a] -> [a] -> Ordering) -> [a] -> [a]
cmp_prime _ [] = []
cmp_prime f p =
    let q = invert 0 p
        r = rotations (pcset p) ++ rotations (pcset q)
    in minimumBy f (map (transposeTo 0) r)

-- | Forte comparison function (rightmost first then leftmost outwards).
--
-- > forte_cmp [0,1,3,6,8,9] [0,2,3,6,7,9] == LT
forte_cmp :: (Ord t) => [t] -> [t] -> Ordering
forte_cmp [] [] = EQ
forte_cmp p  q  =
    let r = compare (last p) (last q)
    in if r == EQ then compare p q else r

-- | Forte prime form, ie. 'cmp_prime' of 'forte_cmp'.
--
-- > forte_prime [0,1,3,6,8,9] == [0,1,3,6,8,9]
-- > forte_prime [0,1,3,6,8,9] /= rahn_prime [0,1,3,6,8,9]
forte_prime :: (Integral a) => [a] -> [a]
forte_prime = cmp_prime forte_cmp

-- | Rahn prime form (comparison is rightmost inwards).
--
-- > rahn_cmp [0,1,3,6,8,9] [0,2,3,6,7,9] == GT
rahn_cmp :: Ord a => [a] -> [a] -> Ordering
rahn_cmp p q = compare (reverse p) (reverse q)

-- | Rahn prime form, ie. 'cmp_prime' of 'rahn_cmp'.
--
-- > rahn_prime [0,1,3,6,8,9] == [0,2,3,6,7,9]
rahn_prime :: (Integral a) => [a] -> [a]
rahn_prime = cmp_prime rahn_cmp

-- | Binary encoding prime form algorithm, equalivalent to Rahn.
--
-- > encode_prime [0,1,3,6,8,9] == rahn_prime [0,1,3,6,8,9]
encode_prime :: (Integral a, Bits a) => [a] -> [a]
encode_prime s =
    let t = map (`tn` s) [0..11]
        c = t ++ map (invert 0) t
    in decode (minimum (map encode c))

-- | Encoder for 'encode_prime'.
encode :: (Integral a) => [a] -> a
encode = sum . map (2 ^)

-- | Decoder for 'encode_prime'.
decode :: (Bits a, Integral a) => a -> [a]
decode n =
    let f i = (i, testBit n i)
    in map (fromIntegral . fst) (filter snd (map f [0..11]))