packages feed

hmt-0.18: Music/Theory/Tuning/Wilson.hs

-- | Erv Wilson, archives <http://anaphoria.com/wilson.html>
module Music.Theory.Tuning.Wilson where

import Control.Monad {- base -}
import Data.List {- base -}
import Data.Maybe {- base -}
import Data.Ratio {- base -}
import Safe {- safe -}
import System.FilePath {- filepath -}
import Text.Printf {- base -}

import qualified Music.Theory.Array.Text as T {- hmt -}
import qualified Music.Theory.Function as T {- hmt -}
import qualified Music.Theory.Graph.Dot as T {- hmt -}
import qualified Music.Theory.Graph.Type as T {- hmt -}
import qualified Music.Theory.Interval.Barlow_1987 as T {- hmt -}
import qualified Music.Theory.List as T {- hmt -}
import qualified Music.Theory.Math as T {- hmt -}
import qualified Music.Theory.Math.Convert as T {- hmt -}
import qualified Music.Theory.Math.OEIS as T {- hmt -}
import qualified Music.Theory.Math.Prime as T {- hmt -}
import qualified Music.Theory.Set.List as T {- hmt -}
import qualified Music.Theory.Show as T {- hmt -}
import qualified Music.Theory.Tuning as T {- hmt -}
import qualified Music.Theory.Tuning.Scala as T {- hmt -}
import qualified Music.Theory.Tuple as T {- hmt -}

-- * GEOM (SEE "Data.CG.Minus.Plain")

type V2 n = (n,n)
v2_map :: (t -> u) -> V2 t -> V2 u
v2_map f (a,b) = (f a,f b)
v2_zip :: (a -> b -> c) -> V2 a -> V2 b -> V2 c
v2_zip f (i,j) (p,q) = (f i p,f j q)
v2_add :: Num n => V2 n -> V2 n -> V2 n
v2_add = v2_zip (+)
v2_sum :: Num n => [V2 n] -> V2 n
v2_sum = foldl v2_add (0,0)
v2_scale :: Num n => n -> V2 n -> V2 n
v2_scale n = v2_map (* n)

-- * PT SET

{- | Normalise set of points to lie in (-1,-1) - (1,1), scaling symetrically about (0,0)

> pt_set_normalise_sym [(40,0),(0,40),(13,11),(-8,4)] == [(1,0),(0,1),(0.325,0.275),(-0.2,0.1)]
> pt_set_normalise_sym [(-10,0),(1,10)] == [(-1,0),(0.1,1)]
-}
pt_set_normalise_sym :: (Fractional n,Ord n) => [V2 n] -> [V2 n]
pt_set_normalise_sym x = let z = maximum (map (uncurry max . T.bimap1 abs) x) in map (v2_scale (recip z)) x

-- * LATTICE CO-ORD

-- | /k/-unit co-ordinates for /k/-lattice.
type LC n = [V2 n]

-- | Erv Wilson standard lattice, unit co-ordinates for 5-dimensions, ie. [3,5,7,11,13]
--
-- <http://anaphoria.com/wilsontreasure.html>
ew_lc_std :: Num n => LC n
ew_lc_std = [(20,0),(0,20),(4,3),(-3,4),(-1,2)]

-- | Kraig Grady standard lattice, unit co-ordinates for 5-dimensions, ie. [3,5,7,11,13]
--
-- <http://anaphoria.com/wilsontreasure.html>
kg_lc_std :: Num n => LC n
kg_lc_std = [(40,0),(0,40),(13,11),(-14,18),(-8,4)]

-- | Erv Wilson tetradic lattice, used especially when working with hexanies or 7 limit tunings
--
-- <http://anaphoria.com/wilsontreasure.html>
ew_lc_tetradic :: Num n => LC n
ew_lc_tetradic = [(-4,-2),(6,1),(5,-2)]

-- | Resolve POS against LC to V2
lc_pos_to_pt :: (Fractional n, Ord n) => LC n -> POS -> V2 n
lc_pos_to_pt lc x = v2_sum (zipWith (v2_scale . fromIntegral) x (pt_set_normalise_sym lc))

-- * LAT

-- | A discrete /k/-lattice is described by a sequence of /k/-factors.
--   LAT values are ordinarily though not necessarily primes.
type LAT = [Integer]

-- | Positions in a /k/-lattice are given as a /k/-list of steps.
type POS = [Int]

-- | White-space pretty printer for POS.
--
-- > pos_pp_ws [0,-2,1] == "  0 -2  1"
pos_pp_ws :: POS -> String
pos_pp_ws = let f x = printf "%3d" x in concatMap f

-- | Given LAT [X,Y,Z..] and POS [x,y,z..], calculate the indicated ratio.
--
-- > lat_res [3,5] [-5,2] == (5 * 5) / (3 * 3 * 3 * 3 * 3)
lat_res :: LAT -> POS -> Rational
lat_res p q =
  let f i j = case compare j 0 of
                GT -> (i ^ T.int_to_integer j) % 1
                EQ -> 1
                LT -> 1 % (i ^ abs (T.int_to_integer j))
  in product (zipWith f p q)

-- * RAT (n,d)

-- | Ratio given as (/n/,/d/)
type RAT = (Integer,Integer)

-- | Remove all octaves from /n/ and /d/.
rat_rem_oct :: RAT -> RAT
rat_rem_oct = T.bimap1 (product . filter (/= 2)) . T.rat_prime_factors

