lookupLE :: Key -> IntMap a -> Maybe (Key, a)
lookupLE k = k `seq` start
  where
    start (IntMap (WordMap Empty)) = Nothing
    start (IntMap (WordMap (NonEmpty min minV Tip)))
        | w2i min <= k = Just (w2i min, minV)
        | otherwise = Nothing
    start (IntMap (WordMap (NonEmpty min minV node@(Bin max maxV l r))))
        | w2i (xor min max) < 0 =
            if w2i min <= k -- if this is true, we know the result is positive
            then Just (goL (xor min (i2w k)) min minV l)
            else if w2i max <= k -- if this is true, k is between the positives and negatives
                 then Just (w2i max, maxV)
                 else case r of
                        Tip -> Nothing
                        Bin minI minVI lI rI
                            | minI > i2w k -> Nothing
                            | otherwise -> Just (goL (xor minI (i2w k)) minI minVI (Bin max maxV lI rI))
        | w2i min <= k = Just (goL (xor min (i2w k)) min minV node)
        | otherwise = Nothing
    
    goL !_ min minV Tip = (w2i min, minV)
    goL !xorCache min minV (Bin max maxV l r)
        | w2i max <= k = (w2i max, maxV)
        | xorCache < xorCacheMax = goL xorCache min minV l
        | otherwise = goR xorCacheMax r min minV l
      where
        xorCacheMax = xor (i2w k) max
    
    goR !_ Tip fMin fMinV fallback = getMax fMin fMinV fallback
    goR !xorCache (Bin min minV l r) fMin fMinV fallback
        | w2i min > k = getMax fMin fMinV fallback
        | xorCache < xorCacheMin = goR xorCache r min minV l
        | otherwise = goL xorCacheMin min minV l
      where
        xorCacheMin = xor min (i2w k)
    
    getMax min minV Tip = (w2i min, minV)
    getMax _   _   (Bin max maxV _ _) = (w2i max, maxV)

-- | /O(log n)/. Find smallest key greater than the given one and return the
-- corresponding (key, value) pair.
--
-- > lookupGT 4 (fromList [(3,'a'), (5,'b')]) == Just (5, 'b')
-- > lookupGT 5 (fromList [(3,'a'), (5,'b')]) == Nothing
lookupLE :: Key -> IntMap a -> Maybe (Key, a)
lookupLE k = k `seq` start
  where
    start (IntMap (WordMap Empty)) = Nothing
    start (IntMap (WordMap (NonEmpty min minV Tip)))
        | w2i min <= k = Just (w2i min, minV)
        | otherwise = Nothing
    start (IntMap (WordMap (NonEmpty min minV node@(Bin max maxV l r))))
        | w2i (xor min max) < 0 =
            if w2i min <= k -- if this is true, we know the result is positive
            then Just (goL (xor min (i2w k)) min minV l)
            else if w2i max <= k -- if this is true, k is between the positives and negatives
                 then Just (w2i max, maxV)
                 else case r of
                        Tip -> Nothing
                        Bin minI minVI lI rI
                            | minI > i2w k -> Nothing
                            | otherwise -> Just (goL (xor minI (i2w k)) minI minVI (Bin max maxV lI rI))
        | w2i min <= k = Just (goL (xor min (i2w k)) min minV node)
        | otherwise = Nothing
    
    goL !_ min minV Tip = (w2i min, minV)
    goL !xorCache min minV (Bin max maxV l r)
        | w2i max <= k = (w2i max, maxV)
        | xorCache < xorCacheMax = goL xorCache min minV l
        | otherwise = goR xorCacheMax r min minV l
      where
        xorCacheMax = xor (i2w k) max
    
    goR !_ Tip fMin fMinV fallback = getMax fMin fMinV fallback
    goR !xorCache (Bin min minV l r) fMin fMinV fallback
        | w2i min > k = getMax fMin fMinV fallback
        | xorCache < xorCacheMin = goR xorCache r min minV l
        | otherwise = goL xorCacheMin min minV l
      where
        xorCacheMin = xor min (i2w k)
    
    getMax min minV Tip = (w2i min, minV)
    getMax _   _   (Bin max maxV _ _) = (w2i max, maxV)

-- | /O(log n)/. Find smallest key greater than the given one and return the
-- corresponding (key, value) pair.
--
-- > lookupGT 4 (fromList [(3,'a'), (5,'b')]) == Just (5, 'b')
-- > lookupGT 5 (fromList [(3,'a'), (5,'b')]) == Nothing
lookupLE k = k `seq` start
  where
    start (IntMap (WordMap Empty)) = Nothing
    start (IntMap (WordMap (NonEmpty min minV Tip)))
        | w2i min <= k = Just (w2i min, minV)
        | otherwise = Nothing
    start (IntMap (WordMap (NonEmpty min minV node@(Bin max maxV l r))))
        | w2i (xor min max) < 0 =
            if w2i min <= k -- if this is true, we know the result is positive
            then Just (goL (xor min (i2w k)) min minV l)
            else if w2i max <= k -- if this is true, k is between the positives and negatives
                 then Just (w2i max, maxV)
                 else case r of
                        Tip -> Nothing
                        Bin minI minVI lI rI
                            | minI > i2w k -> Nothing
                            | otherwise -> Just (goL (xor minI (i2w k)) minI minVI (Bin max maxV lI rI))
        | w2i min <= k = Just (goL (xor min (i2w k)) min minV node)
        | otherwise = Nothing
    
    goL !_ min minV Tip = (w2i min, minV)
    goL !xorCache min minV (Bin max maxV l r)
        | w2i max <= k = (w2i max, maxV)
        | xorCache < xorCacheMax = goL xorCache min minV l
        | otherwise = goR xorCacheMax r min minV l
      where
        xorCacheMax = xor (i2w k) max
    
    goR !_ Tip fMin fMinV fallback = getMax fMin fMinV fallback
    goR !xorCache (Bin min minV l r) fMin fMinV fallback
        | w2i min > k = getMax fMin fMinV fallback
        | xorCache < xorCacheMin = goR xorCache r min minV l
        | otherwise = goL xorCacheMin min minV l
      where
        xorCacheMin = xor min (i2w k)
    
    getMax min minV Tip = (w2i min, minV)
    getMax _   _   (Bin max maxV _ _) = (w2i max, maxV)

-- | /O(log n)/. Find smallest key greater than the given one and return the
-- corresponding (key, value) pair.
--
-- > lookupGT 4 (fromList [(3,'a'), (5,'b')]) == Just (5, 'b')
-- > lookupGT 5 (fromList [(3,'a'), (5,'b')]) == Nothing