packages feed

konnakol-0.2.0.0: Konnakol/Define.hs

module Konnakol.Define where

import Sound.Tidal.Context hiding (s,n)
import System.Random ( getStdGen, Random(randomR), StdGen, mkStdGen )
import Data.String ( IsString(fromString) )
import Data.List ( findIndex, intercalate, intersperse, isPrefixOf, tails )
import System.CPUTime ()

-- | BeatCount consists of the three ways in counting a cycle. Dhruta is typically 2 beats, while
-- Anudhruta is a single beat. The Jati of the Thala defines the Dhruta
data BeatCount = Laghu | Dhruta | Anudhruta deriving(Read)

-- | Define conventional notation used to represent the Thalas
instance Show BeatCount where
    show Laghu = "|"
    show Dhruta = "O"
    show Anudhruta = "U"

-- | Thala is a combination of BeatCounts
newtype Thala = T [BeatCount] deriving (Read)

-- | Standard 7 thalas in the Suladi Sapta Thala system
dhruva::Thala
dhruva = T [Laghu, Dhruta, Laghu, Laghu]
matya::Thala
matya = T [Laghu, Dhruta, Laghu]
rupaka::Thala
rupaka = T [Dhruta, Laghu]
jhampe::Thala
jhampe = T [Laghu, Dhruta, Anudhruta]
thriputa::Thala
thriputa = T [Laghu, Dhruta, Dhruta]
atta::Thala
atta = T [Laghu, Laghu, Dhruta, Dhruta]
eka::Thala
eka = T [Laghu]

-- | Define display of Thala based on that of the BeatCount
instance Show Thala where
    show (T []) = ""
    show (T (x:xs)) = show x ++ show (T xs)

-- | Common class to define both the Jati and Gati
data JatiGati = Tisra | Chaturasra | Khanda | Misra | Sankirna deriving (Show, Eq, Read)

-- | Define instance of enum class by enumerating each Jati / Gati to be the number of beats they represent
instance Enum JatiGati where
    fromEnum a
        | a == Tisra = 3
        | a == Chaturasra = 4
        | a == Khanda = 5
        | a == Misra = 7
        | otherwise =  9
    toEnum a
        | a == 3 = Tisra
        | a == 4 = Chaturasra
        | a == 5 = Khanda
        | a == 7 = Misra
        | a == 9 = Sankirna
        | otherwise = error "Unspecified Length"

-- | Define a class for all syllables used in Konnakol
data Syllable = Tha | Ki | Ta |Di | Dhi | Gi |Jho | Na | Thom |Lan |Gu | Dhin | Ku | Ri | Ka | Tham  | Thak |Dhim | Nam |Mi |Nu| Gdot deriving ( Eq)

-- | Show instance for syllables, to display the gaps using dots
instance Show Syllable where
    show Gdot = "-"
    show Tha = "Tha"
    show Ki = "Ki"
    show Ta = "Ta"
    show Dhi = "Dhi"
    show Gi = "Gi"
    show Jho = "Jho"
    show Na = "Na"
    show Thom = "Thom"
    show Lan = "Lan"
    show Gu = "Gu"
    show Dhin = "Dhin"
    show Ku = "Ku"
    show Ri = "Ri"
    show Ka = "Ka"
    show Tham = "Tham"
    show Thak ="Thak"
    show Dhim = "Dhim"
    show Nam = "Nam"
    show Mi = "Mi"
    show Nu = "Nu"
    show Di = "Di"

-- | Define phrase as a collection of syllables, to faciliate show instance
newtype Phrase = Phr [Syllable]

-- | Show instance for a phrase
instance Show Phrase where
    show (Phr []) = ""
    show (Phr (x:xs)) = show x ++ show (Phr xs)

-- | Define Composition as collection of phrases with changes in speeds
data Comp = C [([Syllable], Int)] | K JatiGati deriving(Show)

-- | Method to show the Thala in the form Notation - Count - Gati
showFinalThala :: JatiGati -> Thala -> JatiGati -> String
showFinalThala jati thala gati =
    let a = show thala
        b = " (" ++ show ( calculateCount jati thala) ++ ") "
        c = " <" ++ show (fromEnum gati )++ "> "
    in a ++ b ++ c

-- | Get the Counts per beat in a certain thala in a particular gati
getCountPerBeat::JatiGati->Int->Int
getCountPerBeat gati maxS
  | maxS == 1 = 1
  | gati == Chaturasra = 2^(maxS - 1)
  | otherwise = fromEnum gati * 2^ max (maxS-2) 0

-- | Method to calculate number of beats in a Thala based on its jati
calculateCount :: JatiGati -> Thala -> Int
calculateCount _ (T []) = 0
calculateCount g (T (x:xs)) =
    case x of Laghu -> fromEnum g + calculateCount g (T xs)
              Dhruta -> 2 + calculateCount g (T xs)
              Anudhruta -> 1 + calculateCount g (T xs)

