diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Author name here (c) 2022
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Author name here nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# int-like
+
+[![CircleCI](https://circleci.com/gh/ejconlon/int-like/tree/master.svg?style=svg)](https://circleci.com/gh/ejconlon/int-like/tree/master)
+
+Newtype wrappers over `IntSet` and `IntMap`
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/int-like.cabal b/int-like.cabal
new file mode 100644
--- /dev/null
+++ b/int-like.cabal
@@ -0,0 +1,116 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.34.4.
+--
+-- see: https://github.com/sol/hpack
+
+name:           int-like
+version:        0.1.0
+synopsis:       Newtype wrappers over IntSet and IntMap
+description:    Please see the README on GitHub at <https://github.com/ejconlon/int-like#readme>
+category:       Data Structures
+homepage:       https://github.com/ejconlon/int-like#readme
+bug-reports:    https://github.com/ejconlon/int-like/issues
+author:         Eric Conlon
+maintainer:     ejconlon@gmail.com
+copyright:      (c) 2022 Eric Conlon
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+
+source-repository head
+  type: git
+  location: https://github.com/ejconlon/int-like
+
+library
+  exposed-modules:
+      IntLike.Equiv
+      IntLike.Graph
+      IntLike.Map
+      IntLike.MultiMap
+      IntLike.Set
+  other-modules:
+      Paths_int_like
+  hs-source-dirs:
+      src
+  default-extensions:
+      BangPatterns
+      ConstraintKinds
+      DeriveAnyClass
+      DeriveFunctor
+      DeriveFoldable
+      DeriveGeneric
+      DeriveTraversable
+      DerivingStrategies
+      DerivingVia
+      FlexibleContexts
+      FlexibleInstances
+      FunctionalDependencies
+      GADTs
+      GeneralizedNewtypeDeriving
+      LambdaCase
+      KindSignatures
+      MultiParamTypeClasses
+      Rank2Types
+      ScopedTypeVariables
+      StandaloneDeriving
+      TemplateHaskell
+      TupleSections
+      TypeApplications
+      TypeOperators
+      TypeFamilies
+  ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints -fno-warn-unused-top-binds -fwrite-ide-info -hiedir=.hie
+  build-depends:
+      algebraic-graphs >=0.5 && <0.7
+    , base >=4.12 && <5
+    , containers ==0.6.*
+    , deepseq ==1.4.*
+    , hashable >=1.3 && <1.5
+  default-language: Haskell2010
+
+test-suite int-like-test
+  type: exitcode-stdio-1.0
+  main-is: Main.hs
+  other-modules:
+      Paths_int_like
+  hs-source-dirs:
+      test
+  default-extensions:
+      BangPatterns
+      ConstraintKinds
+      DeriveAnyClass
+      DeriveFunctor
+      DeriveFoldable
+      DeriveGeneric
+      DeriveTraversable
+      DerivingStrategies
+      DerivingVia
+      FlexibleContexts
+      FlexibleInstances
+      FunctionalDependencies
+      GADTs
+      GeneralizedNewtypeDeriving
+      LambdaCase
+      KindSignatures
+      MultiParamTypeClasses
+      Rank2Types
+      ScopedTypeVariables
+      StandaloneDeriving
+      TemplateHaskell
+      TupleSections
+      TypeApplications
+      TypeOperators
+      TypeFamilies
+  ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints -fno-warn-unused-top-binds -fwrite-ide-info -hiedir=.hie -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      algebraic-graphs >=0.5 && <0.7
+    , base >=4.12 && <5
+    , containers ==0.6.*
+    , deepseq ==1.4.*
+    , hashable >=1.3 && <1.5
+    , int-like
+    , tasty
+    , tasty-hunit
+  default-language: Haskell2010
diff --git a/src/IntLike/Equiv.hs b/src/IntLike/Equiv.hs
new file mode 100644
--- /dev/null
+++ b/src/IntLike/Equiv.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE DeriveAnyClass #-}
+
+module IntLike.Equiv
+  ( IntLikeEquiv
+  , fwdView
+  , bwdView
+  , empty
+  , insert
+  , partialInsert
+  , member
+  , lookupClass
+  ) where
+
+import Control.DeepSeq (NFData)
+import Data.Coerce (Coercible)
+import Data.Either (fromRight)
+import GHC.Generics (Generic)
+import IntLike.Map (IntLikeMap)
+import qualified IntLike.Map as ILM
+import IntLike.MultiMap (IntLikeMultiMap)
+import qualified IntLike.MultiMap as ILMM
+
+data IntLikeEquiv k v = IntLikeEquiv
+  { fwdView :: !(IntLikeMultiMap k v)
+  , bwdView :: !(IntLikeMap v k)
+  } deriving stock (Eq, Show, Generic)
+    deriving anyclass (NFData)
+
+empty :: IntLikeEquiv k v
+empty = IntLikeEquiv ILMM.empty ILM.empty
+{-# INLINE empty #-}
+
+insert :: (Coercible k Int, Coercible v Int) => k -> v -> IntLikeEquiv k v -> Either k (IntLikeEquiv k v)
+insert k v (IntLikeEquiv fwd bwd) =
+  case ILM.lookup v bwd of
+    Nothing -> Right (IntLikeEquiv (ILMM.insert k v fwd) (ILM.insert v k bwd))
+    Just k' -> Left k'
+
+partialInsert :: (Coercible k Int, Coercible v Int) => k -> v -> IntLikeEquiv k v -> IntLikeEquiv k v
+partialInsert k v = fromRight (error "duplicate insert into equiv") . insert k v
+
+member :: (Eq k, Coercible v Int) => k -> v -> IntLikeEquiv k v -> Bool
+member k v m = Just k == lookupClass v m
+
+lookupClass :: (Coercible v Int) => v -> IntLikeEquiv k v -> Maybe k
+lookupClass v = ILM.lookup v . bwdView
diff --git a/src/IntLike/Graph.hs b/src/IntLike/Graph.hs
new file mode 100644
--- /dev/null
+++ b/src/IntLike/Graph.hs
@@ -0,0 +1,74 @@
+module IntLike.Graph
+  ( IntLikeGraph (..)
+  , adjacencyIntMultiMap
+  , vertexList
+  , fromDirectedEdges
+  , fromUndirectedEdges
+  , reachable
+  , Component (..)
+  , undirectedComponents
+  ) where
+
+import Algebra.Graph.AdjacencyIntMap (AdjacencyIntMap)
+import qualified Algebra.Graph.AdjacencyIntMap as AdjacencyIntMap
+import qualified Algebra.Graph.AdjacencyIntMap.Algorithm as AIMA
+import Algebra.Graph.Class (Graph (..))
+import Control.DeepSeq (NFData)
+import Data.Coerce (Coercible, coerce)
+import Data.Foldable (foldl')
+import Data.Hashable (Hashable)
+import Data.Tuple (swap)
+import IntLike.Equiv (IntLikeEquiv)
+import qualified IntLike.Equiv as ILE
+import IntLike.Map (IntLikeMap (..))
+import IntLike.MultiMap (IntLikeMultiMap)
+import IntLike.Set (IntLikeSet (..))
+import qualified IntLike.Set as ILS
+
+newtype IntLikeGraph x = IntLikeGraph { unIntLikeGraph :: AdjacencyIntMap }
+  deriving newtype (Eq, Show, NFData)
+
+instance Coercible x Int => Graph (IntLikeGraph x) where
+  type Vertex (IntLikeGraph x) = x
+  empty = IntLikeGraph AdjacencyIntMap.empty
+  vertex v = IntLikeGraph (AdjacencyIntMap.vertex (coerce v))
+  overlay x y = IntLikeGraph (AdjacencyIntMap.overlay (unIntLikeGraph x) (unIntLikeGraph y))
+  connect x y = IntLikeGraph (AdjacencyIntMap.connect (unIntLikeGraph x) (unIntLikeGraph y))
+
+adjacencyIntMultiMap :: IntLikeGraph x -> IntLikeMultiMap x x
+adjacencyIntMultiMap = coerce . AdjacencyIntMap.adjacencyIntMap . unIntLikeGraph
+{-# INLINE adjacencyIntMultiMap #-}
+
+vertexList :: Coercible x Int => IntLikeGraph x -> [x]
+vertexList = coerce . AdjacencyIntMap.vertexList . unIntLikeGraph
+{-# INLINE vertexList #-}
+
+fromDirectedEdges :: Coercible x Int => [(x, x)] -> IntLikeGraph x
+fromDirectedEdges = IntLikeGraph . AdjacencyIntMap.edges . coerce
+{-# INLINE fromDirectedEdges #-}
+
+fromUndirectedEdges :: Coercible x Int => [(x, x)] -> IntLikeGraph x
+fromUndirectedEdges es = overlay (fromDirectedEdges es) (fromDirectedEdges (fmap swap es))
+{-# INLINE fromUndirectedEdges #-}
+
+reachable :: Coercible x Int => x -> IntLikeGraph x -> [x]
+reachable x = coerce . AIMA.reachable (coerce x) . unIntLikeGraph
+{-# INLINE reachable #-}
+
+newtype Component = Component { unComponent :: Int }
+  deriving stock (Show)
+  deriving newtype (Eq, Ord, Enum, Hashable, NFData)
+
+undirectedComponents :: Coercible x Int => [(x, x)] -> IntLikeEquiv Component x
+undirectedComponents es = go 0 startVs ILE.empty where
+  g = fromUndirectedEdges es
+  startVs = ILS.fromList (vertexList g)
+  go i vs eqv =
+    case ILS.minView vs of
+      Nothing -> eqv
+      Just (v, vs') ->
+        let rs = reachable v g
+            -- partial: ok by construction of graph and defn of reachable
+            eqv' = foldl' (flip (ILE.partialInsert (Component i))) eqv rs
+            vs'' = foldl' (flip ILS.delete) vs' rs
+        in go (i + 1) vs'' eqv'
diff --git a/src/IntLike/Map.hs b/src/IntLike/Map.hs
new file mode 100644
--- /dev/null
+++ b/src/IntLike/Map.hs
@@ -0,0 +1,124 @@
+module IntLike.Map
+  ( IntLikeMap (..)
+  , empty
+  , singleton
+  , fromList
+  , size
+  , null
+  , member
+  , toList
+  , keys
+  , elems
+  , lookup
+  , partialLookup
+  , findWithDefault
+  , insert
+  , insertWith
+  , adjust
+  , alter
+  , delete
+  , minViewWithKey
+  , filter
+  , restrictKeys
+  , map
+  , insertState
+  ) where
+
+import Control.DeepSeq (NFData)
+import Data.Coerce (Coercible, coerce)
+import Data.IntMap.Strict (IntMap)
+import qualified Data.IntMap.Strict as IntMap
+import IntLike.Set (IntLikeSet (..))
+import Prelude hiding (filter, lookup, map, null)
+
+newtype IntLikeMap x a = IntLikeMap { unIntLikeMap :: IntMap a }
+  deriving stock (Show, Traversable)
+  deriving newtype (Eq, Functor, Foldable, NFData, Semigroup, Monoid)
+
+empty :: IntLikeMap x a
+empty = IntLikeMap IntMap.empty
+{-# INLINE empty #-}
+
+singleton :: Coercible x Int => x -> a -> IntLikeMap x a
+singleton x = IntLikeMap . IntMap.singleton (coerce x)
+{-# INLINE singleton #-}
+
+fromList :: Coercible x Int => [(x, a)] -> IntLikeMap x a
+fromList = IntLikeMap . IntMap.fromList . coerce
+{-# INLINE fromList #-}
+
+size :: IntLikeMap x a -> Int
+size = IntMap.size . unIntLikeMap
+{-# INLINE size #-}
+
+null :: IntLikeMap x a -> Bool
+null = IntMap.null . unIntLikeMap
+{-# INLINE null #-}
+
+member :: Coercible x Int => x -> IntLikeMap x a -> Bool
+member x = IntMap.member (coerce x) . unIntLikeMap
+{-# INLINE member #-}
+
+toList :: Coercible x Int => IntLikeMap x a -> [(x, a)]
+toList = coerce . IntMap.toList . unIntLikeMap
+{-# INLINE toList #-}
+
+keys :: Coercible x Int => IntLikeMap x a -> [x]
+keys = coerce . IntMap.keys . unIntLikeMap
+{-# INLINE keys #-}
+
+elems :: IntLikeMap x a -> [a]
+elems = IntMap.elems . unIntLikeMap
+{-# INLINE elems #-}
+
+lookup :: Coercible x Int => x -> IntLikeMap x a -> Maybe a
+lookup x = IntMap.lookup (coerce x) . unIntLikeMap
+{-# INLINE lookup #-}
+
+partialLookup :: Coercible x Int => x -> IntLikeMap x a -> a
+partialLookup x m = unIntLikeMap m IntMap.! coerce x
+{-# INLINE partialLookup #-}
+
+findWithDefault :: Coercible x Int => a -> x -> IntLikeMap x a -> a
+findWithDefault a x = IntMap.findWithDefault a (coerce x) . unIntLikeMap
+{-# INLINE findWithDefault #-}
+
+insert :: Coercible x Int => x -> a -> IntLikeMap x a -> IntLikeMap x a
+insert x a = IntLikeMap . IntMap.insert (coerce x) a . unIntLikeMap
+{-# INLINE insert #-}
+
+insertWith :: Coercible x Int => (a -> a -> a) -> x -> a -> IntLikeMap x a -> IntLikeMap x a
+insertWith f x a = IntLikeMap . IntMap.insertWith f (coerce x) a . unIntLikeMap
+{-# INLINE insertWith #-}
+
+adjust :: Coercible x Int => (a -> a) -> x -> IntLikeMap x a -> IntLikeMap x a
+adjust f x = IntLikeMap . IntMap.adjust f (coerce x) . unIntLikeMap
+{-# INLINE adjust #-}
+
+alter :: Coercible x Int => (Maybe a -> Maybe a) -> x -> IntLikeMap x a -> IntLikeMap x a
+alter f x = IntLikeMap . IntMap.alter f (coerce x) . unIntLikeMap
+{-# INLINE alter #-}
+
+delete :: Coercible x Int => x -> IntLikeMap x a -> IntLikeMap x a
+delete x = IntLikeMap . IntMap.delete (coerce x) . unIntLikeMap
+{-# INLINE delete #-}
+
+minViewWithKey :: Coercible x Int => IntLikeMap x a -> Maybe ((x, a), IntLikeMap x a)
+minViewWithKey = coerce . IntMap.minViewWithKey . unIntLikeMap
+{-# INLINE minViewWithKey #-}
+
+filter :: (a -> Bool) -> IntLikeMap x a -> IntLikeMap x a
+filter f = IntLikeMap . IntMap.filter f . unIntLikeMap
+{-# INLINE filter #-}
+
+restrictKeys :: IntLikeMap x a -> IntLikeSet x -> IntLikeMap x a
+restrictKeys m s = IntLikeMap (IntMap.restrictKeys (unIntLikeMap m) (unIntLikeSet s))
+{-# INLINE restrictKeys #-}
+
+map :: (a -> b) -> IntLikeMap x a -> IntLikeMap x b
+map f = IntLikeMap . IntMap.map f . unIntLikeMap
+{-# INLINE map #-}
+
+insertState :: Coercible x Int => (Maybe a -> b) -> x -> a -> IntLikeMap x a -> (b, IntLikeMap x a)
+insertState f x a = coerce . IntMap.alterF (\m -> (f m, Just a)) (coerce x) . unIntLikeMap
+{-# INLINE insertState #-}
diff --git a/src/IntLike/MultiMap.hs b/src/IntLike/MultiMap.hs
new file mode 100644
--- /dev/null
+++ b/src/IntLike/MultiMap.hs
@@ -0,0 +1,58 @@
+module IntLike.MultiMap
+  ( IntLikeMultiMap
+  , empty
+  , size
+  , toList
+  , insert
+  , member
+  , invertDisjoint
+  , unsafeInvertDisjoint
+  , fromInvertedMap
+  ) where
+
+import Control.Monad (foldM)
+import Data.Coerce (Coercible)
+import Data.Foldable (foldl')
+import IntLike.Map (IntLikeMap)
+import qualified IntLike.Map as ILM
+import IntLike.Set (IntLikeSet)
+import qualified IntLike.Set as ILS
+
+type IntLikeMultiMap k v = IntLikeMap k (IntLikeSet v)
+
+empty :: IntLikeMultiMap k v
+empty = ILM.empty
+{-# INLINE empty #-}
+
+size :: IntLikeMultiMap k v -> Int
+size = ILM.size
+{-# INLINE size #-}
+
+toList :: Coercible k Int => IntLikeMultiMap k v -> [(k, IntLikeSet v)]
+toList = ILM.toList
+{-# INLINE toList #-}
+
+insert :: (Coercible k Int, Coercible v Int) => k -> v -> IntLikeMultiMap k v -> IntLikeMultiMap k v
+insert k v = ILM.insertWith ILS.union k (ILS.singleton v)
+{-# INLINE insert #-}
+
+member :: (Coercible k Int, Coercible v Int) => k -> v -> IntLikeMultiMap k v -> Bool
+member k v = maybe False (ILS.member v) . ILM.lookup k
+{-# INLINE member #-}
+
+invertDisjoint :: (Coercible k Int, Coercible v Int) => IntLikeMultiMap k v -> Either (k, k, v) (IntLikeMap v k)
+invertDisjoint = foldM go1 ILM.empty . ILM.toList where
+  go1 m (k, vs) = foldM (go2 k) m (ILS.toList vs)
+  go2 k m v =
+    case ILM.lookup v m of
+      Nothing -> Right (ILM.insert v k m)
+      Just k' -> Left (k, k', v)
+
+unsafeInvertDisjoint :: (Coercible k Int, Coercible v Int) => IntLikeMultiMap k v -> IntLikeMap v k
+unsafeInvertDisjoint = foldl' go1 ILM.empty . ILM.toList where
+  go1 m (k, vs) = foldl' (go2 k) m (ILS.toList vs)
+  go2 k m v = ILM.insert v k m
+
+fromInvertedMap :: (Coercible k Int, Coercible v Int) => IntLikeMap k v -> IntLikeMultiMap v k
+fromInvertedMap = foldl' (\m (k, v) -> insert v k m) empty . ILM.toList
+{-# INLINE fromInvertedMap #-}
diff --git a/src/IntLike/Set.hs b/src/IntLike/Set.hs
new file mode 100644
--- /dev/null
+++ b/src/IntLike/Set.hs
@@ -0,0 +1,131 @@
+module IntLike.Set
+  ( IntLikeSet (..)
+  , empty
+  , singleton
+  , fromList
+  , size
+  , null
+  , member
+  , toList
+  , insert
+  , delete
+  , isSubsetOf
+  , intersection
+  , difference
+  , union
+  , unions
+  , findMin
+  , minView
+  , disjoint
+  , map
+  , filter
+  , insertState
+  , orderedPairs
+  , unorderedPairs
+  ) where
+
+import Control.DeepSeq (NFData)
+import Data.Coerce (Coercible, coerce)
+import Data.Foldable (foldl')
+import Data.IntSet (IntSet)
+import qualified Data.IntSet as IntSet
+import Prelude hiding (filter, map, null)
+
+newtype IntLikeSet x = IntLikeSet { unIntLikeSet :: IntSet }
+  deriving stock (Show)
+  deriving newtype (Eq, NFData, Semigroup, Monoid)
+
+empty :: IntLikeSet x
+empty = IntLikeSet IntSet.empty
+{-# INLINE empty #-}
+
+singleton :: Coercible x Int => x -> IntLikeSet x
+singleton = IntLikeSet . IntSet.singleton . coerce
+{-# INLINE singleton #-}
+
+fromList :: Coercible x Int => [x] -> IntLikeSet x
+fromList = IntLikeSet . IntSet.fromList . coerce
+{-# INLINE fromList #-}
+
+size :: IntLikeSet x -> Int
+size = IntSet.size . unIntLikeSet
+{-# INLINE size #-}
+
+null :: IntLikeSet x -> Bool
+null = IntSet.null . unIntLikeSet
+{-# INLINE null #-}
+
+member :: Coercible x Int => x -> IntLikeSet x -> Bool
+member x = IntSet.member (coerce x) . unIntLikeSet
+{-# INLINE member #-}
+
+toList :: Coercible x Int => IntLikeSet x -> [x]
+toList = coerce . IntSet.toList . unIntLikeSet
+{-# INLINE toList #-}
+
+insert :: Coercible x Int => x -> IntLikeSet x -> IntLikeSet x
+insert x = IntLikeSet . IntSet.insert (coerce x) . unIntLikeSet
+{-# INLINE insert #-}
+
+delete :: Coercible x Int => x -> IntLikeSet x -> IntLikeSet x
+delete x = IntLikeSet . IntSet.delete (coerce x) . unIntLikeSet
+{-# INLINE delete #-}
+
+isSubsetOf :: IntLikeSet x -> IntLikeSet x -> Bool
+isSubsetOf xs ys = IntSet.isSubsetOf (unIntLikeSet xs) (unIntLikeSet ys)
+{-# INLINE isSubsetOf #-}
+
+intersection :: IntLikeSet x -> IntLikeSet x -> IntLikeSet x
+intersection xs ys = IntLikeSet (IntSet.intersection (unIntLikeSet xs) (unIntLikeSet ys))
+{-# INLINE intersection #-}
+
+difference :: IntLikeSet x -> IntLikeSet x -> IntLikeSet x
+difference xs ys = IntLikeSet (IntSet.difference (unIntLikeSet xs) (unIntLikeSet ys))
+{-# INLINE difference #-}
+
+union :: IntLikeSet x -> IntLikeSet x -> IntLikeSet x
+union xs ys = IntLikeSet (IntSet.union (unIntLikeSet xs) (unIntLikeSet ys))
+{-# INLINE union #-}
+
+-- Copied here because coercion through f is difficult
+unions :: Foldable f => f (IntLikeSet x) -> IntLikeSet x
+unions = foldl' union empty
+{-# INLINE unions #-}
+
+findMin :: Coercible x Int => IntLikeSet x -> x
+findMin = coerce . IntSet.findMin . unIntLikeSet
+{-# INLINE findMin #-}
+
+minView :: Coercible x Int => IntLikeSet x -> Maybe (x, IntLikeSet x)
+minView = coerce . IntSet.minView . unIntLikeSet
+{-# INLINE minView #-}
+
+disjoint :: IntLikeSet x -> IntLikeSet x -> Bool
+disjoint a b = IntSet.disjoint (unIntLikeSet a) (unIntLikeSet b)
+{-# INLINE disjoint #-}
+
+map :: (Coercible x Int, Coercible y Int) => (x -> y) -> IntLikeSet x -> IntLikeSet y
+map f = IntLikeSet . IntSet.map (coerce f) . unIntLikeSet
+{-# INLINE map #-}
+
+filter :: (Coercible x Int) => (x -> Bool) -> IntLikeSet x -> IntLikeSet x
+filter f = IntLikeSet . IntSet.filter (coerce f) . unIntLikeSet
+{-# INLINE filter #-}
+
+insertState :: Coercible x Int => (Bool -> b) -> x -> IntLikeSet x -> (b, IntLikeSet x)
+insertState f x = coerce . IntSet.alterF (\b -> (f b, True)) (coerce x) . unIntLikeSet
+{-# INLINE insertState #-}
+
+orderedPairs :: Coercible x Int => IntLikeSet x -> [(x, x)]
+orderedPairs s = let vs = toList s in [(x, y) | x <- vs, y <- vs]
+
+unorderedPairs :: Coercible x Int => IntLikeSet x -> [(x, x)]
+unorderedPairs = go1 . toList where
+  go1 vs =
+    case vs of
+      [] -> []
+      x:xs -> go2 x xs xs
+  go2 x vl vs =
+    case vl of
+      [] -> go1 vs
+      y:vl' -> (x, y):go2 x vl' vs
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,14 @@
+module Main (main) where
+
+import Test.Tasty (TestTree, defaultMain, testGroup)
+import Test.Tasty.HUnit (testCase, (@?=))
+
+testSimple :: TestTree
+testSimple = testCase "simple" $ do
+    let actual = (1 + 1) :: Int
+        expected = 2 :: Int
+    actual @?= expected
+
+-- TODO uh, write some tests!?!
+main :: IO ()
+main = defaultMain (testGroup "IntLike" [testSimple])