-- | Lift 'RAT' function to 'Rational'.
rat_lift_1 :: (RAT -> RAT) -> Rational -> Rational
rat_lift_1 f = uncurry (%) . f . T.rational_nd

rat_to_ratio :: RAT -> Rational
rat_to_ratio (n,d) = n % d

-- | Mediant, ie. n1+n2/d1+d2
--
-- > rat_mediant (0,1) (1,2) == (1,3)
rat_mediant :: RAT -> RAT -> RAT
rat_mediant (n1,d1) (n2,d2) = (n1 + n2,d1 + d2)

rat_pp :: RAT -> String
rat_pp (n,d) = concat [show n,"/",show d]

-- * Rational

-- | Lifted 'rat_rem_oct'.
--
-- > map ew_r_rem_oct [256/243,7/5,1/7] == [1/243,7/5,1/7]
r_rem_oct :: Rational -> Rational
r_rem_oct = rat_lift_1 rat_rem_oct

-- | Assert that /n/ is in [1,2).
r_verify_oct :: Rational -> Rational
r_verify_oct i = if i >= 1 && i < 2 then i else error (show ("r_verify_oct?",i))

-- | Find limit of set of ratios, ie. largest factor in either numerator or denominator.
--
-- > r_seq_limit [1] == 1
r_seq_limit :: [Rational] -> Integer
r_seq_limit = maximum . map T.rational_prime_limit

-- * Table

-- > map (rat_fact_lm 11) [3,5,7,11] == [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]
rat_fact_lm :: Integer -> Rational -> POS
rat_fact_lm lm = tail . T.rat_prime_factors_t (fromMaybe 1 (T.prime_k lm) + 1) . T.rational_nd

tbl_txt :: Integer -> [Rational] -> [[String]]
tbl_txt lm_z rs =
  let lm = r_seq_limit rs
      scl = map (rat_fact_lm lm) rs
      cs = map (T.ratio_to_cents . T.fold_ratio_to_octave_err) rs
      hs = map (T.harmonicity_r T.barlow) rs :: [Double]
      f (k,x,r,c,h) = [show k
                      ,if lm <= lm_z then pos_pp_ws x else "..."
                      ,T.ratio_pp r
                      ,T.real_pp 2 c
                      ,T.real_pp_unicode 2 h]
  in map (intersperse "=" . f) (zip5 [0::Int ..] scl rs cs hs)

-- > tbl_wr [1,7/6,5/4,4/3,3/2]
tbl_wr :: [Rational] -> IO ()
tbl_wr = putStr . unlines . T.table_pp (False,True,False," ",False) . tbl_txt 31

-- * Graph

-- | (maybe-lc,gr-attr,vertex-pp)
type EW_GR_OPT = (Maybe (LC Rational),[T.DOT_META_ATTR],Rational -> String)

ew_gr_opt_pos :: EW_GR_OPT -> Bool
ew_gr_opt_pos (lc_m,_,_) = isJust lc_m

ew_gr_r_pos :: LC Rational -> Rational -> T.DOT_ATTR
ew_gr_r_pos lc =
  let f m (x,y) = (m * x,m * y)
  in T.node_pos_attr . f 160 . lc_pos_to_pt lc . Safe.tailDef [] . T.rational_prime_factors_l

ew_gr_udot :: EW_GR_OPT -> T.LBL Rational () -> [String]
ew_gr_udot (lc_m,attr,v_pp) =
  let (e,p_f) = case lc_m of
                  Nothing -> ("sfdp",const Nothing)
                  Just lc -> ("neato",Just . ew_gr_r_pos lc)
  in T.lbl_to_udot
     ([("graph:layout",e),("node:shape","plain")] ++ attr) -- ("graph:K","0.6") ("edge:len","1.0")
     (\(_,v) -> T.mcons (p_f v) [("label",v_pp v)]
     ,\_ -> [])

ew_gr_udot_wr :: EW_GR_OPT -> FilePath -> T.LBL Rational () -> IO ()
ew_gr_udot_wr opt fn = writeFile fn . unlines . ew_gr_udot opt

ew_gr_udot_wr_svg :: EW_GR_OPT -> FilePath -> T.LBL Rational () -> IO ()
ew_gr_udot_wr_svg opt fn gr = do
  ew_gr_udot_wr opt fn gr
  void (T.dot_to_svg (if ew_gr_opt_pos opt then ["-n"] else []) fn (replaceExtension fn "svg"))

-- * ZIG-ZAG

zz_seq_1 :: (Eq n,Num n) => Int -> (n,n) -> (n,n) -> [(n,n)]
zz_seq_1 k (p,q) (n,d) = if k == 0 then [(n,d)] else (n,d) : zz_seq_1 (k - 1) (p,q) (n+p,d+q)

-- > zz_next 3 [(0,1),(1,1)] == [(1,1),(1,2),(1,3),(1,4)]
zz_next :: (Eq n, Num n) => Int -> [(n,n)] -> [(n,n)]
zz_next k p =
  case reverse p of
    i:j:_ -> zz_seq_1 k j i
    _ -> error "zz_next?"

zz_recur :: (Eq n, Num n) => [Int] -> [(n,n)] -> [[(n,n)]]
zz_recur k_seq p =
  case k_seq of
    [] -> []
    k:k_rem -> let r = zz_next k p in r : zz_recur k_rem r