-- | Method to get appropriate symbols for representation
getThalaSplitPoints :: JatiGati -> Thala  -> [String]
getThalaSplitPoints _ (T [])  = []
getThalaSplitPoints j (T (x:xs))  =
    case x of Laghu -> [" | "] ++ replicate (fromEnum j - 1) "^" ++ getThalaSplitPoints j (T xs)
              Dhruta -> " O ":" ^ ": getThalaSplitPoints j (T xs)
              Anudhruta ->" U " : getThalaSplitPoints j (T xs)

-- | Representing Compositions with changing speeds
getRepresentation:: [Comp] -> JatiGati ->Thala ->Int->String
getRepresentation ((K x):y:xs) jati thala pos  =
     let (a,b) = getStringComp y jati thala (K x) pos
     in "<" ++ show (fromEnum x) ++ ">" ++ a ++ getRepresentation xs jati thala b
getRepresentation _ _ _ _ =""

-- | Driver function for getting a virtual representation of a composition, after validation
getStringComp :: Comp->JatiGati->Thala->Comp->Int-> (String, Int)
getStringComp (C k) jati (T thala) (K gati) pos =
    let maxS = maximum $ map snd k
        countPerBeat = getCountPerBeat gati maxS
        b = calculateCount jati (T thala)
        countPerAvarta = countPerBeat * b
        a = convToList k countPerBeat gati
        d =
         --if mod (length a) countPerBeat == 0 then
            let c = replicate (calculateCount jati (T thala)) countPerBeat
                e = getThalaSplitPoints jati (T thala)
                in (finalDisp a (T thala) c pos countPerBeat e, pos + div (mod (length a) countPerAvarta) countPerBeat )
         --else ("Error", 0)
    in d
getStringComp _ _ _ _ _ = ("",0)

-- | Core function in obtaining string from Composition
convToList :: [([Syllable], Int)] -> Int ->JatiGati-> [Syllable]
convToList [] _ _= []
convToList listas maxs g =
   concatMap (\(x,d)-> concatMap (\t ->  t:replicate (div maxs (getCountPerBeat g d) -1) Gdot) x) listas

-- | Final display of a thala in lines with proper subdivisions
finalDisp :: [Syllable] ->Thala -> [Int] -> Int ->Int->[String]-> String
finalDisp s (T thala) arr n cPB e =
    if null s then ""
    else let pos = mod n (length arr)
             c = e!!pos
             b = if pos == length arr - 1 then "||\n" else ""
        in c ++ show (Phr (take cPB s)) ++ b ++ finalDisp (drop (arr !! pos) s) (T
        thala) arr (n+1) cPB e

-- | Define the standard phrases for different lengths
phrase4len :: Int -> [[Syllable]]
phrase4len 1 = [[Tha], [Dhi], [Thom], [Nam]]
phrase4len 2 = [[Tha, Ka], [Ki, Ta], [Dhi, Mi], [Tha, Ri], [Di, Na], [Gi, Na], [Jho, Nu]]
phrase4len 3 = [[Tha, Ki, Ta], [Tha, Di, Mi], [Tha, Tha, Ka], [Dhi, Na, Ka]]
phrase4len 4 = [[Ki,Ta, Tha, Ka], [Tha, Ri, Ki, Ta], [Tha, Ka, Di, Na], [Tha, Ka, Dhi, Mi], [Tha, Ka, Jho, Nu], [Tha, Lan, Gdot, Gu],
                [Gi, Na, Ki, Ta], [Tha, Di, Mi, Tha]]
phrase4len 5 = [[Tha, Di, Gi, Na, Thom], [Tha, Ka, Tha, Ki, Ta], [Tha, Ka, Tha, Di, Mi], [Tha, Dhi, Mi, Tha, Ka],
                 [Dhi, Na, Ka, Dhi, Mi],[Tha, Tha, Ka, Dhi, Mi]]
phrase4len 6 = [Tha, Dhi, Gdot, Gi, Na, Thom] : [ x++ y | x <- phrase4len 2, y <- phrase4len 4] ++
                [ x++ y | x <- phrase4len 4, y <- phrase4len 2] ++[ x++ y | x <- phrase4len 3, y <- phrase4len 3]
phrase4len 7 = [Tha, Gdot, Dhi, Gdot, Gi, Na, Thom]: [ x++ y | x <- phrase4len 3, y <- phrase4len 4] ++ [ x++ y | x <- phrase4len 4, y <- phrase4len 3]
                ++ [ x++ y | x <- phrase4len 2, y <- phrase4len 5] ++ [ x++ y | x <- phrase4len 5, y <- phrase4len 2]
