{-# LANGUAGE BangPatterns, NoImplicitPrelude #-} {-# LANGUAGE MultiWayIf #-} {-# OPTIONS_HADDOCK show-extensions #-} -- | -- Module : Phladiprelio.Rhythmicity.TwoFourth -- Copyright : (c) Oleksandr Zhabenko 2021-2023 -- License : MIT -- Stability : Experimental -- Maintainer : oleksandr.zhabenko@yahoo.com -- -- The module is highly experimental approach to estimate further the rhythmicity of the not very long lists (well, -- not longer than e. g. 30 elements). Is rather computationally expensive, so must be used with caution. If the period -- of rhythm is less than 4 it is not effective. module Phladiprelio.Rhythmicity.TwoFourth where import GHC.Base import GHC.Num (Num,(+),(-),(*),abs) import GHC.Real import GHC.List import Text.Show import Data.List (sort) {-| The data type that is used to mark the syllables accordingly to their importance in general rhythm constituting. More important syllables are marked with the less data constuctors (since the data type has an instance of the 'Ord' type class). Can be used in case of three levels of importance for rhythm constituting with the last, third 'C' level of the syllables which position is thought as not significant (though it actually, is not, but for simplicity). -} data Marker3s = A | B | C deriving (Eq,Ord,Show) {-| Data to specify some quantitative information of the structure of rhythmicity. -} data RhythmBasis = Rhythm { eis :: Int, -- ^ the quantity of the most highlighted values bis :: Int, -- ^ the quantity of some other highlighted values cis :: Int -- ^ the quantity of the values which influence on the rhythmicity is not highlighted } deriving (Eq,Show) {-| Data to specify (mostly) the qualitative information of the structure of rhythmicity. -} data Choices = Ch { cheis :: Int, -- ^ the value 0 is for minimum values, all other ones -- for maximums. chbis :: Int, -- ^ the value 0 is for minimum values among the values for 'Rhythm' 'bis' values, all other ones -- for maximum for the ones. qty :: Int -- ^ general quantity of the elements to be taken as one period. Must be not less than the sum of 'cheis' and 'chbis'. Symbolically, it must be 'Rhythm' 'eis' + 'Rhythm' 'bis' + 'Rhythm' 'cis' = 'Ch' 'qty'. } deriving (Show) instance Eq Choices where (Ch n1 m1 l1) /= (Ch n2 m2 l2) | n1 == 0 && n2 /= 0 = True | n1 /= 0 && n2 == 0 = True | m1 == 0 && m2 /= 0 = True | m1 /= 0 && m2 == 0 = True | l1 /= l2 = True | otherwise = False {-| The predicate to check whether the two given arguments can be used together to get meaningful results. -} validChRhPair :: Choices -> RhythmBasis -> Bool validChRhPair (Ch x y n) (Rhythm p q l) | p <= 0 = False | q < 0 = False | l > 0 = n == p + q + l | otherwise = False {-| The value for the 'Choices' with the two maximum highlighted values and two other ones in the 4-element period. -} twoFourthCh = Ch 1 1 4 {-| Converts the list into the form where its 'rhythmicity' can be estimated further. Uses integer division and, therefore, can be not sensible for the last values in the given list. The length of the list must be not less than the first argument 'qty' value though it is not checked. Therefore, the function is not total. -} getChRhData :: (Ord a) => Choices -> RhythmBasis -- ^ Data specifies the quantities of the syllables on the corresponding levels of importance. -> [a] -> [[Marker3s]] getChRhData choice@(Ch n1 m1 l1) rhythm@(Rhythm p q l) xs | validChRhPair choice rhythm = map (g choice rhythm) . f choice $ xs | otherwise = error "Phladiprelio.Rhythmicity.TwoFourth.getChRhData: the first two arguments cannot be used together to get some meaningful result. " where g ch@(Ch n m l) rh@(Rhythm x y z) us = let ws = sort us in case (x,y,n,m) of (x1,y1,0,0) -> let !k1 = ws !! (x1 - 1) !k2 = ws !! (x1 + y1 - 1) in map (\t -> if | t <= k1 -> A | y1 == 0 -> C | t <= k2 -> B | otherwise -> C) us (x1,y1,_,0) -> let !k1 = ws !! (length us - x1) !k2 | y1 < 2 = head ws | otherwise = ws !! (y1 - 1) in map (\t -> if | t >= k1 -> A | y1 == 0 -> C | t <= k2 -> B | otherwise -> C) us (x1,y1,0,_) -> let !k1 = ws !! (x1 - 1) !k2 | y1 == 0 = last ws | otherwise = ws !! (length us - y1) in map (\t -> if | t <= k1 -> A | y1 == 0 -> C | t >= k2 -> B | otherwise -> C) us (x1,y1,_,_) -> let !k1 = ws !! (length us - x1) !k2 = ws !! (length us - x1 - y1) in map (\t -> if | t >= k1 -> A | y1 == 0 -> C | t >= k2 -> B | otherwise -> C) us f ch@(Ch _ _ l1) ys@(_:_) = let !q = length ys `quot` l1 rs = take (q * l1) ys in f' ch rs f' ch@(Ch _ _ l1) qs@(_:_) = let (ts,zs) = splitAt l1 qs in ts : f' ch zs f' _ [] = [] {-| The function that uses a simple arithmetic logics to calculate the similarity of the two equal by length (if not they are truncated to the least one) lists of 'Marker3s'. It emphasises the 'A' similarity and for most values have not so simply defined 'B'-behaviour. Uses 'similarityLogics' inside. -} similarityABC :: Double -- ^ The positive factor (multiplier) coefficient that increases or decreases the result in case of 'A' simultaneous presence (absence). -> Double -- ^ The addition (positive subtraction) coefficient that increases or decreases (probably) the result in case of 'B' similtaneous presence (absence). -> Double -- ^ The initial value starting from which it counts. Usually, equals to 1.0. -> [[Marker3s]] -> Double -- ^ In case of positive previous 'Double' arguments this is a positive value. The greater one corresponds to (probably) more rhythmic list. similarityABC k1 k2 z (xs:ys:xss) = similarityABC k1 k2 (z * similarityLogics 1 k1 k2 xs ys) (ys:xss) similarityABC _ _ z _ = z {-| The function that uses a simple arithmetic logics to calculate the similarity of the two equal by length (if not they are truncated to the least one) lists of 'Marker3s'. It emphasises the 'A' and 'B' similarities. Uses 'similarityLogics0' inside. The more straightforward variant of the 'similarityABC' function. -} similarityABC0 :: Double -- ^ The positive factor (multiplier) coefficient that increases or decreases the result in case of 'A' simultaneous presence (absence). -> Double -- ^ The addition coefficient that increases the result in case of 'B' similtaneous presence. -> Double -- ^ The initial value starting from which it counts. Usually, equals to 1.0. -> [[Marker3s]] -> Double -- ^ In case of positive previous 'Double' arguments this is a positive value. The greater one corresponds to (probably) more rhythmic list. similarityABC0 k1 k2 z (xs:ys:xss) = similarityABC0 k1 k2 (z * similarityLogics0 1 k1 k2 xs ys) (ys:xss) similarityABC0 _ _ z _ = z {-| The function that uses a simple arithmetic logics to calculate the similarity of the two equal by length (if not they are truncated to the least one) lists of 'Marker3s'. It emphasises the 'A' similarity and for most values have not so simply defined 'B'-behaviour. -} similarityLogics :: Double -- ^ The initial value starting from which it counts. Usually, equals to 1.0. -> Double -- ^ The positive factor (multiplier) coefficient that increases or decreases the result in case of 'A' simultaneous presence (absence). -> Double -- ^ The addition (positive subtraction) coefficient that increases or decreases (probably) the result in case of 'B' similtaneous presence (absence). -> [Marker3s] -> [Marker3s] -> Double -- ^ In case of positive previous 'Double' arguments this is a positive value. The greater one corresponds to (probably) more rhythmic list. similarityLogics x0 k1 k2 (x:xs) (y:ys) = similarityLogics (similarityF1 x y k1 k2 x0) k1 k2 xs ys similarityLogics x0 _ _ _ _ = x0 {-| The more straightforward variant of the 'similarityLogics' function. -} similarityLogics0 :: Double -- ^ The initial value starting from which it counts. Usually, equals to 1.0. -> Double -- ^ The positive factor (multiplier) coefficient that increases or decreases the result in case of 'A' simultaneous presence (absence). -> Double -- ^ The addition coefficient that increases the result in case of 'B' similtaneous presence. -> [Marker3s] -> [Marker3s] -> Double -- ^ In case of positive previous 'Double' arguments this is a positive value. The greater one corresponds to (probably) more rhythmic list. similarityLogics0 x0 k1 k2 (x:xs) (y:ys) = similarityLogics0 (similarityF0 x y k1 k2 x0) k1 k2 xs ys similarityLogics0 x0 _ _ _ _ = x0 similarityF1 :: Marker3s -> Marker3s -> Double -- ^ The positive factor (multiplier) coefficient that increases or decreases the result in case of 'A' simultaneous presence (absence). -> Double -- ^ The addition (positive subtraction) coefficient that increases or decreases (probably) the result in case of 'B' similtaneous presence (absence). -> Double -- ^ The initial value. -> Double similarityF1 m1 m2 k1 k2 x0 | m1 == m2 = case m1 of A -> x0 * k1 B -> x0 + k2 _ -> x0 | m1 == A || m2 == A = x0 / k1 | m1 == B || m2 == B = abs (x0 - k2) | otherwise = x0 {-# INLINE similarityF1 #-} {-| The more straightforward variant of the 'similarityF1' function. -} similarityF0 :: Marker3s -> Marker3s -> Double -- ^ The positive factor (multiplier) coefficient that increases or decreases the result in case of 'A' simultaneous presence (absence). -> Double -- ^ The addition coefficient that increases the result in case of 'B' similtaneous presence. -> Double -- ^ The initial value. -> Double similarityF0 m1 m2 k1 k2 x0 | m1 == m2 = case m1 of A -> x0 * k1 B -> x0 + k2 _ -> x0 | m1 == A || m2 == A = x0 / k1 | otherwise = x0 {-# INLINE similarityF0 #-} {-| General function to estimate the inner rhythmicity of the 'Ord'ered list of values. For many cases its arguments can be guessed or approximated by some reasonable values. Nevertheless, it is a highly experimental one. -} rhythmicityABC :: (Ord a) => Double -- ^ The initial value starting from which it counts. Usually, equals to 1.0. -> Double -- ^ The positive factor (multiplier) coefficient that increases or decreases the result in case of 'A' simultaneous presence (absence). -> Double -- ^ The addition (positive subtraction) coefficient that increases or decreases (probably) the result in case of 'B' similtaneous presence (absence). -> Choices -- ^ Data specifies the structure of the period of rhythmicity -- whether maximum or minimum elements are considered and how many syllables costitute the period. -> RhythmBasis -- ^ Data specifies the quantities of the syllables on the corresponding levels of importance. -> [a] -> Double -- ^ In case of positive previous 'Double' arguments this is a positive value. The greater one corresponds to (probably) more rhythmic list. rhythmicityABC x0 k1 k2 choices rhythm = similarityABC k1 k2 x0 . getChRhData choices rhythm {-# INLINE rhythmicityABC #-} {-| General function to estimate the inner rhythmicity of the 'Ord'ered list of values. For many cases its arguments can be guessed or approximated by some reasonable values. Nevertheless, it is a highly experimental one. The more straightforward variant of the 'rhythmicityABC' function. -} rhythmicityABC0 :: (Ord a) => Double -- ^ The initial value starting from which it counts. Usually, equals to 1.0. -> Double -- ^ The positive factor (multiplier) coefficient that increases or decreases the result in case of 'A' simultaneous presence (absence). -> Double -- ^ The addition coefficient that increases the result in case of 'B' similtaneous presence. -> Choices -- ^ Data specifies the structure of the period of rhythmicity -- whether maximum or minimum elements are considered and how many syllables costitute the period. -> RhythmBasis -- ^ Data specifies the quantities of the syllables on the corresponding levels of importance. -> [a] -> Double -- ^ In case of positive previous 'Double' arguments this is a positive value. The greater one corresponds to (probably) more rhythmic list. rhythmicityABC0 x0 k1 k2 choices rhythm = similarityABC0 k1 k2 x0 . getChRhData choices rhythm {-# INLINE rhythmicityABC0 #-}