-- | Godfried T. Toussaint et. al.
-- \"The distance geometry of music\"
-- /Journal of Computational Geometry: Theory and Applications/
-- Volume 42, Issue 5, July, 2009
-- (<http://dx.doi.org/10.1016/j.comgeo.2008.04.005>)
module Music.Theory.Bjorklund (bjorklund,xdot,iseq,iseq_str) where

import Data.List.Split {- split -}

type STEP a = ((Int,Int),([[a]],[[a]]))

left :: STEP a -> STEP a
left ((i,j),(xs,ys)) =
    let (xs',xs'') = splitAt j xs
    in ((j,i-j),(zipWith (++) xs' ys,xs''))

right :: STEP a -> STEP a
right ((i,j),(xs,ys)) =
    let (ys',ys'') = splitAt i ys
    in ((i,j-i),(zipWith (++) xs ys',ys''))

bjorklund' :: STEP a -> STEP a
bjorklund' (n,x) =
    let (i,j) = n
    in if min i j <= 1
       then (n,x)
       else bjorklund' (if i > j then left (n,x) else right (n,x))

-- | Bjorklund's algorithm to construct a binary sequence of /n/ bits
-- with /k/ ones such that the /k/ ones are distributed as evenly as
-- possible among the (/n/ - /k/) zeroes.
--
-- > bjorklund (5,9) == [True,False,True,False,True,False,True,False,True]
-- > xdot (bjorklund (5,9)) == "x.x.x.x.x"
--
-- > let es = [(2,3),(2,5)
-- >          ,(3,4),(3,5),(3,8)
-- >          ,(4,7),(4,9),(4,12),(4,15)
-- >          ,(5,6),(5,7),(5,8),(5,9),(5,11),(5,12),(5,13),(5,16)
-- >          ,(6,7),(6,13)
-- >          ,(7,8),(7,9),(7,10),(7,12),(7,15),(7,16),(7,17),(7,18)
-- >          ,(8,17),(8,19)
-- >          ,(9,14),(9,16),(9,22),(9,23)
-- >          ,(11,12),(11,24)
-- >          ,(13,24)
-- >          ,(15,34)]
-- > in map (\e -> let e' = bjorklund e in (e,xdot e',iseq_str e')) es
--
-- > [((2,3),"xx.","(12)")
-- > ,((2,5),"x.x..","(23)")
-- > ,((3,4),"xxx.","(112)")
-- > ,((3,5),"x.x.x","(221)")
-- > ,((3,8),"x..x..x.","(332)")
-- > ,((4,7),"x.x.x.x","(2221)")
-- > ,((4,9),"x.x.x.x..","(2223)")
-- > ,((4,12),"x..x..x..x..","(3333)")
-- > ,((4,15),"x...x...x...x..","(4443)")
-- > ,((5,6),"xxxxx.","(11112)")
-- > ,((5,7),"x.xx.xx","(21211)")
-- > ,((5,8),"x.xx.xx.","(21212)")
-- > ,((5,9),"x.x.x.x.x","(22221)")
-- > ,((5,11),"x.x.x.x.x..","(22223)")
-- > ,((5,12),"x..x.x..x.x.","(32322)")
-- > ,((5,13),"x..x.x..x.x..","(32323)")
-- > ,((5,16),"x..x..x..x..x...","(33334)")
-- > ,((6,7),"xxxxxx.","(111112)")
-- > ,((6,13),"x.x.x.x.x.x..","(222223)")
-- > ,((7,8),"xxxxxxx.","(1111112)")
-- > ,((7,9),"x.xxx.xxx","(2112111)")
-- > ,((7,10),"x.xx.xx.xx","(2121211)")
-- > ,((7,12),"x.xx.x.xx.x.","(2122122)")
-- > ,((7,15),"x.x.x.x.x.x.x..","(2222223)")
-- > ,((7,16),"x..x.x.x..x.x.x.","(3223222)")
-- > ,((7,17),"x..x.x..x.x..x.x.","(3232322)")
-- > ,((7,18),"x..x.x..x.x..x.x..","(3232323)")
-- > ,((8,17),"x.x.x.x.x.x.x.x..","(22222223)")
-- > ,((8,19),"x..x.x.x..x.x.x..x.","(32232232)")
-- > ,((9,14),"x.xx.xx.xx.xx.","(212121212)")
-- > ,((9,16),"x.xx.x.x.xx.x.x.","(212221222)")
-- > ,((9,22),"x..x.x..x.x..x.x..x.x.","(323232322)")
-- > ,((9,23),"x..x.x..x.x..x.x..x.x..","(323232323)")
-- > ,((11,12),"xxxxxxxxxxx.","(11111111112)")
-- > ,((11,24),"x..x.x.x.x.x..x.x.x.x.x.","(32222322222)")
-- > ,((13,24),"x.xx.x.x.x.x.xx.x.x.x.x.","(2122222122222)")
-- > ,((15,34),"x..x.x.x.x..x.x.x.x..x.x.x.x..x.x.","(322232223222322)")]
bjorklund :: (Int,Int) -> [Bool]
bjorklund (i,j') =
    let j = j' - i
        x = replicate i [True]
        y = replicate j [False]
        (_,(x',y')) = bjorklund' ((i,j),(x,y))
    in concat x' ++ concat y'

-- | /xdot/ notation for pattern.
--
-- > xdot (bjorklund (5,9)) == "x.x.x.x.x"
xdot :: [Bool] -> String
xdot = map (\x -> if x then 'x' else '.')

-- | The 'iseq' of a pattern is the distance between 'True' values.
--
-- > iseq (bjorklund (5,9)) == [2,2,2,2,1]
iseq :: [Bool] -> [Int]
iseq =
    let f = split . keepDelimsL . whenElt
    in tail . map length . f (== True)

-- | 'iseq' of pattern as compact string.
--
-- > iseq_str (bjorklund (5,9)) == "(22221)"
iseq_str :: [Bool] -> String
iseq_str = let f xs = "(" ++ concatMap show xs ++ ")"
           in f . iseq