phrase4len 8 = [[Tha, Dhi, Gdot, Gi, Gdot, Na, Gdot, Thom], [Tha, Ka, Tham, Gdot, Tha, Ri, Ki, Ta],[Dhi,Gdot, Gdot, Gdot, Tham, Gdot, Tha, Ka], [Dhi, Ku, Tha, Ri, Ki, Ta, Tha, Ka],[Tha,Gdot, Dhi, Gdot, Tha, Gdot, Ki, Ta]]++ [ x ++ y ++ z | x <- phrase4len 2, y <- phrase4len 3, z <- phrase4len 3]
                 ++ [ x++ y | x <- phrase4len 4, y <- phrase4len 4] ++ [ x++ y ++z| x <- phrase4len 3, y <- phrase4len 2, z<- phrase4len 3]
                 ++ [ x++ y ++ z  | x <- phrase4len 3, y <- phrase4len 3, z<-phrase4len 2]
phrase4len 9 = [Tha, Gdot, Dhi, Gdot, Gi, Gdot, Na, Gdot, Thom]: [ x++ y | x <- phrase4len 4, y <- phrase4len 5] ++
             [ x++ y | x <- phrase4len 5, y <- phrase4len 4] ++ [ x++ y ++ z  | x <- phrase4len 3, y <- phrase4len 3, z<-phrase4len 3]
             ++ [ x++ y ++ z  | x <- phrase4len 2, y <- phrase4len 3, z<-phrase4len 4]
phrase4len 16 = [[Dhi, Gdot,Gdot, Gdot, Tham, Gdot, Tha, Ka, Dhi, Ku, Tha, Ri, Ki, Ta, Tha, Ka],
                    [Tha, Ka, Thom, Gdot, Tham, Gdot, Tha, Ka, Dhi, Ku, Tha, Ri, Ki, Ta, Tha, Ka]]
phrase4len _ =[]

-- | Function to randomly select a phrase of a specified length. Defined recursively for phrases
-- of length greater than 9
phraseGenerator::Int-> StdGen-> ([Syllable], StdGen)
phraseGenerator x gen =
    if x < 10|| x==16 then let (y,z) =randomR (0, length(phrase4len x) - 1) gen
                    in  (phrase4len x !! y, z)
        else let (a, newgen) = randomR (2, x - 2) gen
                 (b, c) = genPhrase4Me a newgen
                 (d, e) = genPhrase4Me (x - a) c
            in if a > x - a then (d++b, e) else (b++d, e)

-- | Function to decide whether the phrase has to be generated with breaks or without them
genPhrase4Me ::Int-> StdGen-> ([Syllable], StdGen)
genPhrase4Me x tossgen =
    if x == 0 then ([], tossgen) else
  let (a,gen) = randomR (1,10) tossgen ::(Int, StdGen)
  in if a <= 8||x == 1
        then phraseGenerator x gen
    else let factor = head (filter (\y->mod x y==0) [2,3..])
             (one, two) = if factor >=4 then let (y, newgen) = randomR (2, x-2) gen
                                                 (b, c) = genPhrase4Me y newgen
                                                 (d, e) = genPhrase4Me (x - y) c
                                        in if y > x - y then (d ++ b, e) else (b ++ d, e)
                        else let (pha, c) = phraseGenerator (div x factor) gen
                                 b = concatMap (\t->t:replicate (factor - 1) Gdot) pha
                            in (b,c)
        in (one, two)

-- | To randomly generate different phrases
genValues :: [Int] -> StdGen -> ([[Syllable]], StdGen)
genValues [] gen = ([], gen)
genValues [x] gen =
    let (a,b) = genPhrase4Me x gen
        in ([a],b)
genValues (x:xs) gen =
    let (a,newgen) = genValues xs gen
        exLength = if null xs then 0 else head xs
        (b, newgen2) = genPhrase4Me (x - exLength) newgen
    in ((b++head a) :a, newgen2)

-- | Define the constant phrase of a Mohra with respect to the given length
mohrad::JatiGati -> [Syllable]
mohrad Tisra = [Tha, Lan, Gdot, Gu, Dhin, Gdot, Thak, Gdot, Dhin, Gdot, Gdot, Gdot,Tha, Lan, Gdot, Gu, Dhin, Gdot, Thak, Gdot, Dhin, Gdot, Gdot, Gdot]
mohrad Chaturasra = [Tha, Lan, Gdot, Gu, Dhin, Gdot, Thak, Gdot, Tha, Lan, Gdot,Gu, Dhin, Gdot, Gdot, Gdot]
mohrad Khanda = [Tha, Lan, Gdot, Gu, Dhin,Gdot,  Thak, Gdot, Dhin, Gdot, Tha, Lan, Gdot, Gu, Dhin, Gdot,Thak, Gdot, Dhin, Gdot]
mohrad Misra = [Tha, Lan, Gdot, Gu, Dhin, Gdot, Thak,Gdot, Tha, Lan, Gdot,Gu, Dhin, Gdot ]
mohrad Sankirna = [Tha, Lan, Gdot, Gu, Dhin, Gdot, Thak, Gdot, Tha, Lan, Gdot, Gu, Dhin, Gdot, Dhin, Gdot, Gdot, Gdot]

