-- Examples of using list comprehension

import Data.Char



--------------------------------------------------------------------------------
-- Operating on *all* elements in a list

-- sumSquares n gives the sum of the squares of the numbers from 1 to n
sumSquares :: Int -> Int
sumSquares n = sum [x*x | x <- [1..n]]

-- Change each character to upper case
allCAPS :: String -> String
allCAPS s = [toUpper c | c <- s]

-- takeWords n s returns the first n words of s
takeWords :: Int -> String -> String
takeWords n s = unwords (take n (words s))

-- capitalize s capitalizes all words in s
capitalize :: String -> String
capitalize s = unwords [cap w | w <- words s]
  where
    cap (c:str) = toUpper c : str



--------------------------------------------------------------------------------
-- Operating on *some* elements in a list (filtering)

-- keepPositives as delete all negative numbers from as
keepPositives :: [Int] -> [Int]
keepPositives as = [a | a <- as, a >= 0]

-- divisors n lists the (positive) divisors of n
divisors :: Int -> [Int]
divisors n = [m | m <- [1..n], mod n m == 0]

-- isPrime n checks is n is a (positive) prime number
isPrime :: Int -> Bool
isPrime n = divisors n == [1,n]

-- censor s deletes all lines in s which contain the word "money"
censor :: String -> String
censor s = unlines [ l | l <- lines s, "money" `notElem` words l]



--------------------------------------------------------------------------------
-- Multiple list generators

-- dices n returns the ways that a pair of dices can result in the sum n
dices :: Int -> [(Int,Int)]
dices n = [ (a,b) | a <- [1..6], b <- [1..6], a+b==n ]

-- check_max n checks that the max function is correct for all arguments from 0
-- to n
check_max :: Int -> Bool
check_max n =
    and
      [ if a>b then max a b == a else max a b == b
          | a <- [0..n], b <- [0..n]
      ]

-- closestPoints ps gives the distance between the closest (non-equal) points in
-- the list ps
closestPoints :: [(Double,Double)] -> Double
closestPoints ps = minimum
    [ sqrt ((x1-x2)^2 + (y1-y2)^2)
      | p1@(x1,y1) <- ps
      , p2@(x2,y2) <- ps
      , p1 /= p2
    ]
  -- @ is used to give a name to the expression matched by a pattern