memberWithinG' :: (Hashable a, Eq a) => Omega Int -> a -> Closed a -> (Bool, Closed a)
memberWithinG' n a closed@(Closed m iter closef)
  | n - A m < 0 = (Set.member a (setOf closef), closed)
  | otherwise   = memberWithinI (n - A m) 0 closef
  where
    memberWithinI 0 up closef =
      -- We KNOW that 'n' cannot be 'O' here since it was able to be
      -- decremented to 0.
      case n of
        O     -> error "Algebra.Closure.Set.BreadthFirst: Impossible"
        (A n') -> (Set.member a (setOf closef), Closed n' iter closef)

    memberWithinI dn up closef@(ClosedF set next)
      | Set.member a set = (True, Closed (m + up) iter closef)
      | otherwise        = memberWithinI (opred dn) (succ up) next

-- | @memberWithin' n a@ checks to see whether an element is within a
-- 'Closed' set after @n@ improvements (but does not guarantee the @n@
-- is the minimal number needed). The 'Closed' set returned is a
-- compressed, memoized 'Closed' set which may be faster to query.
memberWithinG' :: (Hashable a, Eq a) => Omega Int -> a -> Closed a -> (Bool, Closed a)
memberWithinG' n a closed@(Closed m iter closef)
  | n - A m < 0 = (Set.member a (setOf closef), closed)
  | otherwise   = memberWithinI (n - A m) 0 closef
  where
    memberWithinI 0 up closef =
      -- We KNOW that 'n' cannot be 'O' here since it was able to be
      -- decremented to 0.
      case n of
        O     -> error "Algebra.Closure.Set.BreadthFirst: Impossible"
        (A n') -> (Set.member a (setOf closef), Closed n' iter closef)

    memberWithinI dn up closef@(ClosedF set next)
      | Set.member a set = (True, Closed (m + up) iter closef)
      | otherwise        = memberWithinI (opred dn) (succ up) next

-- | @memberWithin' n a@ checks to see whether an element is within a
-- 'Closed' set after @n@ improvements (but does not guarantee the @n@
-- is the minimal number needed). The 'Closed' set returned is a
-- compressed, memoized 'Closed' set which may be faster to query.
memberWithinG' n a closed@(Closed m iter closef)
  | n - A m < 0 = (Set.member a (setOf closef), closed)
  | otherwise   = memberWithinI (n - A m) 0 closef
  where
    memberWithinI 0 up closef =
      -- We KNOW that 'n' cannot be 'O' here since it was able to be
      -- decremented to 0.
      case n of
        O     -> error "Algebra.Closure.Set.BreadthFirst: Impossible"
        (A n') -> (Set.member a (setOf closef), Closed n' iter closef)

    memberWithinI dn up closef@(ClosedF set next)
      | Set.member a set = (True, Closed (m + up) iter closef)
      | otherwise        = memberWithinI (opred dn) (succ up) next

-- | @memberWithin' n a@ checks to see whether an element is within a
-- 'Closed' set after @n@ improvements (but does not guarantee the @n@
-- is the minimal number needed). The 'Closed' set returned is a
-- compressed, memoized 'Closed' set which may be faster to query.