-- | Define one of the final constants used in the Mohra
mohraC1::JatiGati -> [Syllable]
mohraC1 Chaturasra = [Tha, Lan, Gdot, Gu, Dhin, Gdot, Gdot, Gdot]
mohraC1 Tisra = [Tha, Lan, Gdot, Gu, Dhin, Gdot, Thak, Gdot, Dhin, Gdot, Gdot, Gdot]
mohraC1 Khanda = [Tha, Lan, Gdot, Gu, Dhin, Gdot, Thak, Gdot, Dhin, Gdot]
mohraC1 Misra = [Tha, Lan, Gdot, Gu, Dhin,Gdot, Gdot]
mohraC1 Sankirna = [Tha, Lan, Gdot, Gu, Dhin, Gdot, Gdot, Gdot, Gdot]

-- | Define the constant used to end the Mohra
mohraC2 :: JatiGati -> [Syllable]
mohraC2 Tisra = concat (replicate 3 [Tha, Lan, Gdot, Gu, Dhin, Gdot, Thak, Gdot, Dhin, Gdot, Thak, Gdot, Dhin, Gdot, Gdot, Gdot, Gu, Gdot]) ++ replicate 6 Gdot
mohraC2 Chaturasra =concat (replicate 3 [Tha, Lan, Gdot, Gu, Dhin, Gdot, Thak, Gdot, Dhin, Gdot, Gdot, Gdot]) ++ replicate 4 Gdot
mohraC2 Khanda = concat( replicate 3 [Tha, Lan, Gdot, Gu, Dhin, Gdot,Thak, Gdot, Dhin, Gdot, Thak, Gdot, Dhin, Gdot, Gu, Gdot]) ++ [Gdot, Gdot]
mohraC2 Misra = concat (replicate 3 [Tha, Lan, Gdot, Gu, Dhin, Gdot, Thak, Gdot, Dhin, Gdot]) ++ replicate 5 Gdot
mohraC2 Sankirna = concat (replicate 3 [Tha, Lan, Gdot, Gu, Dhin, Gdot, Thak, Gdot, Dhin, Gdot, Gdot, Gdot, Gu, Gdot]) ++ replicate 3 Gdot

-- | To obtain the right speed for generation of Mohra with respect to its gati/ nadai
getMohraSpeed::JatiGati->Int
getMohraSpeed gati
    | gati == Chaturasra = 4
    | gati == Tisra = 4
    | otherwise = 3

-- | To get the appropriate separations for a required mohra generation
getMohraSeparation::Int->JatiGati->[Int]
getMohraSeparation count gati =
    let scnd = length $ mohrad gati
        frst =div (count - 2*scnd) 2
    in [frst, scnd, frst, scnd]

-- | To generate a Mohra on the basis of the jati, thala and gati
genMohra::JatiGati->Thala->JatiGati->StdGen->[Syllable]
genMohra jati thala gati gen=
    let sp = getMohraSpeed gati
        phd = mohrad gati
        overAllCount = if calculateCount jati thala<= 4 then 2*calculateCount jati thala
                        else calculateCount jati thala
        [a,b,_,_] = getMohraSeparation (getCountPerBeat gati sp*overAllCount) gati
        (pha, gen1) = if gati==Chaturasra || gati == Misra || gati == Sankirna|| mod a 2 ==1 then genPhrase4Me a gen else genPhrase4Me (div a 2) gen
        pham = if gati == Chaturasra || gati == Misra || gati == Sankirna|| mod a 2 == 1 then pha else intersperse Gdot pha ++ [Gdot]
        (phb, _ ) = if gati==Chaturasra || gati == Misra || gati == Sankirna || mod a 2 == 1 then genPhrase4Me b gen1 else genPhrase4Me (div b 2) gen1
        phbm = if gati == Chaturasra || gati == Misra || gati == Sankirna || mod a 2 == 1 then phb else intersperse Gdot phb ++ [Gdot]
        c1 = mohraC1 gati
        c2 = mohraC2 gati
        derMohra = concat [pham, phbm, pham, phd, pham, phbm, pham, phd, pham, phbm, pham, c1,pham, c1, pham,c2]
    in derMohra

-- | To read the thala from user input
getThala :: String->Thala
getThala "Dhruva" = dhruva
getThala "Matya" = matya
getThala "Eka" = eka
getThala "Rupaka" = rupaka
getThala "Thriputa" = thriputa
getThala "Atta" = atta
getThala "Jhampe" = jhampe
getThala _ = eka



