1 Induction


1.1 Readings

Discrete Mathematics using a Comptuter (O’Donnell, Hall, Page) Chapter 4

The Haskell Road to Logic, Math and Programming (Doets, van Eijck) Chapter 7, 8, 10, 11

Discrete Mathematics with Applications - Metric Edition (Epp) Chapter 5

https://runestone.academy/ns/books/published/dmoi-4/sec_seq-induction.html
https://runestone.academy/ns/books/published/dmoi-4/sec_seq-strong-induction.html
https://runestone.academy/ns/books/published/dmoi-4/sec_sequences-conc.html

https://www.cs.yale.edu/homes/aspnes/classes/202/notes.pdf
(Chapter 5)

https://www.cs.carleton.edu/faculty/dln/book/ch05_mathematical-induction_2021_September_08.pdf

https://runestone.academy/ns/books/published/ads/s-induction.html

https://en.wikipedia.org/wiki/Mathematical_induction

https://en.wikipedia.org/wiki/Peano_axioms

1.2 Induction over lists

Code-DMUC/Stdm04Induction.hs

-- # Software Tools for Discrete Mathematics
module Stdm04Induction where

import Data.List
import Stdm03Recursion
import Test.QuickCheck

-- # Chapter 4.  Induction

-- Induction proves that a property P(x) holds for every element of a set.
-- ∀ x ∈ N | P(x)
-- For all x in the net N of natural numbers, the predicate property P is true.
-- Along with list comprehenions, there are four relevant haskell functions:

-- | FOR ALL ∀ with a predicate: all
-- >>> setForAllPredicate
-- True
setForAllPredicate = all (== True) [True, True]

-- | FOR ALL ∀ where the default predicate is a boolean: and
-- >>> setForAllBool
-- True
setForAllBool = and [True, True]

-- | THERE EXISTS ∃ with a predicate: any
-- >>> setThereExistsPredicate
-- True
setThereExistsPredicate = any (== True) [False, True]

-- | THERE EXISTS ∃ where the default predicate is a boolean: or
-- >>> setThereExistsBool
-- True
setThereExistsBool = or [False, True]

-- Theorem 5. ----------------------------------------------
-- Let P(x) be a predicate property that is either true or false,
-- for every element of the set N of natural numbers.
-- If P(0) is true, and P(n) → P(n + 1) holds for all n ≥ 0,
-- then ∀ x ∈ N | P(x) is true.

-- Theorem 6. ----------------------------------------------
-- ∀ x ∈ N | ∑ i for i in n = (n * (n + 1)) `div` 2

-- | 4.2 Proof of sum of natural numbers
-- >>> sumNats 10
-- 55
sumNats :: Integer -> Integer
sumNats n = (n * (n + 1)) `div` 2

-- Theorem 7. ----------------------------------------------
-- ∀ x ∈ N | ∏ i for i in n = fact n

-- | 4.3 Factorial proof
-- >>> factorial 5
-- 120
fact :: Integer -> Integer
fact 0 = 1
fact n = n * fact (n - 1)

-- Theorem 8. ----------------------------------------------
-- ∀ x ∈ N | equals x x = True
-- This is valid Haskell, but it won't ever finish.
equal = and [x == x | x <- [1 ..]]

-- | We can't prove an infinite set by example:
-- >>> equalInsufficient
-- [True,True,True,True,True,True,True,True,True,True]
equalInsufficient = take 10 [x == x | x <- [1 ..]]

-- | Proof base case: equals.1
-- >>> equal1
-- True
equal1 = equals Zero Zero

