-----------------------------------------------------------------------------
-- |
-- Module      :  Data.Tree.AVL.Read
-- Copyright   :  (c) Adrian Hey 2004,2005
-- License     :  BSD3
--
-- Maintainer  :  http://homepages.nildram.co.uk/~ahey/em.png
-- Stability   :  stable
-- Portability :  portable
-----------------------------------------------------------------------------
module Data.Tree.AVL.Read
(-- * Reading from AVL trees

 -- ** Reading from extreme left or right
 assertReadL,tryReadL,
 assertReadR,tryReadR,

 -- ** Reading from /sorted/ AVL trees
 genAssertRead,genTryRead,genTryReadMaybe,genDefaultRead,

 -- ** Simple searches of /sorted/ AVL trees
 genContains,
) where

import Prelude -- so haddock finds the symbols there

import Data.COrdering
import Data.Tree.AVL.Types(AVL(..))

-- | Read the leftmost element from a /non-empty/ tree. Raises an error if the tree is empty.
-- If the tree is sorted this will return the least element.
--
-- Complexity: O(log n)
assertReadL :: AVL e -> e
assertReadL  E        = error "assertReadL: Empty tree."
assertReadL (N l e _) = readLE  l e
assertReadL (Z l e _) = readLE  l e
assertReadL (P l _ _) = readLNE l     -- BF=+1, so left sub-tree cannot be empty.

-- | Similar to 'assertReadL' but returns 'Nothing' if the tree is empty.
--
-- Complexity: O(log n)
tryReadL :: AVL e -> Maybe e
tryReadL  E        = Nothing
tryReadL (N l e _) = Just $! readLE  l e
tryReadL (Z l e _) = Just $! readLE  l e
tryReadL (P l _ _) = Just $! readLNE l     -- BF=+1, so left sub-tree cannot be empty.

-- Local utilities for the above
readLNE :: AVL e -> e
readLNE  E        = error "readLNE: Bug."
readLNE (N l e _) = readLE  l e
readLNE (Z l e _) = readLE  l e
readLNE (P l _ _) = readLNE l     -- BF=+1, so left sub-tree cannot be empty.
readLE :: AVL e -> e -> e
readLE  E        e = e
readLE (N l e _) _ = readLE  l e
readLE (Z l e _) _ = readLE  l e
readLE (P l _ _) _ = readLNE l  -- BF=+1, so left sub-tree cannot be empty.


-- | Read the rightmost element from a /non-empty/ tree. Raises an error if the tree is empty.
-- If the tree is sorted this will return the greatest element.
--
-- Complexity: O(log n)
assertReadR :: AVL e -> e
assertReadR  E        = error "assertReadR: Empty tree."
assertReadR (P _ e r) = readRE  r e
assertReadR (Z _ e r) = readRE  r e
assertReadR (N _ _ r) = readRNE r     -- BF=-1, so right sub-tree cannot be empty.

-- | Similar to 'assertReadR' but returns 'Nothing' if the tree is empty.
--
-- Complexity: O(log n)
tryReadR :: AVL e -> Maybe e
tryReadR  E        = Nothing
tryReadR (P _ e r) = Just $! readRE  r e
tryReadR (Z _ e r) = Just $! readRE  r e
tryReadR (N _ _ r) = Just $! readRNE r   -- BF=-1, so right sub-tree cannot be empty.

-- Local utilities for the above
readRNE :: AVL e -> e
readRNE  E        = error "readRNE: Bug."
readRNE (P _ e r) = readRE  r e
readRNE (Z _ e r) = readRE  r e
readRNE (N _ _ r) = readRNE r     -- BF=-1, so right sub-tree cannot be empty.
readRE :: AVL e -> e -> e
readRE  E        e = e
readRE (P _ e r) _ = readRE  r e
readRE (Z _ e r) _ = readRE  r e
readRE (N _ _ r) _ = readRNE r  -- BF=-1, so right sub-tree cannot be empty.


-- | General purpose function to perform a search of a sorted tree, using the supplied selector.
-- This function raises a error if the search fails.
--
-- Complexity: O(log n)
genAssertRead :: AVL e -> (e -> COrdering a) -> a
genAssertRead t c = genRead' t where
 genRead'  E        = error "genAssertRead failed."
 genRead' (N l e r) = genRead'' l e r
 genRead' (Z l e r) = genRead'' l e r
 genRead' (P l e r) = genRead'' l e r
 genRead''   l e r  = case c e of
                      Lt   -> genRead' l
                      Eq a -> a
                      Gt   -> genRead' r

-- | General purpose function to perform a search of a sorted tree, using the supplied selector.
-- This function is similar to 'genAssertRead', but returns 'Nothing' if the search failed.
--
-- Complexity: O(log n)
genTryRead :: AVL e -> (e -> COrdering a) ->  Maybe a
genTryRead t c = genTryRead' t where
 genTryRead'  E        = Nothing
 genTryRead' (N l e r) = genTryRead'' l e r
 genTryRead' (Z l e r) = genTryRead'' l e r
 genTryRead' (P l e r) = genTryRead'' l e r
 genTryRead''   l e r  = case c e of
                         Lt   -> genTryRead' l
                         Eq a -> Just a
                         Gt   -> genTryRead' r

-- | This version returns the result of the selector (without adding a 'Just' wrapper) if the search
-- succeeds, or 'Nothing' if it fails.
--
-- Complexity: O(log n)
genTryReadMaybe :: AVL e -> (e -> COrdering (Maybe a)) ->  Maybe a
genTryReadMaybe t c = genTryRead' t where
 genTryRead'  E        = Nothing
 genTryRead' (N l e r) = genTryRead'' l e r
 genTryRead' (Z l e r) = genTryRead'' l e r
 genTryRead' (P l e r) = genTryRead'' l e r
 genTryRead''   l e r  = case c e of
                         Lt     -> genTryRead' l
                         Eq mba -> mba
                         Gt     -> genTryRead' r

-- | General purpose function to perform a search of a sorted tree, using the supplied selector.
-- This function is similar to 'genAssertRead', but returns a the default value (first argument) if
-- the search fails.
--
-- Complexity: O(log n)
genDefaultRead :: a -> AVL e -> (e -> COrdering a) -> a
genDefaultRead d t c = genRead' t where
 genRead'  E        = d
 genRead' (N l e r) = genRead'' l e r
 genRead' (Z l e r) = genRead'' l e r
 genRead' (P l e r) = genRead'' l e r
 genRead''   l e r  = case c e of
                      Lt   -> genRead' l
                      Eq a -> a
                      Gt   -> genRead' r

-- | General purpose function to perform a search of a sorted tree, using the supplied selector.
-- Returns True if matching element is found.
--
-- Complexity: O(log n)
genContains :: AVL e -> (e -> Ordering) -> Bool
genContains t c = genContains' t where
 genContains'  E        = False
 genContains' (N l e r) = genContains'' l e r
 genContains' (Z l e r) = genContains'' l e r
 genContains' (P l e r) = genContains'' l e r
 genContains''   l e r  = case c e of
                          LT -> genContains' l
                          EQ -> True
                          GT -> genContains' r