content: import Data.Monoid
  import Control.Monad.Writer
  import Control.Monad.Instances
  import Control.Monad.State
  import System.Random
  import Control.Monad.Error
  import Control.Applicative
  import Data.Ratio
  import Data.List
  
  isBigGang :: Int -> (Bool, String)
  isBigGang x = (x > 9, "Compared gang size to 9.")
  
  applyLog :: (Monoid m) => (a, m) -> (a -> (b, m)) -> (b, m)
  applyLog (x, log) f = let (y, newLog) = f x in (y, log `mappend` newLog)
  
  type Food = String
  type Price = Sum Int
  
  addDrink :: Food -> (Food, Price)
  addDrink "beans" = ("milk", Sum 25)
  addDrink "jerky" = ("whiskey", Sum 99)
  addDrink _ = ("beer", Sum 30)
  
  logNumber :: Int -> Writer [String] Int
  logNumber x = Writer (x, ["Got number: " ++ show x])
  
  multWithLog :: Writer [String] Int
  multWithLog = do
    a <- logNumber 3
    b <- logNumber 5
    tell ["Gonna multiply these two"]
    return (a * b)
  
  gcd' :: Int -> Int -> Writer [String] Int
  gcd' a b
    | b == 0    = do
      tell ["Finished with " ++ show a]
      return a
    | otherwise = do
      tell [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)]
      gcd' b (a `mod` b)
  
  gcdReverse :: Int -> Int -> Writer [String] Int
  gcdReverse a b
    | b == 0    = do
      tell ["Finished with " ++ show a]
      return a
    | otherwise = do
      result <- gcdReverse b (a `mod` b)
      tell [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)]
      return result
  
  newtype DiffList a = DiffList { getDiffList :: [a] -> [a] }
  
  toDiffList :: [a] -> DiffList a
  toDiffList xs = DiffList (xs ++)
  
  fromDiffList :: DiffList a -> [a]
  fromDiffList (DiffList f) = f []
  
  instance Monoid (DiffList a) where
    mempty = DiffList (\xs -> [] ++ xs)
    (DiffList f) `mappend` (DiffList g) = DiffList (\xs -> f (g xs))
  
  gcdReverse' :: Int -> Int -> Writer (DiffList String) Int
  gcdReverse' a b
    | b == 0    = do
      tell (toDiffList ["Finished with " ++ show a])
      return a
    | otherwise = do
      result <- gcdReverse' b (a `mod` b)
      tell (toDiffList [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)])
      return result
  
  finalCountDown :: Int -> Writer (DiffList String) ()
  finalCountDown 0 = do
    tell (toDiffList ["0"])
  finalCountDown x = do
    finalCountDown (x - 1)
    tell (toDiffList [show x])
  
  finalCountDown' :: Int -> Writer [String] ()
  finalCountDown' 0 = do
    tell ["0"]
  finalCountDown' x = do
    finalCountDown' (x - 1)
    tell [show x]
  
  addStuff :: Int -> Int
  addStuff = do
    a <- (* 2)
    b <- (+ 10)
    return (a + b)
  
  addStuff' :: Int -> Int
  addStuff' x = let
    a = (* 2) x
    b = (+ 10) x
    in a + b
  
  type Stack = [Int]
  
  pop :: State Stack Int
  pop = State $ \(x:xs) -> (x, xs)
  
  push :: Int -> State Stack ()
  push a = State $ \xs -> ((), a:xs)
  
  stackManip :: State Stack Int
  stackManip = do
    push 3
    pop
    pop
  
  stackStuff :: State Stack ()
  stackStuff = do
    a <- pop
    if a == 5
       then push 5
       else do
           push 3
           push 8
  
  moreStack :: State Stack ()
  moreStack = do
    a <- stackManip
    if a == 100
       then stackStuff
       else return ()
  
  stackyStack :: State Stack ()
  stackyStack = do
    stackNow <- get
    if stackNow == [1,2,3]
       then put [8,3,1]
       else put [9,2,1]
  
  randomSt :: (RandomGen g, Random a) => State g a
  randomSt = State random
  
  threeCoins :: State StdGen (Bool, Bool, Bool)
  threeCoins = do
    a <- randomSt
    b <- randomSt
    c <- randomSt
    return (a, b, c)
  
  type Birds = Int
  type Pole = (Birds, Birds)
  
  landLeft :: Birds -> Pole -> Either String Pole
  landLeft n (left, right)
      | abs ((left + n) - right) < 4 = Right (left + n, right)
      | otherwise                    = Left (show (left + n) ++ ":" ++ show right)
  
  landRight :: Birds -> Pole -> Either String Pole
  landRight n (left,right)
      | abs (left - (right + n)) < 4 = Right (left, right + n)
      | otherwise                    = Left (show right ++ ":" ++ show (left + n))
  
  routine :: Either String Pole  
  routine = do  
      start <- return (0,0)  
      first <- landLeft 2 start  
      Left "banana!"
      second <- landRight 2 first  
      landLeft 1 second
  
  keepSmall :: Int -> Writer [String] Bool
  keepSmall x
    | x < 4 = do
      tell ["Keeping " ++ show x]
      return True
    | otherwise = do
      tell [show x ++ " is too large"]
      return False
  
  powerset :: [a] -> [[a]]
  powerset xs = filterM (\x -> [True, False]) xs
  
  binSmalls :: Int -> Int -> Maybe Int
  binSmalls acc x
    | x > 9   = Nothing
    | otherwise = Just (acc + x)
  
  newtype Prob a = Prob { getProb :: [(a, Rational)] } deriving Show
  
  instance Functor Prob where
    fmap f (Prob xs) = Prob $ map (\(x, p) -> (f x, p)) xs
  
  thisSituation :: Prob (Prob Char)
  thisSituation = Prob
                  [(Prob [('a',1%2),('b',1%2)], 1%4),
                   (Prob [('c',1%2),('d',1%2)], 3%4)]
  
  flatten :: Prob (Prob a) -> Prob a
  flatten (Prob xs) = Prob $ concat $ map multAll xs
    where multAll (Prob innerxs, p) = map (\(x, r) -> (x, p * r)) innerxs
  
  instance Monad Prob where
    return x = Prob [(x, 1%1)]
    m >>= f = flatten (fmap f m)
    fail _ = Prob []
  
  data Coin = Heads | Tails deriving (Show, Eq)
  
  coin :: Prob Coin
  coin = Prob [(Heads, 1%2), (Tails, 1%2)]
  
  loadedCoin :: Prob Coin
  loadedCoin = Prob [(Heads, 1%10), (Tails, 9%10)]
  
  flipThree :: Prob Bool
  flipThree = do
    a <- coin
    b <- coin
    c <- loadedCoin
    return (all (== Tails) [a, b, c])
  
  mergeOutcomes :: (Eq a, Num b) => [(a, b)] -> [(a, b)]
  mergeOutcomes outcomes = 
    let groups = groupBy (\(x1, _) (x2, _) -> x1 == x2) outcomes
        mergeFunction = foldr1 (\(x1, y1) (_, y2) -> (x1, y1 + y2))
    in map mergeFunction groups
  