getGPh :: Int -> [Syllable]
getGPh 0 = []
getGPh 1 = [Dhi]
getGPh 2 = [Dhin, Gdot]
getGPh 3 = [Dhin, Gdot, Gu]
getGPh 4 = [Dhin, Gdot, Dhin, Gdot]
getGPh 5 = getGPh 2 ++ getGPh 3
getGPh 6 = concat $ replicate 2 (getGPh 3)
getGPh 7 = getGPh 4 ++ getGPh 3
getGPh 8 = getGPh 3 ++ getGPh 5
getGPh _ = error "Undesired length"

-- composing a smaller subpattern for a Korvai without the extra gap
getSub:: Int-> StdGen -> ([Syllable], [JustNums])
getSub s gen =
    let vals1 = [([x, x + d..(x + (n -1)*d)],g) | x <- [1,2..(div s 3)], d <- [2,3..8], g<- 0:[2,3..8], n <-[3,4, 5], n*x + (n-1)*g + (div (n*(n-1)) 2) *d ==s]
        vals2 = map (\(a,b) -> (reverse a,b)) vals1
        vals = vals1 ++ vals2
        (pos, gen2 ) = randomR (0, length vals - 1) gen :: (Int, StdGen)
        (phs, gp) = if null vals then ([s],0) else vals !! pos
        (ph1, _, nList)
          | minimum phs > 15  =
             let fl = map (`getSub` gen2) phs
                 fl1 = map fst  fl
                 fl3 = map snd fl
            in (fl1, gen, fl3)
          | phs !! 1 < head phs =let (a,b) = genValues phs gen2 in (a,b,map (\x -> map P [x]) phs)
          | otherwise = let (a, b) = genValues (reverse phs) gen2 in (reverse a, b, map (\x ->map P [x]) phs)
        ph4 = getGPh gp
        gpNums = [G gp | gp /= 0]
        finPh = intercalate ph4 ph1
        finJustNums = intercalate gpNums nList
    in (finPh , finJustNums )

-- | To generate the purvardha for a given length for the Korvai
getPurvardha::Int->StdGen->([Syllable], StdGen, [JustNums])
getPurvardha s gen =
    let vals1 = [([x, x + d..(x + (n -1)*d)],g) | x <- [1,2..(div s 3)], d <- [2,3..8], g<- [2,3..8], n <-[3,4, 5], n*x + n*g + (div (n*(n-1)) 2) *d ==s]
        vals2 = map (\(a,b) -> (reverse a,b)) vals1
        vals3 = [([x,x,x], g) | x<- [1,2..(div s 3)], g<- 0:[2,3..8], 3*x + 3*g == s]
        vals4 = [(map (\k -> x*d^k) [0,1..(n-1)], g) | x <- [1,2..(div s 3)] , d <- [2,3,4,5], g <- [0,2,3,4,5], n <- [3,4,5], x*(div (d^n - 1) (d -1)) + n*g == s ]
        vals = vals1 ++ vals2 ++ vals3 ++ vals4
        (pos, gen2 ) = randomR (0, length vals - 1) gen :: (Int, StdGen)
        (phs, gp) = if null vals then ([s],0) else vals !! pos
        (ph1, newgen, nList)
          | minimum phs > 15  =
             let fl = map (`getSub` gen2) phs
                 fl1 = map fst  fl
                 fl3 = map snd fl
            in (fl1, gen, fl3)
          | phs !! 1 < head phs =let (a,b) = genValues phs gen2 in (a,b,map (\x -> map P [x]) phs)
          | otherwise = let (a, b) = genValues (reverse phs) gen2 in (reverse a, b, map (\x -> map P [x]) phs)
        ph4 = getGPh gp
        gpNums = [G gp | gp /= 0]
        finPh = intercalate ph4 ph1
        finJustNums = intercalate gpNums nList
    in (finPh ++ ph4, newgen, finJustNums ++ gpNums)

-- | To generate an Uttarardha which has a fixed structure
getUttar :: Int-> ([Syllable], [JustNums])
getUttar len =
    let factor = head (filter (\y->mod (len - 2 * y) 3==0 && (len - 2*y) <= 27) (0:[2,3..8]))
        gapPhrase = if factor == 0  then [] else Dhin:replicate (factor - 1) Gdot
        lenphrase = div (len - 2*factor) 3
        mainPhrase = head (phrase4len lenphrase)
    in (mainPhrase ++ gapPhrase ++ mainPhrase ++ gapPhrase ++ mainPhrase, [P lenphrase, G factor, P lenphrase, G factor, P lenphrase])