-- | Assume inductive case: equals2
-- >>> take 10 [equal2 x | x <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
equal2 x = equals x x

-- | Inductive case 1. Mirrors the implementation of equals itself
-- >>> equal3
-- True
equal3 = equals (Succ Zero) (Succ Zero) == equals Zero Zero

-- | Inductive case n.
-- >>> take 10 [equal4 x | x <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
equal4 x = equals (Succ x) (Succ x) == equals x x

-- Theorem 9. ----------------------------------------------
-- ∀ x ∈ N | sub (add x y) = y
-- This is valid Haskell, but it won't ever finish.
-- We can't prove an infinite set by example:
subZero = and [sub (add x y) x == y | x <- infinitePeanos, y <- infinitePeanos]

-- | Proof base case:
-- >>> take 10 [subZero1 y | y <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
subZero1 y = sub (add Zero y) Zero == y

-- | Inductive case 1
-- >>> take 10 [subZero2 y | y <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
subZero2 y = sub (add (Succ Zero) y) (Succ Zero) == sub (add Zero y) Zero

-- | Inductive case n
-- >>> take 10 [subZero3 x y | x <- infinitePeanos, y <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
subZero3 x y = sub (add (Succ x) y) (Succ x) == sub (add Zero y) x

-- Theorem 10. ----------------------------------------------
-- ∀ x ∈ N | add x (add x y) = add (add x y) z
-- This is valid Haskell, but it won't ever finish.
-- We can't prove an infinite set by example:
associative =
  and
    [ add x (add y z) == add (add x y) z
      | x <- infinitePeanos,
        y <- infinitePeanos,
        z <- infinitePeanos
    ]

-- | Proof base case
-- >>> take 10 [associative1 y z | y <- infinitePeanos, z <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
associative1 y z = add Zero (add y z) == add (add Zero y) z

-- | Inductive case 1
-- >>> take 10 [associative2 y z | y <- infinitePeanos, z <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
associative2 y z = add (Succ Zero) (add y z) == add (add (Succ Zero) y) z

-- | Inductive case n
-- >>> take 10 [associative3 x y z | x <- infinitePeanos, y <- infinitePeanos, z <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
associative3 x y z = add (Succ x) (add y z) == add (add (Succ x) y) z

-- Theorem 11. ----------------------------------------------
-- ∀ x ∈ N | add x Zero = x
-- This is valid Haskell, but it won't ever finish.
-- We can't prove an infinite set by example:
addZero = and [add x Zero == x | x <- infinitePeanos]

-- | Base case
-- >>> addZero1
-- True
addZero1 = add Zero Zero == Zero

-- | Inductive case 1
-- >>> addZero2
-- True
addZero2 = add (Succ Zero) Zero == Succ Zero

-- | Inductive case n.
-- >>> take 10 [addZero3 x | x <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
addZero3 x = add (Succ x) Zero == Succ x

-- Theorem 12. ----------------------------------------------
-- ∀ x ∈ N | add (Succ x) y = add x (Succ y)
-- This is valid Haskell, but it won't ever finish.
-- We can't prove an infinite set by example:
addSucc = and [add (Succ x) y == add x (Succ y) | x <- infinitePeanos, y <- infinitePeanos]

-- | Base case
-- >>> take 10 [addSucc1 y | y <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
addSucc1 y = add (Succ Zero) y == add Zero (Succ y)

-- | Inductive case 1
-- >>> take 10 [addSucc2 y | y <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
addSucc2 y = add (Succ (Succ Zero)) y == add (Succ Zero) (Succ y)

-- | Inductive case n
-- >>> take 10 [addSucc3 x y | x <- infinitePeanos, y <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
addSucc3 x y = add (Succ (Succ x)) y == add (Succ x) (Succ y)

-- Theorem 13. ----------------------------------------------
-- ∀ x ∈ N | add x y = add y x
-- This is valid Haskell, but it won't ever finish.
-- We can't prove an infinite set by example:
commutative = and [add x y == add x y | x <- infinitePeanos, y <- infinitePeanos]

-- | Base case
-- >>> take 10 [commutative1 y | y <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
commutative1 y = add Zero y == add y Zero

-- | Inductive case 1
-- >>> take 10 [commutative2 y | y <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
commutative2 y = add (Succ Zero) y == add y (Succ Zero)

-- | Inductive case n
-- >>> take 10 [commutative3 x y | x <- infinitePeanos, y <- infinitePeanos]
-- [True,True,True,True,True,True,True,True,True,True]
commutative3 x y = add (Succ x) y == add y (Succ x)

-- Theorem 14. ----------------------------------------------
-- Principle of list induction.
-- Suppose P(xs) is a predicate property on a list of some type [a].
-- Suppose that P([]) is true (base case).
-- Suppose that P(xs) holds for arbitrary xs :: [a],
-- then P(x : xs) also holds for arbitrary x :: a.
-- Then, P(xs) holds for every list xs that has infinite length.

-- | Bulit in: sum
-- >>> sumL [1,2,3,4]
-- 10
sumL :: (Num a) => [a] -> a
sumL [] = 0
sumL (x : xs) = x + sumL xs

-- Theorem 15. ----------------------------------------------
-- This is valid Haskell, but needs proving.
-- We can't prove an infinite set by example:
sumCons :: (Num a, Eq a) => [a] -> [a] -> Bool
sumCons xs ys = sum (xs ++ ys) == sum xs + sum ys

-- | Base case
-- >>> quickCheck sumCons
-- +++ OK, passed 100 tests.
sumCons1 :: [Int] -> Bool
sumCons1 ys = sum ([] ++ ys) == sum [] + sum ys

-- | Inductive case
-- >>> quickCheck sumCons2
-- +++ OK, passed 100 tests.
sumCons2 :: [Int] -> [Int] -> Bool
sumCons2 [] ys = sum ([] ++ ys) == sum [] + sum ys
sumCons2 (x : xs) ys = sum ((x : xs) ++ ys) == sum (x : xs) + sum ys

-- Theorem 16. ----------------------------------------------
-- This is valid Haskell, but needs proving.
-- We can't prove an infinite set by example:
lenCons :: (Num a, Eq a) => [a] -> [a] -> Bool
lenCons xs ys = length (xs ++ ys) == length xs + length ys

-- Theorem 17. ----------------------------------------------
-- This is valid Haskell, but needs proving.
-- We can't prove an infinite set by example:
lenMap :: (a -> b) -> [a] -> Bool
lenMap f xs = length (map f xs) == length xs

-- | Base case
-- >>> lenMap1 (+1) []
-- True

-- | Inductive case example
-- >>> lenMap1 (+ 1) [1,2]
-- True
lenMap1 :: (Int -> Int) -> [Int] -> Bool
lenMap1 f [] = length (map f []) == length []
lenMap1 f (x : xs) = length (map f (x : xs)) == length (x : xs)

-- Theorem 18. ----------------------------------------------
-- This is valid Haskell, but needs proving.
-- We can't prove an infinite set by example:

-- | Base case
-- >>> mapCons (+ 1) [] []
-- True

-- | Inductive case example
-- >>> mapCons (+ 1) [1,2] [3,4]
-- True
mapCons :: (Eq b) => (a -> b) -> [a] -> [a] -> Bool
mapCons f [] ys = map f ([] ++ ys) == map f [] ++ map f ys
mapCons f xs [] = map f (xs ++ []) == map f xs ++ map f []
mapCons f xs ys = map f (xs ++ ys) == map f xs ++ map f ys

-- Theorem 19. ----------------------------------------------

-- | Base case
-- >>> mapComposition (+ 2) (* 2) []
-- True

-- | Inductive case example
-- >>> mapComposition (+ 2) (* 2) [1,2]
-- True
mapComposition :: (Eq c) => (b -> c) -> (a -> b) -> [a] -> Bool
mapComposition f g [] = (map f . map g) [] == map (f . g) []
mapComposition f g xs = (map f . map g) xs == map (f . g) xs

-- Theorem 20. ----------------------------------------------

-- | Base case
-- >>> sumMap []
-- True

-- | Inductive case example
-- >>> sumMap [1]
-- True
sumMap :: (Num a, Eq a) => [a] -> Bool
sumMap [] = sum (map (1 +) []) == genericLength [] + sum []
sumMap xs = sum (map (1 +) xs) == genericLength xs + sum xs

-- Theorem 21. ----------------------------------------------

-- | Base case
-- >>> foldCons []
-- True

-- | Inductive case example
-- >>> foldCons [1]
-- True
foldCons :: (Eq a) => [a] -> Bool
foldCons (x : xs) = foldr (:) [] (x : xs) == (x : xs)
foldCons xs = foldr (:) [] xs == xs

-- Theorem 22. ----------------------------------------------

-- | Base case
-- >>> mapConcat (1 +) []
-- True

-- | Inductive case example
-- >>> mapConcat (1 +) [[1,2],[3,4]]
-- True
mapConcat :: (Eq b) => (a -> b) -> [[a]] -> Bool
mapConcat f (xs : xss) = map f (concat (xs : xss)) == concat (map (map f) (xs : xss))
mapConcat f xss = map f (concat xss) == concat (map (map f) xss)

-- Theorem 23. ----------------------------------------------

-- | Example
-- >>> lengthConcat [1,2,3] [3,4,5,6]
-- True
lengthConcat :: [a] -> [a] -> Bool
lengthConcat xs (y : ys) = length (xs ++ (y : ys)) == 1 + length xs + length ys

-- Equality of functions and function composition.

-- Intensional equality: f and g are equal if their definitions are identical.

-- Extensional equality: f and g are equal if they have:
-- the same type a → b and
-- f x = g x for all well-typed arguments where x :: a
-- ∀ x :: a | f x = gx

-- | Demonstrating id function
-- >>> id 5
-- 5

-- | Demonstrating id function
-- >>> id False
-- False

-- Theorem 24. ----------------------------------------------

-- | Base case
-- >>> foldrCons []
-- True

-- | Inductive case example
-- >>> foldrCons [1,2,3]
-- True
foldrCons :: (Eq a) => [a] -> Bool
foldrCons x = foldr (:) [] x == id x

-- Theorem 25. ----------------------------------------------
-- Almost the as Theorem 22.
mapConcatF :: (Eq b) => (a -> b) -> [[a]] -> Bool
mapConcatF f xxs = (map f . concat) xxs == concat (map (map f) xxs)

-- Theorem 26. ----------------------------------------------
-- Word problem in book

-- Theorem 27. ----------------------------------------------

-- | Base case
-- >>> reverseId []
-- True

-- | Inductive case example
-- >>> reverseId [1,2,3]
-- True
reverseId :: (Eq a) => [a] -> Bool
reverseId xs = (reverse . reverse) xs == id xs

Code-HRLMP/HL07IAR.hs

module HL07IAR where

import Data.List
import HL04STAL (display)

sumOdds' :: Integer -> Integer
sumOdds' n = sum [2 * k - 1 | k <- [1 .. n]]

sumOdds :: Integer -> Integer
sumOdds n = n ^ 2

sumEvens' :: Integer -> Integer
sumEvens' n = sum [2 * k | k <- [1 .. n]]

sumEvens :: Integer -> Integer
sumEvens n = n * (n + 1)

sumInts :: Integer -> Integer
sumInts n = (n * (n + 1)) `div` 2

sumSquares' :: Integer -> Integer
sumSquares' n = sum [k ^ 2 | k <- [1 .. n]]

sumSquares :: Integer -> Integer
sumSquares n = (n * (n + 1) * (2 * n + 1)) `div` 6

sumCubes' :: Integer -> Integer
sumCubes' n = sum [k ^ 3 | k <- [1 .. n]]

sumCubes :: Integer -> Integer
sumCubes n = (n * (n + 1) `div` 2) ^ 2

-- Z is Zero
-- S is Successor
data Natural = Z | S Natural
  deriving (Eq, Show)

plus m Z = m
plus m (S n) = S (plus m n)

m `mult` Z = Z
m `mult` (S n) = (m `mult` n) `plus` m

expn m Z = (S Z)
expn m (S n) = (expn m n) `mult` m

leq Z _ = True
leq (S _) Z = False
leq (S m) (S n) = leq m n

geq m n = leq n m

gt m n = not (leq m n)

lt m n = gt n m

foldn :: (a -> a) -> a -> Natural -> a
foldn h c Z = c
foldn h c (S n) = h (foldn h c n)

exclaim :: Natural -> String
exclaim = foldn ('!' :) []

bittest :: [Int] -> Bool
bittest [] = True
bittest [0] = True
bittest (1 : xs) = bittest xs
bittest (0 : 1 : xs) = bittest xs
bittest _ = False

fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

fib' n = fib2 0 1 n
  where
    fib2 a b 0 = a
    fib2 a b n = fib2 b (a + b) (n - 1)

data BinTree = L | N BinTree BinTree deriving (Show)

makeBinTree :: Integer -> BinTree
makeBinTree 0 = L
makeBinTree n = N (makeBinTree (n - 1)) (makeBinTree (n - 1))

count :: BinTree -> Integer
count L = 1
count (N t1 t2) = 1 + count t1 + count t2

depth :: BinTree -> Integer
depth L = 0
depth (N t1 t2) = (max (depth t1) (depth t2)) + 1

balanced :: BinTree -> Bool
balanced L = True
balanced (N t1 t2) =
  (balanced t1)
    && (balanced t2)
    && depth t1 == depth t2

data Tree = Lf | Nd Int Tree Tree deriving (Show)

data Tr a = Nil | T a (Tr a) (Tr a) deriving (Eq, Show)

type Dict = Tr (String, String)

split :: [a] -> ([a], a, [a])
split xs = (ys1, y, ys2)
  where
    ys1 = take n xs
    (y : ys2) = drop n xs
    n = length xs `div` 2

data LeafTree a
  = Leaf a
  | Node (LeafTree a) (LeafTree a)
  deriving (Show)

ltree :: LeafTree String
ltree =
  Node
    (Leaf "I")
    ( Node
        (Leaf "love")
        (Leaf "you")
    )

data Rose a = Bud a | Br [Rose a] deriving (Eq, Show)

rose = Br [Bud 1, Br [Bud 2, Bud 3, Br [Bud 4, Bud 5, Bud 6]]]

len [] = 0
len (x : xs) = 1 + len xs

cat :: [a] -> [a] -> [a]
cat [] ys = ys
cat (x : xs) ys = x : (cat xs ys)

add :: [Natural] -> Natural
add = foldr plus Z

mlt :: [Natural] -> Natural
mlt = foldr mult (S Z)

ln :: [a] -> Natural
ln = foldr (\_ n -> S n) Z

rev :: [a] -> [a]
rev = foldl (\xs x -> x : xs) []

rev' :: [a] -> [a]
rev' = foldr (\x xs -> xs ++ [x]) []

data Peg = A | B | C

type Tower = ([Int], [Int], [Int])

move :: Peg -> Peg -> Tower -> Tower
move A B (x : xs, ys, zs) = (xs, x : ys, zs)
move B A (xs, y : ys, zs) = (y : xs, ys, zs)
move A C (x : xs, ys, zs) = (xs, ys, x : zs)
move C A (xs, ys, z : zs) = (z : xs, ys, zs)
move B C (xs, y : ys, zs) = (xs, ys, y : zs)
move C B (xs, ys, z : zs) = (xs, z : ys, zs)

transfer :: Peg -> Peg -> Peg -> Int -> Tower -> [Tower]
transfer _ _ _ 0 tower = [tower]
transfer p q r n tower =
  transfer p r q (n - 1) tower
    ++ transfer r q p (n - 1) (move p q tower')
  where
    tower' = last (transfer p r q (n - 1) tower)

hanoi :: Int -> [Tower]
hanoi n = transfer A C B n ([1 .. n], [], [])

check :: Int -> Tower -> Bool
check 0 t = t == ([], [], [])
check n (xs, ys, zs)
  | xs /= [] && last xs == n = check (n - 1) (init xs, zs, ys)
  | zs /= [] && last zs == n = check (n - 1) (ys, xs, init zs)
  | otherwise = False

maxT :: Tower -> Int
maxT (xs, ys, zs) = foldr max 0 (xs ++ ys ++ zs)

checkT :: Tower -> Bool
checkT t = check (maxT t) t

parity :: Tower -> (Int, Int, Int)
parity (xs, ys, zs) = par (xs ++ [n + 1], ys ++ [n], zs ++ [n + 1])
  where
    n = maxT (xs, ys, zs)
    par (x : xs, y : ys, z : zs) = (mod x 2, mod y 2, mod z 2)

target :: Tower -> Peg
target t@(xs, ys, zs)
  | parity t == (0, 1, 1) = A
  | parity t == (1, 0, 1) = B
  | parity t == (1, 1, 0) = C

move1 :: Tower -> Tower
move1 t@(1 : _, ys, zs) = move A (target t) t
move1 t@(xs, 1 : _, zs) = move B (target t) t
move1 t@(xs, ys, 1 : _) = move C (target t) t

move2 :: Tower -> Tower
move2 t@(1 : xs, [], zs) = move C B t
move2 t@(1 : xs, ys, []) = move B C t
move2 t@(1 : xs, ys, zs) = if ys < zs then move B C t else move C B t
move2 t@([], 1 : ys, zs) = move C A t
move2 t@(xs, 1 : ys, []) = move A C t
move2 t@(xs, 1 : ys, zs) = if xs < zs then move A C t else move C A t
move2 t@([], ys, 1 : zs) = move B A t
move2 t@(xs, [], 1 : zs) = move A B t
move2 t@(xs, ys, 1 : zs) = if xs < ys then move A B t else move B A t

done :: Tower -> Bool
done ([], [], _) = True
done (xs, ys, zs) = False

transfer1, transfer2 :: Tower -> [Tower]
transfer1 t = t : transfer2 (move1 t)
transfer2 t = if done t then [t] else t : transfer1 (move2 t)

hanoi' :: Int -> [Tower]
hanoi' n = transfer1 ([1 .. n], [], [])

zazen :: [Tower]
zazen = hanoi' 64

hanoiCount :: Int -> Integer -> Tower
hanoiCount n k
  | k < 0 = error "argument negative"
  | k > 2 ^ n - 1 = error "argument not in range"
  | k == 0 = ([1 .. n], [], [])
  | k == 2 ^ n - 1 = ([], [], [1 .. n])
  | k < 2 ^ (n - 1) = (xs ++ [n], zs, ys)
  | k >= 2 ^ (n - 1) = (ys', xs', zs' ++ [n])
  where
    (xs, ys, zs) = hanoiCount (n - 1) k
    (xs', ys', zs') = hanoiCount (n - 1) (k - 2 ^ (n - 1))

toTower :: Integer -> Tower
toTower n = hanoiCount k m
  where
    n' = fromInteger (n + 1)
    k = truncate (logBase 2 n')
    m = truncate (n' - 2 ^ k)

data Form = P Int | Conj Form Form | Disj Form Form | Neg Form

instance Show Form where
  show (P i) = 'P' : show i
  show (Conj f1 f2) = "(" ++ show f1 ++ " & " ++ show f2 ++ ")"
  show (Disj f1 f2) = "(" ++ show f1 ++ " v " ++ show f2 ++ ")"
  show (Neg f) = "~" ++ show f

subforms :: Form -> [Form]
subforms (P n) = [(P n)]
subforms (Conj f1 f2) = (Conj f1 f2) : (subforms f1 ++ subforms f2)
subforms (Disj f1 f2) = (Disj f1 f2) : (subforms f1 ++ subforms f2)
subforms (Neg f) = (Neg f) : (subforms f)

ccount :: Form -> Int
ccount (P n) = 0
ccount (Conj f1 f2) = 1 + (ccount f1) + (ccount f2)
ccount (Disj f1 f2) = 1 + (ccount f1) + (ccount f2)
ccount (Neg f) = 1 + (ccount f)

acount :: Form -> Int
acount (P n) = 1
acount (Conj f1 f2) = (acount f1) + (acount f2)
acount (Disj f1 f2) = (acount f1) + (acount f2)
acount (Neg f) = acount f

Code-HRLMP/Sol07.hs

module Sol7 where

import Data.List
import HL07IAR

-- | 7.14
-- >>>
subtr :: Natural -> Natural -> Natural
subtr Z _ = Z
subtr m Z = m
subtr (S m) (S n) = subtr m n

-- | 7.15
-- >>>
qrm :: Natural -> Natural -> (Natural, Natural)
qrm m n
  | gt n m = (Z, m)
  | otherwise = (S (fst qr), snd qr)
  where
    qr = qrm (subtr m n) n

quotient :: Natural -> Natural -> Natural
quotient m n = fst (qrm m n)

remainder :: Natural -> Natural -> Natural
remainder m n = snd (qrm m n)

-- | 7.16
-- >>>
pre :: Natural -> Natural
pre Z = Z
pre (S n) = n

subtr2 :: Natural -> Natural -> Natural
subtr2 = foldn pre

-- Did Haskell uset to allow this math on the left of = ?

-- | 7.20
-- >>>
--
-- catalan :: Integer -> Integer
-- catalan 0 = 1
-- catalan (n + 1) = sum [(catalan i) * (catalan (n - i)) | i <- [0 .. n]]

-- | 7.25
-- >>>
--
-- data TernTree = L' | N' TernTree TernTree TernTree deriving (Show)
--
-- makeTernTree :: Integer -> TernTree
-- makeTernTree 0 = L'
-- makeTernTree (n + 1) = N' (makeTernTree n) (makeTernTree n) (makeTernTree n)
--
-- count3 :: TernTree -> Integer
-- count3 L' = 1
-- count3 (N' t1 t2 t3) = 1 + count3 t1 + count3 t2 + count3 t3

-- | 7.28
-- >>>
insertTree :: Int -> Tree -> Tree
insertTree n Lf = (Nd n Lf Lf)
insertTree n t@(Nd m left right)
  | m < n = Nd m left (insertTree n right)
  | m > n = Nd m (insertTree n left) right
  | otherwise = t

-- | 7.29
-- >>>
list2tree :: [Int] -> Tree
list2tree [] = Lf
list2tree (n : ns) = insertTree n (list2tree ns)

tree2list :: Tree -> [Int]
tree2list Lf = []
tree2list (Nd n left right) = tree2list left ++ [n] ++ tree2list right

-- | 7.30
-- >>>
inTree :: Int -> Tree -> Bool
inTree n Lf = False
inTree n (Nd m left right)
  | n == m = True
  | n < m = inTree n left
  | n > m = inTree n right

-- | 7.31
-- >>>
mergeTrees :: Tree -> Tree -> Tree
mergeTrees t1 t2 = foldr insertTree t2 (tree2list t1)

-- | 7.32
findDepth :: Int -> Tree -> Int
findDepth _ Lf = -1
findDepth n (Nd m left right)
  | n == m = 0
  | n < m = if d1 == -1 then -1 else d1 + 1
  | n > m = if d2 == -1 then -1 else d2 + 1
  where
    d1 = findDepth n left
    d2 = findDepth n right

-- | 7.33
mapT :: (a -> b) -> Tr a -> Tr b
mapT f Nil = Nil
mapT f (T x left right) = T (f x) (mapT f left) (mapT f right)

-- | 7.34
foldT :: (a -> b -> b -> b) -> b -> (Tr a) -> b
foldT h c Nil = c
foldT h c (T x left right) = h x (foldT h c left) (foldT h c right)

-- | 7.35
preorderT, inorderT, postorderT :: Tr a -> [a]
preorderT = foldT preLists []
  where
    preLists x ys zs = (x : ys) ++ zs
inorderT = foldT inLists []
  where
    inLists x ys zs = ys ++ [x] ++ zs
postorderT = foldT postLists []
  where
    postLists x ys zs = ys ++ zs ++ [x]

-- | 7.36
orderedT :: (Ord a) => Tr a -> Bool
orderedT tree = ordered (inorderT tree)
  where
    ordered xs = (sort (nub xs) == xs)

-- | 7.37
lookupD :: String -> Dict -> [String]
lookupD _ Nil = []
lookupD x (T (v, w) left right)
  | x == v = [w]
  | x < v = lookupD x left
  | otherwise = lookupD x right

-- | 7.38
buildTree :: [a] -> Tr a
buildTree [] = Nil
buildTree xs = T m (buildTree left) (buildTree right)
  where
    (left, m, right) = split xs

-- | 7.39
mapLT :: (a -> b) -> LeafTree a -> LeafTree b
mapLT f (Leaf x) = Leaf (f x)
mapLT f (Node left right) = Node (mapLT f left) (mapLT f right)

-- | 7.40
reflect :: LeafTree a -> LeafTree a
reflect (Leaf x) = Leaf x
reflect (Node left right) = Node (reflect right) (reflect left)

-- | 7.42
mapR :: (a -> b) -> Rose a -> Rose b
mapR f (Bud x) = Bud (f x)
mapR f (Br roses) = Br (map (mapR f) roses)

-- | 7.46
genUnion :: (Eq a) => [[a]] -> [a]
genUnion = foldr union []

genIntersect :: (Eq a) => [[a]] -> [a]
genIntersect = foldr1 intersect

-- | 7.47
insrt :: (Ord a) => a -> [a] -> [a]
insrt x [] = [x]
insrt x (y : ys) = if x <= y then (x : y : ys) else y : (insrt x ys)

srt :: (Ord a) => [a] -> [a]
srt = foldr insrt []

-- | 7.51
ln' :: [a] -> Natural
ln' = foldl (\n _ -> S n) Z

-- Skipped those about Tower of Hanoi

Code-HRLMP/Nats.hs

module Nats where

import Data.Ratio

data Natural = Z | S Natural deriving (Eq, Show)

instance Ord Natural where
  compare Z Z = EQ
  compare Z _ = LT
  compare _ Z = GT
  compare (S m) (S n) = compare m n

instance Enum Natural where
  succ = S
  pred Z = Z
  pred (S n) = n
  toEnum = fromIntegral
  fromEnum = foldn succ 0
  enumFrom n = map toEnum [(fromEnum n) ..]

instance Num Natural where
  (+) = foldn succ
  (*) = \m -> foldn (+ m) Z
  (-) = foldn pred
  abs = id
  signum Z = Z
  signum n = (S Z)
  fromInteger n
    | n < 0 = error "no negative naturals"
    | n == 0 = Z
    | otherwise = S (fromInteger (n - 1))

foldn :: (a -> a) -> a -> Natural -> a
foldn h c Z = c
foldn h c (S n) = h (foldn h c n)

instance Real Natural where toRational x = toInteger x % 1

instance Integral Natural where
  quotRem n d
    | d > n = (Z, n)
    | otherwise = (S q, r)
    where
      (q, r) = quotRem (n - d) d
  toInteger = foldn succ 0

Code-HRLMP/HL08WWN.hs

module HL08WWN where

import Data.Char
import Data.Complex
import Data.List
import Data.Ratio
import Nats

binary :: (Integral a) => a -> [Int]
binary x = reverse (bits x)
  where
    bits 0 = [0]
    bits 1 = [1]
    bits n = fromIntegral (rem n 2) : bits (quot n 2)

showDigits :: [Int] -> String
showDigits = map intToDigit

bin :: (Integral a) => a -> String
bin = showDigits . binary

coprime :: Integer -> Integer -> Bool
coprime m n = (gcd m n) == 1

gcde :: (Integral a) => a -> a -> (a, a, a)
gcde a b
  | a == 0 && b == 0 = error "gcd undefined"
  | a < 0 || b < 0 = error "gcd undefined"
  | a == 0 = (b, 0, 1)
  | b == 0 = (a, 1, 0)
  | a > b =
      let (k, r) = quotRem a b
          (d, x, y) = gcde b r
       in (d, y, x - k * y)
  | otherwise =
      let (k, r) = quotRem b a
          (d, x, y) = gcde a r
       in (d, x - k * y, y)

data Sgn = P | N deriving (Eq, Show)

type MyInt = (Sgn, Natural)

myplus :: MyInt -> MyInt -> MyInt
myplus (s1, m) (s2, n)
  | s1 == s2 = (s1, m + n)
  | s1 == P && n <= m = (P, m - n)
  | s1 == P && n > m = (N, n - m)
  | otherwise = myplus (s2, n) (s1, m)

type NatPair = (Natural, Natural)

plus1 :: NatPair -> NatPair -> NatPair
plus1 (m1, m2) (n1, n2) = (m1 + n1, m2 + n2)

subtr1 :: NatPair -> NatPair -> NatPair
subtr1 (m1, m2) (n1, n2) = plus1 (m1, m2) (n2, n1)

mult1 :: NatPair -> NatPair -> NatPair
mult1 (m1, m2) (n1, n2) = (m1 * n1 + m2 * n2, m1 * n2 + m2 * n1)

eq1 :: NatPair -> NatPair -> Bool
eq1 (m1, m2) (n1, n2) = (m1 + n2) == (m2 + n1)

reduce1 :: NatPair -> NatPair
reduce1 (m1, Z) = (m1, Z)
reduce1 (Z, m2) = (Z, m2)
reduce1 (S m1, S m2) = reduce1 (m1, m2)

decExpand :: Rational -> [Integer]
decExpand x
  | x < 0 = error "negative argument"
  | r == 0 = [q]
  | otherwise = q : decExpand ((r * 10) % d)
  where
    (q, r) = quotRem n d
    n = numerator x
    d = denominator x

decForm :: Rational -> (Integer, [Int], [Int])
decForm x
  | x < 0 = error "negative argument"
  | otherwise = (q, ys, zs)
  where
    (q, r) = quotRem n d
    n = numerator x
    d = denominator x
    (ys, zs) = decF (r * 10) d []

decF :: Integer -> Integer -> [(Int, Integer)] -> ([Int], [Int])
decF n d xs
  | r == 0 = (reverse (q : (map fst xs)), [])
  | elem (q, r) xs = (ys, zs)
  | otherwise = decF (r * 10) d ((q, r) : xs)
  where
    (q', r) = quotRem n d
    q = fromIntegral q'
    xs' = reverse xs
    Just k = elemIndex (q, r) xs'
    (ys, zs) = splitAt k (map fst xs')

mechanicsRule :: Rational -> Rational -> Rational
mechanicsRule p x = (1 % 2) * (x + (p * (recip x)))

mechanics :: Rational -> Rational -> [Rational]
mechanics p x = iterate (mechanicsRule p) x

sqrtM :: Rational -> [Rational]
sqrtM p
  | p < 0 = error "negative argument"
  | otherwise = mechanics p s
  where
    s = if xs == [] then 1 else last xs
    xs = takeWhile (\m -> m ^ 2 <= p) [1 ..]

approximate :: Rational -> [Rational] -> Rational
approximate eps (x : y : zs)
  | abs (y - x) < eps = y
  | otherwise = approximate eps (y : zs)

apprx :: [Rational] -> Rational
apprx = approximate (1 / 10 ^ 6)

mySqrt :: Rational -> Rational
mySqrt p = apprx (sqrtM p)

solveQ ::
  (Complex Float, Complex Float, Complex Float) ->
  (Complex Float, Complex Float)
solveQ = \(a, b, c) ->
  if a == 0
    then error "not quadratic"
    else
      let d = b ^ 2 - 4 * a * c
       in ( (-b + sqrt d) / 2 * a,
            (-b - sqrt d) / 2 * a
          )

Code-HRLMP/Sol08.hs

-- TODO: add all the solutions to corresponding files

Code-HRLMP/HL10COR.hs

module HL10COR where

import Polynomials
import PowerSeries
import System.Random (mkStdGen, randomRs)

default (Integer, Rational, Double)

ones = 1 : ones

nats = 0 : map (+ 1) nats

odds = 1 : map (+ 2) odds

theOnes = iterate id 1

theNats = iterate (+ 1) 0

theOdds = iterate (+ 2) 1

theNats1 = 0 : zipWith (+) ones theNats1

theFibs = 0 : 1 : zipWith (+) theFibs (tail theFibs)

pr (x1 : x2 : x3 : xs) = x1 * x3 - x2 * x2 : pr (x2 : x3 : xs)

sieve :: [Integer] -> [Integer]
sieve (0 : xs) = sieve xs
sieve (n : xs) = n : sieve (mark xs 1 n)
  where
    mark (y : ys) k m
      | k == m = 0 : (mark ys 1 m)
      | otherwise = y : (mark ys (k + 1) m)

sieve' :: [Integer] -> [Integer]
sieve' (n : xs) = n : sieve' (filter (\m -> (rem m n) /= 0) xs)

primes' :: [Integer]
primes' = sieve' [2 ..]

randomInts :: Int -> Int -> [Int]
randomInts bound seed =
  tail (randomRs (0, bound) (mkStdGen seed))

type Process = [Int] -> [String]

start :: Process -> Int -> Int -> [String]
start process bound seed = process (randomInts bound seed)

clock :: Process
clock (0 : xs) = "tick" : clock xs
clock (1 : xs) = "crack" : []

vending, vending1, vending2, vending3 :: Process
vending (0 : xs) = "coin" : vending1 xs
vending (1 : xs) = vending xs
vending1 (0 : xs) = "coin" : vending2 xs
vending1 (1 : xs) = "water" : vending xs
vending2 (0 : xs) = "coin" : vending3 xs
vending2 (1 : xs) = "beer" : vending xs
vending3 (0 : xs) = "moneyback" : vending xs
vending3 (1 : xs) = vending3 xs

ptd :: Process
ptd = ptd0 0

ptd0 :: Int -> Process
ptd0 0 (0 : xs) = ptd0 0 xs
ptd0 i (0 : xs) = ("return " ++ show i ++ " euro") : ptd0 0 xs
ptd0 i (1 : xs) = "1 euro" : ptd0 (i + 1) xs
ptd0 i (2 : xs) = "2 euro" : ptd0 (i + 2) xs
ptd0 0 (3 : xs) = ptd0 0 xs
ptd0 i (3 : xs) = ("ticket " ++ show (i * 20) ++ " min") : ptd0 0 xs

actions = user [0, 0, 1] responses

responses = vending actions

user acts ~(r : s : p : resps) = acts ++ user (proc [r, s, p]) resps

proc ["coin", "coin", "beer"] = [0, 0, 1]

approx :: Integer -> [a] -> [a]
approx n []
  | n <= 0 = undefined
  | otherwise = []
approx n (x : xs) = x : approx (n - 1) xs

o2e :: (Num a) => [a] -> [a]
o2e [] = []
o2e (f : fs) = f : o2e (deriv (f : fs))

e2o :: (Ord a, Fractional a) => [a] -> [a]
e2o [] = []
e2o (f : fs) = [f] + (int (e2o (fs)))

Code-HRLMP/Sol10.hs

module Sol10 where

import HL10COR

-- | 10.1 Write a corecursive definition that generates the even natural numbers.
-- >>> take 5 evens
-- [0,2,4,6,8]
evens = 0 : map (+ 2) evens

-- | 10.2 Use iterate to define the infinite stream of even natural numbers.
-- >>> take 5 theEvens
-- [0,2,4,6,8]
theEvens = iterate (+ 2) 0

-- | 10.3 Thue-Morse sequence
-- >>> take 100 thue
-- "0110100110010110100101100110100110010110011010010110100110010110100101100110100101101001100101100110"
swap "" = ""
swap ('1' : xs) = '0' : swap xs
swap ('0' : xs) = '1' : swap xs

morse xs = xs ++ morse (xs ++ swap xs)

thue = '0' : morse "1"

-- | 10.5 How would you implement a generator for streams of 0’s and 1’s with, in the long run, a proportion of 0’s and 1’s of 2 to 1?
-- >>>
random001s :: Int -> [Int]
random001s i = map (`mod` 2) (randomInts 2 i)

-- | 10.10 Vending machine from text
-- >>>
vend, vend1, vend2, vend3, vend4 :: Process
vend (0 : xs) = "coin" : vend1 xs
vend (1 : xs) = "coin" : vend4 xs
vend1 (0 : xs) = "coin" : vend2 xs
vend1 (1 : xs) = vend1 xs
vend2 (0 : xs) = "beer" : vend xs
vend2 (1 : xs) = "coin" : vend3 xs
vend3 (0 : xs) = "moneyback" : vend xs
vend3 (1 : xs) = vend3 xs
vend4 (0 : xs) = "water" : vend xs
vend4 (1 : xs) = vend4 xs

Code-HRLMP/HL11FAIS.hs

module HL11FAIS where

natpairs = [(x, z - x) | z <- [0 ..], x <- [0 .. z]]

rationals = [(n, m) | (n, m) <- natpairs, m /= 0, gcd n m == 1]

diagonal :: (Integer -> [Bool]) -> Integer -> Bool
diagonal f n = not ((f n) !! (fromInteger n))

f :: Integer -> [Bool]
f 0 = cycle [False]
f (n + 1) = True : f n

Code-HRLMP/Sol11.hs

module Sol11 where

import HL11FAIS

-- | 11.17
ball :: Int -> [[Int]]
ball n = ballgame [n]

ballgame :: [Int] -> [[Int]]
ballgame xs
  | all (== 1) xs = [xs]
  | otherwise = xs : ballgame (reduce xs)
  where
    reduce (1 : ys) = 1 : reduce ys
    reduce (n : ys) = (n - 1) : (n - 1) : ys

-- | 11.79 The function pair is nothing but the function j defined in Theorem 11.52. Here it is (to check that it is the inverse of natpairs, you can use map pair natpairs):
pair (n, m) = (n + m) * (n + m + 1) `div` 2 + n

-- | 11.79 First, we need code for enumerating N2 , N3 , . . . , as lists:
natpairs2 = [(x, toInt z - x) | z <- [0 ..], x <- [0 .. z]]

natlist 0 = [[n] | n <- [0 ..]]
natlist k = [n : (natlist (k - 1) !! m) | (n, m) <- natpairs2]

natstar = [] : [natlist n !! m | (n, m) <- natpairs2]