-- To make GHC stop warning about the Prelude
{-# OPTIONS_GHC -Wall -fwarn-tabs -fno-warn-unused-imports #-}
{-# LANGUAGE NoImplicitPrelude #-}
----------------------------------------------------------------
--                                                  ~ 2014.10.09
-- |
-- Module      :  Data.Trie
-- Copyright   :  Copyright (c) 2008--2015 wren gayle romano
-- License     :  BSD3
-- Maintainer  :  wren@community.haskell.org
-- Stability   :  experimental
-- Portability :  portable
--
-- An efficient implementation of finite maps from strings to values.
-- The implementation is based on /big-endian patricia trees/, like
-- "Data.IntMap". We first trie on the elements of "Data.ByteString"
-- and then trie on the big-endian bit representation of those
-- elements. For further details on the latter, see
--
--    * Chris Okasaki and Andy Gill,  \"/Fast Mergeable Integer Maps/\",
--    Workshop on ML, September 1998, pages 77-86,
--    <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.37.5452>
--
--    * D.R. Morrison, \"/PATRICIA -- Practical Algorithm To Retrieve/
--    /Information Coded In Alphanumeric/\", Journal of the ACM, 15(4),
--    October 1968, pages 514-534.
--
-- This module aims to provide an austere interface, while being
-- detailed enough for most users. For an extended interface with
-- many additional functions, see "Data.Trie.Convenience". For
-- functions that give more detailed (potentially abstraction-breaking)
-- access to the data strucuture, or for experimental functions
-- which aren't quite ready for the public API, see "Data.Trie.Internal".
----------------------------------------------------------------

module Data.Trie
    (
    -- * Data type
      Trie()
    
    -- * Basic functions
    , empty, null, singleton, size
    
    -- * Conversion functions
    , fromList, toListBy, toList, keys, elems
    
    -- * Query functions
    , lookupBy, lookup, member, submap, match, matches
    
    -- * Single-value modification
    , alterBy, insert, adjust, delete
    
    -- * Combining tries
    , mergeBy, unionL, unionR
    
    -- * Mapping functions
    , mapBy, filterMap
    ) where

import Prelude hiding     (null, lookup)
import qualified Prelude  (null, lookup)

import Data.Trie.Internal
import Data.Trie.Errors   (impossible)
import Data.ByteString    (ByteString)
import qualified Data.ByteString as S
import Data.Maybe         (isJust)
import Control.Monad      (liftM)
----------------------------------------------------------------
----------------------------------------------------------------


{---------------------------------------------------------------
-- Conversion functions 
---------------------------------------------------------------}

-- | Convert association list into a trie. On key conflict, values
-- earlier in the list shadow later ones.
fromList :: [(ByteString,a)] -> Trie a
{-# INLINE fromList #-}
fromList :: [(ByteString, a)] -> Trie a
fromList = ((ByteString, a) -> Trie a -> Trie a)
-> Trie a -> [(ByteString, a)] -> Trie a
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ((ByteString -> a -> Trie a -> Trie a)
-> (ByteString, a) -> Trie a -> Trie a
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry ByteString -> a -> Trie a -> Trie a
forall a. ByteString -> a -> Trie a -> Trie a
insert) Trie a
forall a. Trie a
empty

-- | Convert trie into association list. Keys will be in sorted order.
toList :: Trie a -> [(ByteString,a)]
{-# INLINE toList #-}
toList :: Trie a -> [(ByteString, a)]
toList  = (ByteString -> a -> (ByteString, a)) -> Trie a -> [(ByteString, a)]
forall a b. (ByteString -> a -> b) -> Trie a -> [b]
toListBy (,)

-- FIX? should 'keys' and 'elems' move to Data.Trie.Convenience instead?

-- | Return all keys in the trie, in sorted order.
keys :: Trie a -> [ByteString]
{-# INLINE keys #-}
keys :: Trie a -> [ByteString]
keys  = (ByteString -> a -> ByteString) -> Trie a -> [ByteString]
forall a b. (ByteString -> a -> b) -> Trie a -> [b]
toListBy ByteString -> a -> ByteString
forall a b. a -> b -> a
const

-- | Return all values in the trie, in sorted order according to the keys.
elems :: Trie a -> [a]
{-# INLINE elems #-}
elems :: Trie a -> [a]
elems  = (ByteString -> a -> a) -> Trie a -> [a]
forall a b. (ByteString -> a -> b) -> Trie a -> [b]
toListBy ((a -> ByteString -> a) -> ByteString -> a -> a
forall a b c. (a -> b -> c) -> b -> a -> c
flip a -> ByteString -> a
forall a b. a -> b -> a
const)


{---------------------------------------------------------------
-- Query functions (just recurse)
---------------------------------------------------------------}

-- | Generic function to find a value (if it exists) and the subtrie
-- rooted at the prefix.
lookupBy :: (Maybe a -> Trie a -> b) -> ByteString -> Trie a -> b
{-# INLINE lookupBy #-}
lookupBy :: (Maybe a -> Trie a -> b) -> ByteString -> Trie a -> b
lookupBy Maybe a -> Trie a -> b
f = (Maybe a -> Trie a -> b)
-> b -> (Trie a -> b) -> ByteString -> Trie a -> b
forall a b.
(Maybe a -> Trie a -> b)
-> b -> (Trie a -> b) -> ByteString -> Trie a -> b
lookupBy_ Maybe a -> Trie a -> b
f (Maybe a -> Trie a -> b
f Maybe a
forall a. Maybe a
Nothing Trie a
forall a. Trie a
empty) (Maybe a -> Trie a -> b
f Maybe a
forall a. Maybe a
Nothing)

-- | Return the value associated with a query string if it exists.
lookup :: ByteString -> Trie a -> Maybe a
{-# INLINE lookup #-}
lookup :: ByteString -> Trie a -> Maybe a
lookup = (Maybe a -> Trie a -> Maybe a)
-> Maybe a
-> (Trie a -> Maybe a)
-> ByteString
-> Trie a
-> Maybe a
forall a b.
(Maybe a -> Trie a -> b)
-> b -> (Trie a -> b) -> ByteString -> Trie a -> b
lookupBy_ Maybe a -> Trie a -> Maybe a
forall a b. a -> b -> a
const Maybe a
forall a. Maybe a
Nothing (Maybe a -> Trie a -> Maybe a
forall a b. a -> b -> a
const Maybe a
forall a. Maybe a
Nothing)

-- TODO? move to "Data.Trie.Convenience"?
-- | Does a string have a value in the trie?
member :: ByteString -> Trie a -> Bool
{-# INLINE member #-}
member :: ByteString -> Trie a -> Bool
member ByteString
q = Maybe a -> Bool
forall a. Maybe a -> Bool
isJust (Maybe a -> Bool) -> (Trie a -> Maybe a) -> Trie a -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Trie a -> Maybe a
forall a. ByteString -> Trie a -> Maybe a
lookup ByteString
q


-- | Given a query, find the longest prefix with an associated value
-- in the trie, returning that prefix, it's value, and the remaining
-- string.
match :: Trie a -> ByteString -> Maybe (ByteString, a, ByteString)
match :: Trie a -> ByteString -> Maybe (ByteString, a, ByteString)
match Trie a
t ByteString
q =
    case Trie a -> ByteString -> Maybe (Int, a)
forall a. Trie a -> ByteString -> Maybe (Int, a)
match_ Trie a
t ByteString
q of
    Maybe (Int, a)
Nothing    -> Maybe (ByteString, a, ByteString)
forall a. Maybe a
Nothing
    Just (Int
n,a
x) ->
        case Int -> ByteString -> (ByteString, ByteString)
S.splitAt Int
n ByteString
q of
        (ByteString
p,ByteString
q') -> (ByteString, a, ByteString) -> Maybe (ByteString, a, ByteString)
forall a. a -> Maybe a
Just (ByteString
p, a
x, ByteString
q')


-- | Given a query, find all prefixes with associated values in the
-- trie, returning the prefixes, their values, and their remaining
-- strings. This function is a good producer for list fusion.
matches :: Trie a -> ByteString -> [(ByteString, a, ByteString)]
{-# INLINE matches #-}
matches :: Trie a -> ByteString -> [(ByteString, a, ByteString)]
matches Trie a
t ByteString
q = ((Int, a) -> (ByteString, a, ByteString))
-> [(Int, a)] -> [(ByteString, a, ByteString)]
forall a b. (a -> b) -> [a] -> [b]
map (Int, a) -> (ByteString, a, ByteString)
forall b. (Int, b) -> (ByteString, b, ByteString)
f (Trie a -> ByteString -> [(Int, a)]
forall a. Trie a -> ByteString -> [(Int, a)]
matches_ Trie a
t ByteString
q)
    where
    f :: (Int, b) -> (ByteString, b, ByteString)
f (Int
n,b
x) =
        case Int -> ByteString -> (ByteString, ByteString)
S.splitAt Int
n ByteString
q of
        (ByteString
p,ByteString
q') -> (ByteString
p, b
x, ByteString
q')


{---------------------------------------------------------------
-- Single-value modification functions (recurse and clone spine)
---------------------------------------------------------------}

-- | Insert a new key. If the key is already present, overrides the
-- old value
insert :: ByteString -> a -> Trie a -> Trie a
{-# INLINE insert #-}
insert :: ByteString -> a -> Trie a -> Trie a
insert = (ByteString -> a -> Maybe a -> Maybe a)
-> ByteString -> a -> Trie a -> Trie a
forall a.
(ByteString -> a -> Maybe a -> Maybe a)
-> ByteString -> a -> Trie a -> Trie a
alterBy (\ByteString
_ a
x Maybe a
_ -> a -> Maybe a
forall a. a -> Maybe a
Just a
x)

-- | Apply a function to the value at a key.
adjust :: (a -> a) -> ByteString -> Trie a -> Trie a
{-# INLINE adjust #-}
adjust :: (a -> a) -> ByteString -> Trie a -> Trie a
adjust a -> a
f ByteString
q = (ByteString -> a -> a -> a) -> ByteString -> a -> Trie a -> Trie a
forall a.
(ByteString -> a -> a -> a) -> ByteString -> a -> Trie a -> Trie a
adjustBy (\ByteString
_ a
_ -> a -> a
f) ByteString
q (String -> a
forall a. String -> a
impossible String
"adjust")
-- TODO: benchmark vs the definition with alterBy/liftM

-- | Remove the value stored at a key.
delete :: ByteString -> Trie a -> Trie a
{-# INLINE delete #-}
delete :: ByteString -> Trie a -> Trie a
delete ByteString
q = (ByteString -> a -> Maybe a -> Maybe a)
-> ByteString -> a -> Trie a -> Trie a
forall a.
(ByteString -> a -> Maybe a -> Maybe a)
-> ByteString -> a -> Trie a -> Trie a
alterBy (\ByteString
_ a
_ Maybe a
_ -> Maybe a
forall a. Maybe a
Nothing) ByteString
q (String -> a
forall a. String -> a
impossible String
"delete")


{---------------------------------------------------------------
-- Trie-combining functions
---------------------------------------------------------------}

-- | Combine two tries, resolving conflicts by choosing the value
-- from the left trie.
unionL :: Trie a -> Trie a -> Trie a
{-# INLINE unionL #-}
unionL :: Trie a -> Trie a -> Trie a
unionL = (a -> a -> Maybe a) -> Trie a -> Trie a -> Trie a
forall a. (a -> a -> Maybe a) -> Trie a -> Trie a -> Trie a
mergeBy (\a
x a
_ -> a -> Maybe a
forall a. a -> Maybe a
Just a
x)

-- | Combine two tries, resolving conflicts by choosing the value
-- from the right trie.
unionR :: Trie a -> Trie a -> Trie a
{-# INLINE unionR #-}
unionR :: Trie a -> Trie a -> Trie a
unionR = (a -> a -> Maybe a) -> Trie a -> Trie a -> Trie a
forall a. (a -> a -> Maybe a) -> Trie a -> Trie a -> Trie a
mergeBy (\a
_ a
y -> a -> Maybe a
forall a. a -> Maybe a
Just a
y)

----------------------------------------------------------------
----------------------------------------------------------- fin.