-- | To generate an Uttarardha which has a varying structure
getUttarVarying :: Int -> ([Syllable], [JustNums])
getUttarVarying len =
    let factor = head (filter (\y->mod (len - 2 * y) 6==0 && ((len - 2*y) <= 54 || mod (len - 2*y) 9 ==0) && (len - 2*y) >= 30) [1,2,3,4,5,6,7,8])
        gapPhrase = if factor == 0  then [] else Dhin:replicate (factor - 1) Gdot
        lenphrase = if (len -2*factor) <= 54 then div (len - 2*factor) 6 else div (len - 2*factor) 9
        mainPhrase = head (phrase4len lenphrase)
    in if len - 2*factor <=54 then (mainPhrase ++ gapPhrase ++ mainPhrase ++ mainPhrase ++ gapPhrase ++ mainPhrase ++ mainPhrase ++ mainPhrase,
                                        [P lenphrase, G factor, P lenphrase, P lenphrase, G factor, P lenphrase, P lenphrase, P lenphrase ])
        else (mainPhrase ++ mainPhrase ++ mainPhrase ++ gapPhrase ++ mainPhrase ++ mainPhrase ++ mainPhrase ++ gapPhrase ++mainPhrase ++ mainPhrase ++ mainPhrase,
            [P lenphrase, P lenphrase, P lenphrase, G factor, P lenphrase, P lenphrase, P lenphrase, G factor, P lenphrase, P lenphrase, P lenphrase ])

