module Data.Homeomorphic.Check where

import qualified Data.Homeomorphic.Simple as H1
import qualified Data.Homeomorphic.SimpleParallel as H2
import qualified Data.Homeomorphic.Hash1 as H3
import qualified Data.Homeomorphic.Hash2 as H4
import qualified Data.Homeomorphic.Memo as H5
import qualified Data.Homeomorphic.MemoCache as H6

import Data.Homeomorphic.Internal
import Data.Maybe
import Data.List(sort,(\\))


data Homeomorphic k v = Homeomorphic
    (H1.Homeomorphic k v)
    (H2.Homeomorphic k v)
    (H3.Homeomorphic k v)
    (H4.Homeomorphic k v)
    (H5.Homeomorphic k v)
    (H6.Homeomorphic k v)

empty :: Homeomorphic k v
empty = Homeomorphic
    H1.empty
    H2.empty
    H3.empty
    H4.empty
    H5.empty
    H6.empty


insert :: Ord k => Shell k -> v -> Homeomorphic k v -> Homeomorphic k v
insert k v (Homeomorphic h1 h2 h3 h4 h5 h6) = Homeomorphic
    (H1.insert k v h1)
    (H2.insert k v h2)
    (H3.insert k v h3)
    (H4.insert k v h4)
    (H5.insert k v h5)
    (H6.insert k v h6)


-- additional Eq v constraint required to check the answers
-- additional Show constraints required to give a meainingful error message
find :: (Ord k, Eq v, Show v, Show k) => Shell k -> Homeomorphic k v -> [v]
find k (Homeomorphic h1 h2 h3 h4 h5 h6) = check
    [H1.find k h1 >< H1.findOne k h1
    ,H2.find k h2 >< H2.findOne k h2
    ,H3.find k h3 >< H3.findOne k h3
    ,H4.find k h4 >< H4.findOne k h4
    ,H5.find k h5 >< H5.findOne k h5
    ,H6.find k h6 >< H6.findOne k h6
    ]
    where
        [] >< Nothing = []
        xs >< Just x | x `elem` xs = xs
        _ >< _ = error "Find, inconsistent between find and findOne"

        check (x:xs) | all (setEq x) xs = x
        check _ = error "Find, inconsistent between different ones"

        setEq x y = null (x \\ y) && null (y \\ x)


-- find does lots of checking, so can just use that
findOne :: (Ord k, Eq v, Show v, Show k) => Shell k -> Homeomorphic k v -> Maybe v
findOne k = listToMaybe . find k