module RedBlack where

-- Red-Black trees in a functional setting, by Okasaki.
-- (With invariants coded, and a fault injected.)

data Colour = R | B
  deriving Show

data Tree a = E | T Colour (Tree a) a (Tree a)
  deriving Show

-- Methods

member x E = False
member x (T _ a y b)
  | x < y = member x a
  | x > y = member x b
  | otherwise = True

makeBlack (T _ a y b) = T B a y b

insert x s = makeBlack (ins x s)

ins x E = T R E x E
ins x (T col a y b)
  | x < y = balance col (ins x a) y b
  | x > y = balance col a y (ins x b)
  | otherwise = T col a y b

-- Mistake on 4th line, 3rd line is correct
balance B (T R (T R a x b) y c) z d = T R (T B a x b) y (T B c z d)
balance B (T R a x (T R b y c)) z d = T R (T B a x b) y (T B c z d)
--balance B a x (T R (T R b y c) z d) = T R (T B a x b) y (T B c z d)
balance B a x (T R (T R c y b) z d) = T R (T B a x b) y (T B c z d)
balance B a x (T R b y (T R c z d)) = T R (T B a x b) y (T B c z d)
balance col a x b = T col a x b

-- Helpers

isRed R = True
isRed B = False

blackRoot E = True
blackRoot (T col a x b) = not (isRed col)

-- INVARIANT 1. No red node has a red parent.

red E = True
red (T col a x b) =
  (if isRed col then blackRoot a && blackRoot b else True) && red a && red b

-- INVARIANT 2. Every path from the root to an empty node contains the
-- same number of black nodes.

black t = fst (black' t)

black' E = (True, 1)
black' (T col a x b) = (b0 && b1 && n == m, n + if isRed col then 0 else 1)
  where (b0, n) = black' a
        (b1, m) = black' b

-- INVARIANT 3. Trees are ordered.

every p E = True
every p (T _ a x b) = p x && every p a && every p b

ord E = True
ord (T _ a x b) = every (<= x) a && every (>= x) b && ord a && ord b

-- Properties

infixr 0 -->
False --> _ = True
True --> x = x

redBlack t = red t && black t && ord t

prop_insertRB (x, t) = redBlack t --> redBlack (insert x t)
  where
    types = x :: Int