-- | Core function to generate a desired Korvai
genKorvai::JatiGati -> Thala -> JatiGati -> StdGen -> ([Syllable], [JustNums])
genKorvai jati thala gati gen=
    let sp = getMohraSpeed gati -1
        avarta = calculateCount jati thala*getCountPerBeat gati sp
        counts = 4*avarta
        overallCount = 2* counts
        (totPur', gen1) = randomR (div (overallCount - 50) 2, div (overallCount - 15) 2) gen :: (Int, StdGen)
        totPur = totPur' * 2
        totUtt = overallCount -  totPur
        (purva, _, nList) =getPurvardha totPur gen1
        (uttara, nList2) = if even totUtt && totUtt >=32 then getUttarVarying totUtt else getUttar totUtt
        in (purva ++ uttara ,  nList ++ nList2)

-- | New datatype for users to input compositions as numbers
data UIComp = Ph [(Int, Int)] | Gp [(Int, Int)] | Tc JatiGati

-- | Method to convert compositions into a format which can be used by getRepresentation
concatPhGp :: [Comp] -> [Comp]
concatPhGp ((C a):(C b):t) = concatPhGp (C (a ++ b) : t)
concatPhGp (x: t) = x: concatPhGp t
concatPhGp [] = []

-- | Method to generate phrases for a composition
genComp::[UIComp] -> JatiGati -> Thala ->StdGen -> [Comp]
genComp ((Tc x):y ) jati thala gen =K x:genComp y jati thala gen
genComp ((Ph t:y)) jati thala gen =
    let b = foldl (\(acc, gen1) (p,q) -> ((fst (genPhrase4Me p gen1),q):acc, snd(genPhrase4Me p gen1))) ([], gen) t
        a = fst b
        in C a:genComp y jati thala (snd b)
genComp ((Gp t: y)) jati thala gen =
    let a  = map(\(p,q) -> (Dhi:replicate (p - 1) Gdot,q)) t
        in C a: genComp y jati thala gen
genComp [] _ _ _  = []

-- | Helper function to check for substring
findString :: (Eq a) => [a] -> [a] -> Maybe Int
findString search str = findIndex (isPrefixOf search) (tails str)

-- | Core function which validates a string and prints if valid
compValidator :: [UIComp] -> JatiGati -> Thala -> StdGen -> String
compValidator comp jati thala gen =
    let actcomp = concatPhGp $ genComp comp jati thala gen
        stringedcomp = getRepresentation actcomp jati thala 0
        valComp = if findString "Error" stringedcomp == Nothing then stringedcomp else "Error"
    in valComp

-- | Datatype for users to enter values for Korvais
data JustNums = P Int | G Int| B deriving(Show)

-- | Simplify Korvais to the maximum extent
groupPhs::[JustNums] -> [JustNums]
groupPhs [] = []
groupPhs (P a:P b:xs) = groupPhs (P (a+b): xs)
groupPhs (G a:G b: xs) = groupPhs ( G (a + b) : xs)
groupPhs (G a:B : xs) = groupPhs(G a:xs)
groupPhs (x:xs) = x:groupPhs xs

-- | To convert a simplified Korvai to its numerical equivalent
getNums::[JustNums]->[Int]
getNums [] = []
getNums ((P a):xs) = a:getNums xs
getNums ((G a) : xs) = a:getNums xs
getNums (B:xs) = 0:getNums xs

-- | Validate the second half of the Korvai
validateUttar :: [Int]  -> Bool
validateUttar arr =
    let a = head arr
        b = arr !! 2
        c = arr !! 4
    in (arr!! 1 == arr !!3) && ((c - b) == (b -  a)) && (a<=10 || mod a 3 ==0) && (b<=10 || mod b 3 == 0) && (c <= 10 || mod c 3 ==0)

-- | Validate the overall Korvai
validateKorvai :: [JustNums] -> JatiGati -> Thala -> JatiGati-> Bool
validateKorvai arr jati thala gati =
    let narr = getNums $ groupPhs arr
        totsum = sum narr
        nrev = reverse narr
        bool1 = validateUttar nrev
        npur = reverse $ drop 5 nrev
        pursum = sum npur
        vals1 = [([x, x + d..(x + (n -1)*d)],g) | x <- [1,2..(div pursum 3)], d <- [2..(div pursum 4)], g<- [2,3,4,5], n <-[3,4, 5], n*x + n*g + (div (n*(n-1)) 2) *d == pursum]
        vals2 = map (\(a,b) -> (reverse a,b)) vals1
        vals3 = [([x,x,x], g) | x<- [1,2..(div pursum 3)], g<-[0,2,3,4,5], 3*x + 3*g == pursum]
        vals4 = [(map (\k -> x*d^k) [0,1..(n-1)], g) | x <- [1,2..(div pursum 3)] , d <- [2,3,4,5], g <- [0,2,3,4,5], n <- [3,4,5], x*(div (d^n - 1) (d -1)) + n*g == pursum ]
        vals = map (\(l, g) -> intersperse g l ++ [g]) (vals1 ++ vals2 ++ vals3 ++ vals4)
        bool2 = elem npur vals
        sp = if gati ==Tisra then getMohraSpeed gati -1 else getMohraSpeed gati
        avarta = calculateCount jati thala*getCountPerBeat gati sp
        bool3 = mod totsum avarta == 0
    in bool1 && bool2 && bool3

main :: IO()
main = do
    value1 <- getLine
    value2 <- getLine
    value3 <- getLine
    value4 <- getLine
    gen <- getStdGen
    let
        x = (read value1::JatiGati)
        y = getThala value2
        z = (read value3:: JatiGati)
        ch = (read value4 :: Int)
        w =  if ch==1 then getRepresentation [K z, C [(fst (genKorvai x y z gen), getMohraSpeed z - 1)]] x y 0
        else getRepresentation [K z, C [(genMohra x y z gen, getMohraSpeed z)]] x y 0
    putStrLn w


-- | Representing Compositions with changing speeds
getRT:: [Comp] -> JatiGati ->Thala ->Int->String
getRT ((K x):y:xs) jati thala pos  =
     let (a,b) = getSCT y jati thala (K x) pos
     in a ++ getRT xs jati thala b
getRT _ _ _ _ =""

-- | Driver function for getting a virtual representation of a composition, after validation
getSCT :: Comp->JatiGati->Thala->Comp->Int-> (String, Int)
getSCT (C k) jati (T thala) (K gati) pos =
    let maxS = maximum $ map snd k
        countPerBeat = getCountPerBeat gati maxS
        b = calculateCount jati (T thala)
        countPerAvarta = countPerBeat * b
        a = convToList k countPerBeat gati
        d =
         --if mod (length a) countPerBeat == 0 then
            let c = replicate (calculateCount jati (T thala)) countPerBeat
                e = getThalaSplitPoints jati (T thala)
                in (finalDT a (T thala) c pos countPerBeat e, pos + div (mod (length a) countPerAvarta) countPerBeat )
         --else ("Error", 0)
    in d
getSCT _ _ _ _ _ = ("",0)

-- | Final display of a thala in lines with proper subdivisions
finalDT :: [Syllable] ->Thala -> [Int] -> Int ->Int->[String]-> String
finalDT s (T thala) arr n cPB e =
    if null s then ""
    else let pos = mod n (length arr)
        in "[" ++ showT (take cPB s) ++"] " ++ finalDT (drop (arr !! pos) s) (T thala) arr (n+1) cPB e

-- | Function which returns compositions in mini-notation
showT::[Syllable] -> String
showT [] = ""
showT (Gdot:xs) = "~ " ++ showT xs
showT (Tha:xs) = "tha " ++ showT xs
showT (Ki:xs) = "ki " ++ showT xs
showT (Ta:xs) = "ta " ++ showT xs
showT (Di:xs) = "dhi " ++ showT xs
showT (Dhi:xs) = "dhi " ++ showT xs
showT (Gi:xs) = "ki " ++ showT xs
showT (Jho:xs) = "thom " ++ showT xs
showT (Na:xs) = "na " ++ showT xs
showT (Thom:xs) = "thom " ++ showT xs
showT (Lan:xs) = "c " ++ showT xs
showT (Gu:xs) = "gumki " ++ showT xs
showT (Dhin:xs) = "dhin " ++ showT xs
showT (Ku:xs) = "ac " ++ showT xs
showT (Ri:xs) = "c " ++ showT xs
showT (Ka:xs) = "ka " ++ showT xs
showT (Tham:xs) = "ac " ++ showT xs
showT (Thak:xs) = "c " ++ showT xs
showT (Dhim:xs) = "dhin " ++ showT xs
showT (Nam:xs) = "nam " ++ showT xs
showT (Mi:xs) = "c " ++ showT xs
showT (Nu:xs) = "ac " ++ showT xs

-- | Function to return a desired Korvai in mini-notation
tidalK :: JatiGati -> Thala -> JatiGati -> StdGen ->ControlPattern
tidalK jati thala gati gen =
    let x = getRT [K gati, C[(fst(genKorvai jati thala gati gen), getMohraSpeed gati - 1)]] jati thala 0
        y = (length.filter (=='[') ) x
    in (slow (fromInteger $ toInteger y) $ sound (fromString x))

-- | Function to return a desired Mohra in mini-notation
tidalM :: JatiGati -> Thala -> JatiGati -> StdGen -> ControlPattern
tidalM jati thala gati gen =
    let x = getRT [K gati, C [(genMohra jati thala gati gen, getMohraSpeed gati - 1)]] jati thala 0
        y = (length.filter (=='[') ) x
    in (slow (fromInteger $ toInteger y) $ sound (fromString x))

-- Method to convert a composition into a pattern Sequence
-- Converting getRT would be pretty easy I guess in this regard

-- | Representing Compositions with changing speeds as lists of lists of syllables
getSeqRep:: [Comp] -> JatiGati ->Thala ->Int->[[Syllable]]
getSeqRep ((K x):y:xs) jati thala pos  =
     let (a,b) = getSCTSeq y jati thala (K x) pos
     in a ++ getSeqRep xs jati thala b
getSeqRep _ _ _ _ =[]

-- | Driver function for getting a list representation of a composition, after validation
getSCTSeq :: Comp->JatiGati->Thala->Comp->Int-> ([[Syllable]], Int)
getSCTSeq (C k) jati (T thala) (K gati) pos =
    let maxS = maximum $ map snd k
        countPerBeat = getCountPerBeat gati maxS
        b = calculateCount jati (T thala)
        countPerAvarta = countPerBeat * b
        a = convToList k countPerBeat gati
        d =
         --if mod (length a) countPerBeat == 0 then
            let c = replicate (calculateCount jati (T thala)) countPerBeat
                in (finalDTSeq a (T thala) c pos countPerBeat, pos + div (mod (length a) countPerAvarta) countPerBeat )
         --else ("Error", 0)
    in d
getSCTSeq _ _ _ _ _ = ([[]],0)

-- | Final display of a composition as a list of lists of syllables with proper subdivisions
finalDTSeq :: [Syllable] ->Thala -> [Int] -> Int ->Int-> [[Syllable]]
finalDTSeq s (T thala) arr n cPB=
    if null s then [[]]
    else let pos = mod n (length arr)
        in  take cPB s : finalDTSeq (drop (arr !! pos) s) (T thala) arr (n+1) cPB

-- | Function which takes a list of list of syllables and converts each list to a Sequence 
-- getSeqfromKon :: [[Syllable]] -> Rational  -> [Sequence Syllable]
-- getSeqfromKon [] _  = []
-- getSeqfromKon (x:xs) durat =
--     let y = durat / realToFrac (length x)
--         a = map (\t -> if t == Gdot then Gap y else Atom y t) x
--     in Sequence a : getSeqfromKon xs durat

-- -- | Function to return a desired Korvai in Sequence notation
-- sequenceK :: JatiGati -> Thala -> JatiGati -> StdGen ->Sequence Syllable
-- sequenceK jati thala gati gen =
--     let x = getSeqRep [K gati, C[(fst(genKorvai jati thala gati gen), getMohraSpeed gati - 1)]] jati thala 0
--         y = getSeqfromKon x 1
--     in Sequence.unwrap $ Sequence y

-- -- | Function to return a desired Mohra in Sequence notation
-- sequenceM :: JatiGati -> Thala -> JatiGati -> StdGen -> Sequence Syllable
-- sequenceM jati thala gati gen =
--     let x = getSeqRep [K gati, C [(genMohra jati thala gati gen, getMohraSpeed gati - 1)]] jati thala 0
--         y = getSeqfromKon x 1
--     in Sequence.unwrap $ Sequence y


-- Mridangam samples (c) Arthur Carabott, distributed under a CC-BY-SA license https://creativecommons.org/licenses/by-sa/4.0/