-- > zz_seq [3,9,2,2,4,6,2,1,1,3]
-- > zz_seq [2,4,2,158]
-- > zz_seq [1,1,4,2,1,3,1,6,2]
zz_seq :: (Eq n, Num n) => [Int] -> [[(n, n)]]
zz_seq k_seq = zz_recur k_seq [(0,1),(1,1)]

-- * MOS

-- > gen_coprime 12 == [1,5]
-- > gen_coprime 49 == [1..24] \\ [7,14,21]
gen_coprime :: Integral a => a -> [a]
gen_coprime x = filter (\y -> gcd y x == 1) [1 .. (x `div` 2)]

-- > mos_2 12 5 == (5,7)
mos_2 :: Num n => n -> n -> (n,n)
mos_2 p g = (g,p - g)

-- | Divide MOS, keeps retained value on same side
--
-- > mos_step (5,7) == (5,2)
-- > mos_step (5,2) == (3,2)
-- > mos_step (3,2) == (1,2)
mos_step :: (Ord a, Num a) => (a, a) -> (a, a)
mos_step (i,j) = if i < j then (i,j - i) else (i - j,j)

-- > mos_unfold (5,7)  == [(5,7),(5,2),(3,2),(1,2)]
-- > mos_unfold (41,17) == [(41,17),(24,17),(7,17),(7,10),(7,3),(4,3),(1,3),(1,2)]
mos_unfold :: (Ord b, Num b) => (b, b) -> [(b, b)]
mos_unfold x =
  let y = mos_step x
  in if T.t2_sum y == 3 then [x,y] else x : mos_unfold y

mos_verify :: Integral a => a -> a -> Bool
mos_verify p g =
  let x = if g > (p `div` 2) then p `mod` g else g
  in x `elem` gen_coprime p

-- > mos 12 5 == [(5,7),(5,2),(3,2),(1,2)]
mos :: (Ord b, Integral b) => b -> b -> [(b, b)]
mos p g = if mos_verify p g then mos_unfold (mos_2 p g) else error "mos?"

-- > mos_seq 12 5 == [[5,7],[5,5,2],[3,2,3,2,2],[1,2,2,1,2,2,2]]
-- > mos_seq 41 17 !! 4 == [3,3,4,3,4,3,3,4,3,4,3,4]
-- > map length (mos_seq 49 27) == [2,3,5,7,9,11,20,29]
mos_seq :: (Ord b, Integral b) => b -> b -> [[b]]
mos_seq p g =
  let step_f (i,j) = concatMap (\x -> if x == i + j then [i,j] else [x])
      recur_f x l = if null x then [l] else l : recur_f (tail x) (step_f (head x) l)
      (i0,j0):r = mos p g
  in recur_f r [i0,j0]

mos_cell_pp :: (Integral i,Show i) => i -> String
mos_cell_pp x = let s = show x in s ++ genericReplicate (x - genericLength s) '-'

mos_row_pp :: (Integral i,Show i) => [i] -> String
mos_row_pp = concatMap mos_cell_pp

mos_tbl_pp :: (Integral i,Show i) => [[i]] -> [String]
mos_tbl_pp = map mos_row_pp

-- > mos_tbl_wr (mos_seq 49 27)
mos_tbl_wr :: (Integral i,Show i) => [[i]] -> IO ()
mos_tbl_wr = putStrLn . unlines . mos_tbl_pp

-- * MOS/LOG

mos_recip_seq :: Double -> [(Int,Double)]
mos_recip_seq x = let y = truncate x in (y,x) : mos_recip_seq (recip (x - fromIntegral y))

-- > take 3 (mos_log (5/4)) == [(3,3.10628371950539),(9,9.408778735385603),(2,2.4463112031908785)]
mos_log :: Double -> [(Int,Double)]
mos_log r = mos_recip_seq (recip (logBase 2 r))

-- > take 9 (mos_log_kseq 1.465571232) == [1,1,4,2,1,3,1,6,2]
mos_log_kseq :: Double -> [Int]
mos_log_kseq = map fst . mos_log

-- * STERN-BROCOT TREE

data SBT_DIV = NIL | LHS | RHS deriving (Show)
type SBT_NODE = (SBT_DIV,RAT,RAT,RAT)

sbt_step :: SBT_NODE -> [SBT_NODE]
sbt_step (_,l,m,r) = [(LHS,l,rat_mediant l m, m),(RHS,m,rat_mediant m r,r)]

-- sbt = stern-brocot tree
sbt_root :: SBT_NODE
sbt_root = (NIL,(0,1),(1,1),(1,0))

sbt_half :: SBT_NODE
sbt_half = (NIL,(0,1),(1,2),(1,1))

-- > sbt_from sbt_root
sbt_from :: SBT_NODE -> [[SBT_NODE]]
sbt_from = iterate (concatMap sbt_step) . return

sbt_k_from :: Int -> SBT_NODE -> [[SBT_NODE]]
sbt_k_from k = take k . sbt_from

sbt_node_to_edge :: SBT_NODE -> String
sbt_node_to_edge (dv,l,m,r) =
  let edge_pp p q = printf "\"%s\" -- \"%s\"" (rat_pp p) (rat_pp q)
  in case dv of
       NIL -> ""
       LHS -> edge_pp r m
       RHS -> edge_pp l m

sbt_node_elem :: SBT_NODE -> [RAT]
sbt_node_elem (dv,l,m,r) =
  case dv of
    NIL -> [l,m,r]
    _ -> [m]

