{-|
License     :  CC0 1.0 Universal Public Domain Dedication
Maintainer  :  niswegmann@gmail.com
Stability   :  provisional
Portability :  portable (Haskell 2010)
-}

module Algorithms.Majority
  ( majority
  ) where

import Data.List (foldr)
import Prelude hiding (foldr)

-- | /O(length xs)/, Determines if there in a list of votes /xs/ is a candidate
-- that has more than half of the votes, and if so, returns that canditate.
--
-- E.g. applying @majority@ on the string @\"AAACCBBCCCBCC\"@ yields
-- @Just \'C\'@, since @\'C\'@ has 7 out of 13 votes.

majority :: Eq a => [a] -> Maybe a
majority xs = eliminate xs >>= verify xs

-- First pass: eliminates all but a single candidate.
eliminate :: Eq a => [a] -> Maybe a
eliminate []     = Nothing
eliminate (x:xs) = Just (eliminate1 x xs)

eliminate1 :: Eq a => a -> [a] -> a
eliminate1 can0 = fst . foldr step (can0, 1 :: Int)

  where

    {-# INLINE step #-}
    step x (can, cnt)
      | can == x  = (can, succ cnt)
      | cnt == 0  = (x  , 1       )
      | otherwise = (can, pred cnt)

-- Second pass: asserts that the remaining candidate is a majority.
verify :: Eq a => [a] -> a -> Maybe a
verify xs can | verify1 can xs = Just can
              | otherwise      = Nothing

verify1 :: Eq a => a -> [a] -> Bool
verify1 can = (> 0) . foldr step (0 :: Int)

  where

    {-# INLINE step #-}
    step x cnt
      | can == x  = succ cnt
      | otherwise = pred cnt