-- |
-- Module      : Mas_repetido
-- Description : Elemento con más repeticiones consecutivas.
-- Copyright   : Exercitium (20-05-14)
-- License     : GPL-3
-- Maintainer  : JoseA.Alonso@gmail.com
-- 
-- Definir la función
-- 
-- > masRepetido :: Ord a => [a] -> (a,Int)
--
-- tal que __(masRepetido xs)__ es el mayor elemento de xs que aparece
-- más veces de manera consecutiva en la lista junto con el número de sus
-- apariciones consecutivas. Por ejemplo,
-- 
-- >>> masRepetido [1,1,4,4,1]
-- (4,2)
-- >>> masRepetido [4,4,1,1,4]
-- (4,2)
-- >>> masRepetido "aadda"
-- ('d',2)
-- >>> masRepetido "a"
-- ('a',1)
-- >>> masRepetido "ba"
-- ('b',1)

{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}

module Mas_repetido where

import Data.List (group, maximumBy)
import Data.Function (on)
import Data.Tuple (swap)
import Control.Arrow ((&&&))
import Test.QuickCheck

-- | 1ª definición (por recursión)
masRepetido :: Ord a => [a] -> (a,Int)
masRepetido = maximum . masRepetidos

-- | __(masRepetidos xs)__ es la lista de los elementos de xs con más
-- repeticiones consecutivas junto con el número de sus repeticiones
-- consecutivas. Por ejemplo,
--
-- >>> masRepetidos "aabxxbccb"
-- [('a',2),('x',2),('c',2)]
masRepetidos :: Ord a => [a] -> [(a,Int)]
masRepetidos []  = []
masRepetidos [x] = [(x,1)]
masRepetidos (x:y:zs) | m > n     = [(x,m)]
                      | m == n    = (x,m):vs
                      | otherwise = vs
  where vs@((_,n):_) = masRepetidos (y:zs)
        m            = length (takeWhile (==x) (x:y:zs))

-- | 2ª definición (con 'group' y 'maximum').
masRepetido2 :: Ord a => [a] -> (a,Int)
masRepetido2 xs = (n,z)
  where (z,n) = maximum [(1 + length ys,y) | (y:ys) <- group xs]
  
-- | 3ª definición (con 'group', 'maximum' y 'swap').
masRepetido3 :: Ord a => [a] -> (a,Int)
masRepetido3 xs = 
  swap (maximum [(1 + length ys,y) | (y:ys) <- group xs])

-- | 4ª definición (con 'group' y 'maximumBy').
masRepetido4 :: Ord a => [a] -> (a,Int)
masRepetido4 xs =  
  maximumBy compara [(y,1 + length ys) | (y:ys) <- group xs]
  where compara (a,n) (b,m) = compare (n,a) (m,b)

-- | 5ª definición (sin argumentos)
masRepetido5 :: Ord a => [a] -> (a,Int)
masRepetido5 =
  maximumBy (compare `on` (snd &&& fst)) . map (head &&& length) . group

-- | __(prop_masRepetido xs)__ se verifica si las definiciones de
-- masRepetido son equivalentes sobre xs. Por ejemplo,
-- 
-- >>> quickCheck prop_masRepetido
-- +++ OK, passed 100 tests.
prop_masRepetido :: (NonEmptyList Int) -> Bool
prop_masRepetido (NonEmpty xs) =
  all (== masRepetido xs)
      [f xs | f <- [ masRepetido2
                   , masRepetido3
                   , masRepetido4
                   , masRepetido5
                   ]]