sbt_dot :: [SBT_NODE] -> [String]
sbt_dot n =
  let e = map sbt_node_to_edge n
  in concat [["graph {","node [shape=plain]"],e,["}"]]

-- * M-GEN

(^.) :: Rational -> Int -> Rational
(^.) = (^)

r_normalise :: [Rational] -> [Rational]
r_normalise = nub . sortOn T.fold_ratio_to_octave_err

-- | (ratio,multiplier,steps)
type M_GEN = (Rational,Rational,Int)

m_gen_unfold :: M_GEN -> [Rational]
m_gen_unfold (r,m,n) = take n (iterate (* m) r)

m_gen_to_r :: [M_GEN] -> [Rational]
m_gen_to_r = r_normalise . concatMap m_gen_unfold

-- * M3-GEN

-- | (ratio,M3-steps)
type M3_GEN = (Rational,Int)

m3_to_m :: M3_GEN -> M_GEN
m3_to_m (r,n) = (r,3,n)

-- > map m3_gen_unfold [(3,4),(21/9,4),(15/9,4),(35/9,3),(21/5,4),(27/5,3)]
m3_gen_unfold :: M3_GEN -> [Rational]
m3_gen_unfold = m_gen_unfold . m3_to_m

m3_gen_to_r :: [M3_GEN] -> [Rational]
m3_gen_to_r = r_normalise . concatMap m3_gen_unfold

-- * SCALA

