------------------------------------------------------------------------------
--
--  Haskell: The Craft of Functional Programming, 3e
--  Simon Thompson
--  (c) Addison-Wesley, 1996-2011.
--
--  Chapter 3
--
------------------------------------------------------------------------------

module Chapter3 where

import Prelude hiding (max)
import Test.QuickCheck

-- The import statement which follows hides certain of the Prelude functions
-- so that they can be given the definitions they have in their book.


-- The Booleans.
-- ^^^^^^^^^^^^^

-- Exclusive or: this gives the result True if one of its arguments is True and
-- the other False, and gives the result False in other cases.

exOr :: Bool -> Bool -> Bool
exOr x y = (x || y) && not (x && y)

-- Using literals instead of variables in a definition; a simple example of
-- pattern matching to give another definition of `not', ...

myNot :: Bool -> Bool
myNot True  = False
myNot False = True

prop_myNot :: Bool -> Bool

prop_myNot x =
    not x == myNot x

-- ... and of `exclusive or'.

exOr1 True  x = not x
exOr1 False x = x

-- Test exOrs

prop_exOrs :: Bool -> Bool -> Bool

prop_exOrs x y =
    exOr x y == exOr1 x y

prop_exOr2 :: Bool -> Bool -> Bool

prop_exOr2 x y =
    exOr x y == (x /= y)

-- Integers and guards.
-- ^^^^^^^^^^^^^^^^^^^^

-- A to test whether three Ints are equal.

threeEqual :: Integer -> Integer -> Integer -> Bool
threeEqual m n p = (m==n) && (n==p)

-- The maximum of two integers; this is already defined in the Prelude, 
-- so its definition is hidden by the import statement at the top of this file.

max :: Integer -> Integer -> Integer
max x y
  | x >= y      = x
  | otherwise   = y

-- The maximum of three integers.

maxThree :: Integer -> Integer -> Integer -> Integer
maxThree x y z
  | (x >= y) && (x >= z)    = x
  | y >= z                  = y
  | otherwise               = z

-- An alternative definition of max which uses if ... then ... else ...

max' :: Integer -> Integer -> Integer
max' x y
  = if x >= y then x else y

prop_compareMax :: Integer -> Integer -> Bool
prop_compareMax x y =
    max x y == max' x y

prop_max1, prop_max2, prop_max3 :: Integer -> Integer -> Bool

prop_max1 x y =
    x <= max x y && y <= max x y

prop_max2 x y =
    x == max x y || y == max x y

prop_max3 x y =
    (x == max x y) `exOr` (y == max x y)


-- Characters.
-- ^^^^^^^^^^^

-- Converting lower-case letters to upper-case; does something odd if you apply
-- it to anythig else: how would you modify it to return anything else
-- unchanged?

toUpper :: Char -> Char
toUpper ch = toEnum (fromEnum ch + offset)

offset = fromEnum 'A' - fromEnum 'a'

-- A check whether a character is a digit.

isDigit :: Char -> Bool
isDigit ch = ('0' <= ch) && (ch <= '9')

isLowercase :: Char -> Bool
isLowercase ch = ('a' <= ch) && (ch <= 'z')

toUpperRight :: Char -> Char
toUpperRight ch
  | isLowercase ch = toUpper ch
  | otherwise = ch

--3.17
toDigit :: Char -> Int
toDigit ch
 | isDigit ch = fromEnum ch - fromEnum '0'
 | otherwise = 0

-- The String type
-- ^^^^^^^^^^^^^^^

-- Example strings

str1, str2, str3, str4, str5 :: String

str1 = "baboon"
str2 = ""
str3 = "\99a\116"
str4 = "gorilla\nhippo\nibex"
str5 = "1\t23\t456"

pstr1, pstr2, pstr3, pstr4, pstr5 :: IO ()

pstr1 = putStr str1
pstr2 = putStr str2
pstr3 = putStr str3
pstr4 = putStr str4
pstr5 = putStr str5



-- Some syntax.
-- ^^^^^^^^^^^^

-- Layout: two definitions on one line, separated by a `;'.

answer = 42 ;   facSix = 720 

-- Adding two integers: you can use longer names for variables than x and y!

addTwo :: Integer -> Integer -> Integer
addTwo first second = first+second

-- Defining an operator for yourself: another version of max!

(&&&) :: Integer -> Integer -> Integer
x &&& y 
  | x > y       = y
  | otherwise   = x
prop_fix x y = max x y == x `max` y