r_to_scale :: String -> String -> [Rational] -> T.Scale
r_to_scale nm dsc r =
  let r' = map T.fold_ratio_to_octave_err (tail r) ++ [2]
  in if r !! 0 /= 1 || not (T.is_ascending r')
     then error "r_to_scale?"
     else (nm,dsc,length r,map Right r')

ew_scl_find_r :: [Rational] -> IO [String]
ew_scl_find_r r =
  let set_eq x y = sort x == sort y
  in if head r /= 1
     then error "ew_scl_find_r?"
     else fmap (map T.scale_name) (T.scl_find_ji set_eq (map T.fold_ratio_to_octave_err r ++ [2]))

-- * <http://anaphoria.com/1-3-5-7-9Genus.pdf>

ew_1357_3_gen :: [M3_GEN]
ew_1357_3_gen = [(3,4),(21/9,4),(15/9,4),(35/9,3),(21/5,4),(27/5,3)]

{- | P.3 7-limit {SCALA=NIL}

> ew_scl_find_r (1 : ew_1357_3_r)
-}
ew_1357_3_r :: [Rational]
ew_1357_3_r = r_normalise (concatMap m3_gen_unfold ew_1357_3_gen)

ew_1357_3_scl :: T.Scale
ew_1357_3_scl = r_to_scale "ew_1357_3" "EW, 1-3-5-7-9Genus.pdf, P.3" (1 : ew_1357_3_r)

-- * <http://anaphoria.com/earlylattices12.pdf>

{- | P.7 11-limit {SCALA=NIL}

> ew_scl_find_r ew_el12_7_r
-}
ew_el12_7_r :: [Rational]
ew_el12_7_r = [1,5/(7*11),1/7,7*11,7*11*11/5,11,5/7,1/11,7*11*11,1/(7*11),11*11,7*11/5]

ew_el12_7_scl :: T.Scale
ew_el12_7_scl = r_to_scale "ew_el12_7" "EW, earlylattices12.pdf, P.7" ew_el12_7_r

{- | P.9 7-limit {SCALA=wilson_class}

> ew_scl_find_r ew_el12_9_r
-}
ew_el12_9_r :: [Rational]
ew_el12_9_r = [1,5*5/3,7/(5*5),7/3,5,1/3,7/5,5*7/3,1/5,5/3,7,7/(3*5)]

--ew_el12_9_scl :: T.Scale
--ew_el12_9_scl = r_to_scale "ew_el12_9" "EW, earlylattices12.pdf, P.9" ew_el12_9_r

{- | P.12 11-limit {SCALA=NIL}

> ew_scl_find_r ew_el12_12_r
-}
ew_el12_12_r :: [Rational]
ew_el12_12_r = [1,3*3*5/11,3/11,7/3,5,7/11,3*5/11,5*7/3,7/(3*3),5*7/11,7/(3*11),3*5]

ew_el12_12_scl :: T.Scale
ew_el12_12_scl = r_to_scale "ew_el12_12" "EW, earlylattices12.pdf, P.12" ew_el12_12_r

-- * <http://anaphoria.com/earlylattices22.pdf>

{- | P.2 11-limit {SCALA=wilson_l4}

> ew_scl_find_r ew_el22_2_r
-}
ew_el22_2_r :: [Rational]
ew_el22_2_r =
  [1,7*7/3,3*7/5,5/(3*3),1/7,7/3,3/5,5,5*7/(3*3*3),1/3,7*7/(3*3)
  ,7/5,5*7/3,3,7/(3*3),1/5,5/3,3/7,7,3*3/5,7/(3*5),5*7/(3*3)]

{- | P.3 11-limit {SCALA=wilson_l5}

> ew_scl_find_r ew_el22_3_r
-}
ew_el22_3_r :: [Rational]
ew_el22_3_r =
  [1,7*7/3,7*11/(3*3),3/11,1/7,7/3,3/5,5,7/11,1/3,7*7/(3*3)
  ,7/5,5*7/3,3,7/(3*3),1/5,5/3,3/7,7,11/3,7/(3*5),5*7/(3*3)]

{- | P.4 11-limit {SCALA=wilson_l3}

> ew_scl_find_r ew_el22_4_r
-}
ew_el22_4_r :: [Rational]
ew_el22_4_r =
  [1,3*11,3*7/5,5*7,3*3,7/3,3/5,5,7/11,3*7,11
  ,7/5,5*7/3,3,7/(3*3),1/5,3*5*7,3*3*3,7,3*3/5,3*5,3*7/11]

{- | P.5 11-limit {SCALA=wilson_l1}

> ew_scl_find_r ew_el22_5_r
-}
ew_el22_5_r :: [Rational]
ew_el22_5_r =
  [1,3*11,3*7/5,5*7,3*3,7/3,7*11,5,3*5*11,3*7,11
  ,7/5,3*7*11/5,3,3*3*11,7*11/3,3*11/5,5*11,7,3*7*11,3*5,7*11/5]

{- | P.6 11-limit {SCALA=wilson_l2}

> ew_scl_find_r ew_el22_6_r
-}
ew_el22_6_r :: [Rational]
ew_el22_6_r =
  [1,7*7/3,7*11/(3*3),11/5,3*3,7/3,7*11,5,7*11/(3*5),1/3,11
  ,7*11/(3*3*3),5*7/3,3,11/7,7*11/3,5/3,7*11/(3*3*5),7,11/3,3*5,7*11/5]

-- * <http://anaphoria.com/diamond.pdf>

ew_diamond_mk :: [Integer] -> [Rational]
ew_diamond_mk u = r_normalise [x % y | x <- u, y <- u]

-- > m3_gen_to_r ew_diamond_12_gen == ew_diamond_12_r
ew_diamond_12_gen :: [M3_GEN]
ew_diamond_12_gen =
  [(1/(3^.2),5),(5/(3^.2),3),(7/(3^.2),3),(11/(3^.2),3)
  ,(1/5,3),(1/7,3),(1/11,3)
  ,(5/7,1),(5/11,1),(7/5,1),(7/11,1),(11/5,1),(11/7,1)]

{- | P.7 & P.12 11-limit {SCALA=partch_29}

1,3,5,7,9,11 diamond

> ew_scl_find_r ew_diamond_12_r -- partch_29
-}
ew_diamond_12_r :: [Rational]
ew_diamond_12_r = ew_diamond_mk [1,3,5,7,9,11]

{- | P.10 & P.13 13-limit {SCALA=novaro15}

1,3,5,7,9,11,13,15 diamond

> ew_scl_find_r ew_diamond_13_r -- novaro15
-}
ew_diamond_13_r :: [Rational]
ew_diamond_13_r = ew_diamond_mk [1,3,5,7,9,11,13,15]

-- * <http://anaphoria.com/hel.pdf>

hel_r_asc :: (Integer,Integer) -> [Rational]
hel_r_asc (n,d) = n%d : hel_r_asc (n+1,d+1)

type HEL = ([Rational],[Rational])

-- | P.6
hel_1_i :: HEL
hel_1_i =
  let i = take 6 (hel_r_asc (7,6))
  in (take 5 i,take 5 (T.rotate_left 2 i))

-- | P.6
hel_2_i :: HEL
hel_2_i =
  let i = take 10 (hel_r_asc (9,8))
  in (take 8 (T.rotate_left 3 (tail i))
     ,take 7 i)

-- | P.10
hel_3_i :: HEL
hel_3_i =
  let i = take 16 (hel_r_asc (15,14))
  in (take 13 (T.rotate_left 6 (take 14 i)),take 14 (tail i))

hel_r :: HEL -> [[Rational]]
hel_r (p,q) =
  let i_to_r = scanl (*) 1
  in [i_to_r p,i_to_r q,r_normalise (concat [i_to_r p,i_to_r q])]

{- | P.12 {SCALA=NIL}

22-tone 23-limit Evangalina tuning (2001)

> ew_scl_find_r ew_hel_12_r
-}
ew_hel_12_r :: [Rational]
ew_hel_12_r =
  [1,3*3*3*5,13/3,5/(3*3),3*3,7/3,11/(3*3),5,3*3*3*3,1/3,11
  ,3*3*5,17/3,3,3*3*3*3*5,13,5/3,3*3*3,7,11/3,3*5,23/3]

ew_hel_12_scl :: T.Scale
ew_hel_12_scl = r_to_scale "ew_hel_12" "EW, hel.pdf, P.12" ew_hel_12_r

-- * <http://anaphoria.com/HexanyStellatesExpansions.pdf>

-- > she_div "ABCD" == [["BCD","A"],["ACD","B"],["ABD","C"],["ABC","D"]]
she_div :: Eq a => [a] -> [[[a]]]
she_div x =
  let f = (== [1,length x - 1]) . sort . map length
  in map (reverse . sortOn length) (filter f (T.partitions x))

-- > she_div_r [1,3,5,7] == [105,35/3,21/5,15/7]
she_div_r :: [Rational] -> [Rational]
she_div_r =
  let f x =
        case x of
          [[a,b,c],[d]] -> (a * b * c) / d
          _ -> error "she_div?"
  in map f . she_div

-- > she_mul_r [1,3,5,7] == [1,3,5,7,9,15,21,25,35,49]
she_mul_r :: [Rational] -> [Rational]
she_mul_r r = [(x * y) | x <- r,y <- r,x <= y]

{- | she = Stellate Hexany Expansions, P.10 {SCALA=stelhex1,stelhex2,stelhex5,stelhex6}

> she [1,3,5,7] == [1,21/20,15/14,35/32,9/8,5/4,21/16,35/24,3/2,49/32,25/16,105/64,7/4,15/8]
> mapM (ew_scl_find_r . she) [[1,3,5,7],[1,3,5,9],[1,3,7,9],[1,3,5,11]]
> ew_scl_find_r (she [1,(5*7)/(3*3),1/(3 * 5),1/3]) -- NIL
-}
she :: [Rational] -> [Rational]
she r = nub (sort (map T.fold_ratio_to_octave_err (she_mul_r r ++ she_div_r r)))

-- * <http://anaphoria.com/meru.pdf>

-- > map (every_nth "abcdef") [1..3] == ["abcdef","ace","ad"]
every_nth :: [t] -> Int -> [t]
every_nth l k =
  case l of
    [] -> []
    x:_ -> x : every_nth (drop k l) k

meru :: Num n => [[n]]
meru =
  let f xs = zipWith (+) ([0] ++ xs) (xs ++ [0])
  in iterate f [1]

-- > meru_k 13
meru_k :: Num n => Int -> [[n]]
meru_k k = take k meru

-- > map (sum . meru_1) [1 .. 13] == [1,1,2,3,5,8,13,21,34,55,89,144,233]
meru_1 :: Num n => Int -> [n]
meru_1 k = zipWith (\x l -> atDef 0 l x) [0..] (reverse (meru_k k))

-- > take 13 meru_1_direct == [1,1,2,3,5,8,13,21,34,55,89,144,233]
meru_1_direct :: Num n => [n]
meru_1_direct = tail T.a000045

-- | Meru 2 = META-PELOG
--
-- > map (sum . meru_2) [1 .. 14] == [1,1,1,2,3,4,6,9,13,19,28,41,60,88]
meru_2 :: Num n => Int -> [n]
meru_2 k = zipWith (\x l -> atDef 0 l x) [0..] (every_nth (reverse (meru_k k)) 2)

-- > take 14 meru_2_direct == [1,1,1,2,3,4,6,9,13,19,28,41,60,88]
meru_2_direct :: Num n => [n]
meru_2_direct = T.a000930

-- | meru_3 = META-SLENDRO
meru_3 :: Num n => Int -> [[n]]
meru_3 k =
  let f t = zipWith (\x l -> atDef 0 l x) [0,2..] t
      t0 = reverse (meru_k k)
      t1 = map tail t0
  in [f t0,f t1]

-- > map sum (meru_3_seq 13) == [1,0,1,1,1,2,2,3,4,5,7,9,12,16,21,28,37,49,65,86,114,151,200,265,351,465]
meru_3_seq :: Num n => Int -> [[n]]
meru_3_seq k = concatMap meru_3 [1 .. k]

-- > take 26 meru_3_direct == [1,0,1,1,1,2,2,3,4,5,7,9,12,16,21,28,37,49,65,86,114,151,200,265,351,465]
meru_3_direct :: Num n => [n]
meru_3_direct = drop 3 T.a000931

-- > map (sum . meru_4) [1 .. 13] == [1,1,1,1,2,3,4,5,7,10,14,19,26]
meru_4 :: Num n => Int -> [n]
meru_4 k = zipWith (\x l -> atDef 0 l x) [0..] (every_nth (reverse (meru_k k)) 3)

-- > take 31 meru_4_direct == map (sum . meru_4) [1 .. 31]
meru_4_direct :: Num n => [n]
meru_4_direct = tail T.a003269

-- > map meru_5 [1..4]
meru_5 :: Num n => Int -> [[n]]
meru_5 k =
  let f t = zipWith (\x l -> atDef 0 l x) [0,3..] t
      t0 = reverse (meru_k k)
  in map (\n -> f (map (drop n) t0)) [0 .. 2]

-- > map sum (meru_5_seq 13)
meru_5_seq :: Num n => Int -> [[n]]
meru_5_seq k = concatMap meru_5 [1 .. k]

-- > take 39 meru_5_direct == map sum (meru_5_seq 13)
meru_5_direct :: Num n => [n]
meru_5_direct = T.a017817

-- > map (sum . meru_6) [1 .. 21] == [1,1,1,1,1,2,3,4,5,6,8,11,15,20,26,34,45,60,80,106,140]
meru_6 :: Num n => Int -> [n]
meru_6 k = zipWith (\x l -> atDef 0 l x) [0..] (every_nth (reverse (meru_k k)) 4)

-- > take 21 meru_6_direct == map (sum . meru_6) [1 .. 21]
meru_6_direct :: Num n => [n]
meru_6_direct = T.a003520

-- > take 26 meru_7_direct == [0,1,0,1,0,1,1,1,2,1,3,2,4,4,5,7,7,11,11,16,18,23,29,34,45,52]
meru_7_direct :: Num n => [n]
meru_7_direct = T.a001687

-- * <http://anaphoria.com/mos.pdf>

{- | P.13, tanabe {SCALA=chin_7}

> ew_scl_find_r ew_mos_13_tanabe_r
-}
ew_mos_13_tanabe_r :: [Rational]
ew_mos_13_tanabe_r = [1,9/8,81/64,4/3,3/2,27/16,243/128]

-- * <http://anaphoria.com/novavotreediamond.pdf> (Novaro)

ew_novarotreediamond_1 :: ([[Rational]],[[Rational]])
ew_novarotreediamond_1 =
  let rem_oct x = if last x /= 2 then error "rem_oct?" else T.drop_last x
      add_oct x = if last x >= 2 then error "add_oct?" else x ++ [2]
      r_to_i = T.d_dx_by (/) . add_oct
      i_to_r = rem_oct . scanl (*) 1
      r_0 = [1,5/4,4/3,3/2,5/3,7/4]
      i_0 = r_to_i r_0
      i = T.rotations i_0
  in (i,map i_to_r i)

{- | P.1 {SCALA=NIL}

23-tone 7-limit (2004)

> ew_scl_find_r ew_novarotreediamond_1_r
-}
ew_novarotreediamond_1_r :: [Rational]
ew_novarotreediamond_1_r = r_normalise (concat (snd ew_novarotreediamond_1))

ew_novarotreediamond_1_scl :: T.Scale
ew_novarotreediamond_1_scl = r_to_scale "ew_novarotreediamond_1" "EW, novavotreediamond.pdf, P.1" ew_novarotreediamond_1_r

-- * <http://anaphoria.com/Pelogflute.pdf>

{- | P.2 {SCALA=NIL}

9-tone Pelog cycle (1988)

> ew_scl_find_r ew_pelogFlute_2
-}
ew_Pelogflute_2_r :: Fractional n => [n]
ew_Pelogflute_2_r = [1,16/15,64/55,5/4,4/3,16/11,8/5,128/75,20/11]

ew_Pelogflute_2_scl :: T.Scale
ew_Pelogflute_2_scl = r_to_scale "ew_Pelogflute_2" "EW, Pelogflute.pdf, P.2" ew_Pelogflute_2_r


-- * <http://anaphoria.com/xen1.pdf>

-- | P.9, Fig. 3
xen1_fig3 :: (SBT_NODE,Int)
xen1_fig3 = ((NIL,(1,3),(2,5),(1,2)),5)

-- | P.9, Fig. 4
xen1_fig4 :: (SBT_NODE,Int)
xen1_fig4 = ((NIL,(2,5),(5,12),(3,7)),5)

-- * <http://anaphoria.com/xen3b.pdf>

-- | P.3 Turkisk Baglama Scale {11-limit, SCALA=NIL}
ew_xen3b_3_gen :: [(Rational,Int)]
ew_xen3b_3_gen = [(1/(3^.6),12),(1/11,2),(5/3,3)]

ew_xen3b_3_r :: [Rational]
ew_xen3b_3_r = m3_gen_to_r ew_xen3b_3_gen

ew_xen3b_3_scl :: T.Scale
ew_xen3b_3_scl = r_to_scale "ew_xen3b_3" "EW, xen3b.pdf, P.3" ew_xen3b_3_r

-- > map length xen3b_9_i == [5,7,12,19,31]
xen3b_9_i :: [[Rational]]
xen3b_9_i =
  [[6/5,                                             10/9,                          9/8,                           6/5,                                             10/9]
  ,[16/15,9/8,                                       10/9,                          9/8,                           16/15,9/8,                                       10/9]
  ,[16/15,135/128,16/15,                             25/24,16/15,                   16/15,135/128,                 16/15,135/128,16/15,                             25/24,16/15]
  ,[28/27,36/35,135/128,28/27,36/35,                 25/24,28/27,36/35,             28/27,36/35,135/128,           28/27,36/35,135/128,28/27,36/35,                 25/24,28/27,36/35]
  ,[64/63,49/48,36/35,45/44,33/32,64/63,49/48,36/35, 45/44,55/54,64/63,49/48,36/35, 64/63,49/48,36/35,45/44,33/32, 64/63,49/48,36/35,45/44,33/32,64/63,49/48,36/35, 45/44,55/54,64/63,49/48,36/35]]

{- | P.9 {SCALA 5=nil 7=ptolemy_idiat 12=nil 19=wilson2 31=wilson_31}

> mapM ew_scl_find_r xen3b_9_r
-}
xen3b_9_r :: [[Rational]]
xen3b_9_r = map (T.drop_last . scanl (*) 1) xen3b_9_i

-- > map length xen3b_13_i == [5,7,12,17,22]
xen3b_13_i :: [[Rational]]
xen3b_13_i =
  [[7/6,                           8/7,                     9/8,                     7/6,                           8/7]
  ,[28/27,9/8,                     8/7,                     9/8,                     28/27,9/8,                     8/7]
  ,[28/27,243/224,28/27,           10/9,36/35,              28/27,243/224,           28/27,243/224,28/27,           10/9,36/35]
  ,[28/27,36/35,135/128,28/27,     36/35,175/162,36/35,     28/27,36/35,135/128,     28/27,36/35,135/128,28/27,     36/35,175/162,36/35]
  ,[28/27,36/35,25/24,81/80,28/27, 36/35,25/24,28/27,36/35, 28/27,36/35,25/24,81/80, 28/27,36/35,25/24,81/80,28/27, 36/35,25/24,28/27,36/35]]

-- | P.13 {SCALA 5=slendro5_2 7=ptolemy_diat2 12=nil 17=nil 22=wilson7_4}
xen3b_13_r :: [[Rational]]
xen3b_13_r = map (T.drop_last . scanl (*) 1) xen3b_13_i

-- * <http://anaphoria.com/xen3bappendix.pdf>

{- | PP.1-2 {SCALA: 22=wilson7_4}

17,31,41 lattices from XEN3B (1975)
-}
ew_xen3b_apx_gen :: [(Int,[M3_GEN])]
ew_xen3b_apx_gen =
  [(17,[(1/729,12)
       ,(5/3,3)
       ,(11,2)])
  ,(31,[(1/3,5)
       ,(5,2),(1/(5*(3^.2)),5)
       ,(7/(3^.4),5),(1/(7*(3^.4)),5)
       ,(1/11,5)
       ,((1/3)*(1/7)*5,2)
       ,((1/(7*(3^.3))) * 5,2)])
  ,(41,[(1/(3^.6),12)
       ,(5/(3^.3),5),(1/(5*(3^.2)),5)
       ,(7/(3^.4),7),(1/(7*(3^.3)),7)
       ,(11,5)])
  ,(22,[(1/3,5)
       ,(5/(3^.3),5),(1/(5*(3^.2)),5)
       ,(7/(3^.4),5)
       ,(7/(3^.3)*5,2)])]

ew_xen3b_apx_r :: [(Int,[Rational])]
ew_xen3b_apx_r =
  let f (k,g) = (k,r_normalise (concatMap m3_gen_unfold g))
  in map f ew_xen3b_apx_gen

-- * <http://anaphoria.com/xen456.pdf>

ew_xen456_7_gen :: [M3_GEN]
ew_xen456_7_gen = [(25/24,4),(5/3,4),(4/3,4),(16/15,4),(32/25,3)]

{- P.7 {SCALA=wilson1}

19-tone "A Scale for Scott" (1976)

> L.ew_find_scl_name ew_xen456_7_r -- wilson1
-}
ew_xen456_7_r :: [Rational]
ew_xen456_7_r = m3_gen_to_r ew_xen456_7_gen

ew_xen456_9_gen :: [M3_GEN]
ew_xen456_9_gen =
  [(1/(3^.3),4)
  ,(1/(5*(3^.2)),3)
  ,(1/(7*3),3)
  ,(1/11,3)
  ,(5/(11*3),4)
  ,(7/11,2)]

{- | P.9 {SCALA=NIL}

19-tone scale for the Clavichord-19 (1976)

> ew_scl_find_r ew_xen456_9_r

> import qualified Music.Theory.List as T {- hmt -}
> T.scl_find_ji T.is_subset ew_xen456_9_r -- NIL
-}
ew_xen456_9_r :: [Rational]
ew_xen456_9_r = m3_gen_to_r ew_xen456_9_gen

ew_xen456_9_scl :: T.Scale
ew_xen456_9_scl = r_to_scale "ew_xen456_9" "EW, xen456.pdf, P.9" ew_xen456_9_r

-- * GEMS

{- | <http://wilsonarchives.blogspot.com/2010/10/scale-for-rod-poole.html>

13-limit 22-tone scale {SCALA=nil}

> ew_scl_find_r ew_poole_r
-}
ew_poole_r :: [Rational]
ew_poole_r =
  [1,11*3,7*3/5,13/3,3*3,7/3,11/(3*3),5,7/11,1/3
  ,11,7/5,13/(3*3),3,7/(3*3),11/(3*3*3),5/3,3*3*3,7,11/3,5*3,7*3/11]

ew_poole_scl :: T.Scale
ew_poole_scl = r_to_scale "ew_poole" "EW, 2010/10/scale-for-rod-poole.html" ew_poole_r

{- | <http://wilsonarchives.blogspot.com/2014/05/an-11-limit-centaur-implied-in-wilson.html>

11-limit 17-tone scale {SCALA=wilcent17}

> ew_scl_find_r ew_centaur17_r
-}
ew_centaur17_r :: [Rational]
ew_centaur17_r = [1,11/(3*7),11/5,3*3,7/3,11/(3*3),5,1/3,11,11/(3*5),3,11/7,11/(3*3*3),5/3,7,11/3,3*5]

{- | <http://wilsonarchives.blogspot.com/2018/03/an-unusual-22-tone-7-limit-tuning.html>

7-limit 22-tone scale {SCALA=nil}

> ew_scl_find_r ew_two_22_7_r
-}
ew_two_22_7_r :: [Rational]
ew_two_22_7_r =
  [1/1,9/35,1/15,35/1,9/1,7/3,3/5,315/1,245/3,21/1,27/5
  ,7/5,735/1,189/1,49/1,63/5,5/3,3/7,1/9,1/35,15/1,35/9]

ew_two_22_7_scl :: T.Scale
ew_two_22_7_scl = r_to_scale "ew_two_22_7" "EW, 2018/03/an-unusual-22-tone-7-limit-tuning.html" ew_two_22_7_r

-- * DB

{- | Scales /not/ present in the standard scala file set.

> mapM_ (T.scale_wr_dir "/home/rohan/sw/hmt/data/scl/") ew_scl_db
> map T.scale_name ew_scl_db
-}
ew_scl_db :: [T.Scale]
ew_scl_db =
  [ew_1357_3_scl
  ,ew_el12_7_scl
  ,ew_el12_12_scl
  ,ew_hel_12_scl
  ,ew_novarotreediamond_1_scl
  ,ew_Pelogflute_2_scl
  ,ew_xen3b_3_scl
  ,ew_xen456_9_scl
  ,ew_poole_scl
  ,ew_two_22_7_scl
  ]

-- Local Variables:
-- truncate-lines:t
-- End: