diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,5 @@
+# Version history for skew-list
+
+## 0.1
+
+- First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2017-2019, Oleg Grenrus
+
+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 Oleg Grenrus 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/bench/skew-list-bench.hs b/bench/skew-list-bench.hs
new file mode 100644
--- /dev/null
+++ b/bench/skew-list-bench.hs
@@ -0,0 +1,101 @@
+module Main where
+
+import Criterion.Main (bench, bgroup, defaultMain, nf, whnf)
+import Data.Foldable  (foldl')
+
+import qualified Data.List            as L
+import qualified Data.RAList          as R
+import qualified Data.Sequence        as Q
+import qualified Data.SkewList.Strict as S
+import qualified Data.Vector          as V
+import qualified Data.Vector.Unboxed  as U
+
+size :: Int
+-- size = 16383 -- bad size for RAList
+size = 16384 -- good size for RAList
+
+idx :: Int
+idx = 8888
+
+list :: [Int]
+list = [1 .. size]
+
+ralist :: R.RAList Int
+ralist = R.fromList list
+
+skewed :: S.SkewList Int
+skewed = S.fromList list
+
+vector :: V.Vector Int
+vector = V.fromList list
+
+uvector :: U.Vector Int
+uvector = U.fromList list
+
+sequ :: Q.Seq Int
+sequ = Q.fromList list
+
+main :: IO ()
+main = defaultMain
+    [ bgroup "Index"
+        [ bench "List"           $ whnf (\xs -> xs L.!! idx) list
+        , bench "RAList"         $ whnf (\xs -> xs R.!  idx) ralist
+        , bench "Vector"         $ whnf (\xs -> xs V.!  idx) vector
+        , bench "Vector.Unboxed" $ whnf (\xs -> xs U.!  idx) uvector
+        , bench "Seq"            $ whnf (\xs -> xs `Q.index`  idx) sequ
+        , bench "SkewList"       $ whnf (\xs -> xs S.!  idx) skewed
+        ]
+    , bgroup "Cons"
+        [ bench "List"           $ whnf (0 :)      list
+        , bench "RAList"         $ whnf (R.cons 0) ralist
+        , bench "Vector"         $ whnf (V.cons 0) vector
+        , bench "Vector.Unboxed" $ whnf (U.cons 0) uvector
+        , bench "Seq"            $ whnf (0 Q.<|) sequ
+        , bench "SkewList"       $ whnf (S.cons 0) skewed
+        ]
+    , bgroup "Length"
+        [ bench "List"           $ whnf L.length list
+        , bench "RAList"         $ whnf R.length ralist
+        , bench "Vector"         $ whnf V.length vector
+        , bench "Vector.Unboxed" $ whnf U.length uvector
+        , bench "Seq"            $ whnf Q.length sequ
+        , bench "SkewList"       $ whnf S.length skewed
+        ]
+    , bgroup "IndexAfterCons"
+        [ bench "List"           $ whnf (\xs -> (0 : xs)    L.!! idx) list
+        , bench "RAList"         $ whnf (\xs -> R.cons 0 xs R.!  idx) ralist
+        , bench "Vector"         $ whnf (\xs -> V.cons 0 xs V.!  idx) vector
+        , bench "Vector.Unboxed" $ whnf (\xs -> U.cons 0 xs U.!  idx) uvector
+        , bench "Seq"            $ whnf (\xs -> (0 Q.<| xs) `Q.index` idx) sequ
+        , bench "SkewList"       $ whnf (\xs -> S.cons 0 xs S.!  idx) skewed
+        ]
+
+    , bgroup "Append"
+        [ bench "List"           $ nf (\xs -> xs <> xs) list
+        , bench "RAList"         $ nf (\xs -> xs <> xs) ralist
+        , bench "Vector"         $ nf (\xs -> xs <> xs) vector
+        , bench "Vector.Unboxed" $ nf (\xs -> xs <> xs) uvector
+        , bench "Seq"            $ nf (\xs -> xs <> xs) sequ
+        , bench "SkewList"       $ nf (\xs -> xs <> xs) skewed
+        , bench "SkewList slow"  $ nf (\xs -> S.foldr S.cons xs xs) skewed
+        ]
+    , bgroup "IndexAfterAppend"
+        [ bench "List"           $ nf (\xs -> (xs <> xs) L.!! idx) list
+        , bench "RAList"         $ nf (\xs -> (xs <> xs) R.!  idx) ralist
+        , bench "Vector"         $ nf (\xs -> (xs <> xs) V.!  idx) vector
+        , bench "Vector.Unboxed" $ nf (\xs -> (xs <> xs) U.!  idx) uvector
+        , bench "Seq"            $ nf (\xs -> (xs <> xs) `Q.index` idx) sequ
+        , bench "SkewList"       $ nf (\xs -> (xs <> xs) S.!  idx) skewed
+        , bench "SkewList slow"  $ nf (\xs -> S.foldr S.cons xs xs S.! idx) skewed
+        ]
+
+    , bgroup "Sum"
+        [ bench "List"            $ whnf sum list
+        , bench "List foldl'"     $ whnf (foldl' (+) 0) list
+        , bench "RAList"          $ whnf sum ralist
+        , bench "Vector"          $ whnf sum vector
+        , bench "Seq"             $ whnf sum sequ
+        , bench "SkewList"        $ whnf sum skewed
+        , bench "SkewList foldl'" $ whnf (foldl' (+) 0) skewed
+        ]
+    ]
diff --git a/skew-list.cabal b/skew-list.cabal
new file mode 100644
--- /dev/null
+++ b/skew-list.cabal
@@ -0,0 +1,123 @@
+cabal-version:      2.2
+name:               skew-list
+version:            0.1
+synopsis:           Random access lists: skew binary
+category:           Data
+description:
+  This package provides ordinary random access list, 'SkewList'
+  implemented using skew binary approach.
+  .
+  It's worth comparing to ordinary lists, binary random access list (as in @ral@ package) and vectors (@vector@ package)
+  across two operations: indexing and consing.
+  .
+  +------------------------------+------------+----------+
+  |                              | Consing    | Indexing |
+  +------------------------------+------------+----------+
+  | Ordinary list, @[a]@         | O(1)       | O(n)     |
+  +------------------------------+------------+----------+
+  | Binary list, @RAList a@      | O(log n)   | O(log n) |
+  +------------------------------+------------+----------+
+  | Vector, @Vector@             | O(n)       | O(1)     |
+  +------------------------------+------------+----------+
+  | Sequence, @Seq@              | O(1)       | O(log n) |
+  +------------------------------+------------+----------+
+  | Skew binary list, @SkewList@ | O(1)       | O(log n) |
+  +------------------------------+------------+----------+
+  .
+  @SkewList@ improves upon ordinary list, the cons operation is still
+  constant time (though with higher constant factor), but indexing
+  can be done in a logarithmic time.
+  .
+  Binary list cons is slower, as it might need to walk over whole
+  /log n/ sized structure.
+  .
+  @Vector@ is the other end of trade-off spectrum: indexing is constant time
+  operation, but consing a new element will need to copy whole spine.
+  .
+  @Seq@ from "Data.Sequence" has similar (but amortized) complexity bounds for
+  cons and index as @SkewList@.  However (it seems) that indexing is quicker for
+  @SkewList@ in practice. Also @SkewList@ has strict spine.
+  On the other hand, @Seq@ has quick append if you need that.
+  .
+  If you need both: fast consing and index, consider using @SkewList@.
+
+homepage:           https://github.com/phadej/skew-list
+bug-reports:        https://github.com/phadej/skew-list/issues
+license:            BSD-3-Clause
+license-file:       LICENSE
+author:             Oleg Grenrus <oleg.grenrus@iki.fi>
+maintainer:         Oleg.Grenrus <oleg.grenrus@iki.fi>
+copyright:          (c) 2022 Oleg Grenrus
+build-type:         Simple
+extra-source-files: ChangeLog.md
+tested-with:
+  GHC ==8.6.5 || ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.5 || ==9.4.4
+
+source-repository head
+  type:     git
+  location: https://github.com/phadej/skew-list.git
+
+library
+  default-language: Haskell2010
+  hs-source-dirs:   src
+  ghc-options:      -Wall -fprint-explicit-kinds
+  exposed-modules:
+    Data.SkewList.Lazy
+    Data.SkewList.Strict
+
+  -- Internal modules
+  exposed-modules:
+    Data.SkewList.Lazy.Internal
+    Data.SkewList.Strict.Internal
+
+  other-modules:    TrustworthyCompat
+
+  -- GHC boot libs
+  build-depends:
+    , base     >=4.12.0.0 && <4.18
+    , deepseq  >=1.4.4.0  && <1.5
+
+  -- other dependencies
+  build-depends:
+    , hashable             ^>=1.4.1.0
+    , indexed-traversable  ^>=0.1.1
+    , QuickCheck           ^>=2.14.2
+    , strict               ^>=0.4.0.1
+
+  if impl(ghc >=9.0)
+    -- these flags may abort compilation with GHC-8.10
+    -- https://gitlab.haskell.org/ghc/ghc/-/merge_requests/3295
+    ghc-options: -Winferred-safe-imports -Wmissing-safe-haskell-mode
+
+test-suite skew-list-tests
+  type:             exitcode-stdio-1.0
+  main-is:          skew-list-tests.hs
+  other-modules:
+    Lazy
+    Strict
+
+  default-language: Haskell2010
+  hs-source-dirs:   tests
+  ghc-options:      -Wall
+  build-depends:
+    , base
+    , indexed-traversable
+    , QuickCheck           ^>=2.14.2
+    , skew-list
+    , tasty                ^>=1.4.2.3
+    , tasty-hunit          ^>=0.10.0.3
+    , tasty-quickcheck     ^>=0.10.2
+
+benchmark skew-list-bench
+  type:             exitcode-stdio-1.0
+  main-is:          skew-list-bench.hs
+  default-language: Haskell2010
+  hs-source-dirs:   bench
+  ghc-options:      -Wall
+  build-depends:
+    , base
+    , containers
+    , criterion   ^>=1.6.0.0
+    , ral         ^>=0.2.1
+    , skew-list
+    , vector      ^>=0.13.0.0
diff --git a/src/Data/SkewList/Lazy.hs b/src/Data/SkewList/Lazy.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SkewList/Lazy.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE Safe #-}
+
+-- | Skewed binary lists.
+--
+-- This module is intended to be imported qualified, to avoid name clashes with Prelude functions:
+--
+-- @
+-- import qualified Data.SkewList.Lazy as Skew
+-- @
+--
+module Data.SkewList.Lazy (
+    SkewList (Cons, Nil),
+    -- * Construction
+    empty,
+    singleton,
+    cons,
+    append,
+    -- * Indexing
+    (!),
+    (!?),
+    uncons,
+    length,
+    null,
+    -- * Conversions
+    toList,
+    fromList,
+    -- * Folding
+    foldMap,
+    foldMap',
+    foldr,
+    foldl',
+    -- ** Indexed
+    ifoldMap,
+    ifoldr,
+    -- * Mapping
+    adjust,
+    map,
+    -- ** Indexed
+    imap,
+    itraverse,
+    -- * Debug
+    valid,
+    explicitShow,
+    explicitShowsPrec,
+) where
+
+import Data.SkewList.Lazy.Internal
+import Prelude ()
diff --git a/src/Data/SkewList/Lazy/Internal.hs b/src/Data/SkewList/Lazy/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SkewList/Lazy/Internal.hs
@@ -0,0 +1,681 @@
+{-# LANGUAGE BangPatterns           #-}
+{-# LANGUAGE CPP                    #-}
+{-# LANGUAGE DeriveTraversable      #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE PatternSynonyms        #-}
+{-# LANGUAGE RoleAnnotations        #-}
+{-# LANGUAGE Safe                   #-}
+{-# LANGUAGE StandaloneDeriving     #-}
+{-# LANGUAGE TypeFamilies           #-}
+{-# LANGUAGE ViewPatterns           #-}
+
+-- Strict instance has to be orphan.
+-- (we cannot define strict and lazy versions in the same module).
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Data.SkewList.Lazy.Internal (
+    SkewList (Cons_, Cons, Nil), Tree (..),
+    -- * Construction
+    empty,
+    singleton,
+    cons,
+    append,
+    -- * Indexing
+    (!),
+    (!?),
+    uncons,
+    length,
+    null,
+    -- * Conversions
+    toList,
+    fromList,
+    -- * Folding
+    foldMap,
+    foldMap',
+    foldr,
+    foldl',
+    -- ** Indexed
+    ifoldMap,
+    ifoldr,
+    -- * Mapping
+    adjust,
+    map,
+    -- ** Indexed
+    imap,
+    itraverse,
+    -- * Debug
+    valid,
+    explicitShow,
+    explicitShowsPrec,
+) where
+
+import Prelude
+       (Bool (..), Eq ((==)), Functor (..), Int, Maybe (..), Num (..), Ord (..),
+       Show (..), ShowS, String, error, fromIntegral, otherwise, seq, showChar,
+       showParen, showString, ($), (&&), (.))
+
+import Control.Applicative (Applicative (..), (<$>))
+import Control.DeepSeq     (NFData (..))
+import Data.Bits           (popCount, unsafeShiftL, unsafeShiftR, (.|.))
+import Data.Hashable       (Hashable (..))
+import Data.Monoid         (Monoid (..))
+import Data.Semigroup      (Semigroup (..))
+import Data.Strict.Classes (Strict (..))
+import Data.Word           (Word)
+import GHC.Stack           (HasCallStack)
+
+import qualified Data.Foldable    as I (Foldable (..))
+import qualified Data.List        as L
+import qualified Data.Traversable as I (Traversable (..))
+import qualified Test.QuickCheck  as QC
+
+import qualified Data.Foldable.WithIndex    as WI (FoldableWithIndex (..))
+import qualified Data.Functor.WithIndex     as WI (FunctorWithIndex (..))
+import qualified Data.Traversable.WithIndex as WI (TraversableWithIndex (..))
+
+import qualified Data.SkewList.Strict.Internal as Strict
+import qualified TrustworthyCompat             as TC
+
+-- $setup
+-- >>> import Prelude (Int, ($), (<>), (==), Bool (..), error)
+-- >>> import Data.Char (toUpper)
+-- >>> import Data.Hashable (hash)
+
+-------------------------------------------------------------------------------
+-- Type
+-------------------------------------------------------------------------------
+
+-- | List with efficient random access.
+--
+-- Implemented using skewed binary.
+--
+-- Strict spine, lazy elements variant:
+--
+-- >>> length $ fromList [True, error "bar"]
+-- 2
+--
+data SkewList a
+    = Nil
+
+    -- | Internal constructor. If you use it, maintain invariants (see 'valid').
+    | Cons_
+        {-# UNPACK #-} !Word -- ^ size of the head tree
+        !(Tree a)
+        !(SkewList a)
+  deriving (Eq, Functor, I.Traversable)
+
+type role SkewList representational
+
+-- |
+-- This instance provides total ordering, but this ordering /is not lexicographic/.
+-- I.e. it is different order than on ordinary lists.
+deriving instance Ord a => Ord (SkewList a)
+
+-- | A complete binary tree (completeness not enforced)
+data Tree a
+    = Lf a
+    | Nd a !(Tree a) !(Tree a)
+  deriving (Eq, Ord, Show, Functor, I.Traversable)
+
+-------------------------------------------------------------------------------
+-- Validity
+-------------------------------------------------------------------------------
+
+-- | Check invariants.
+--
+-- * Trees are stored in increasing order.
+--
+-- * Only first two trees can have the same size.
+--
+-- * Tree sizes should be of form @2^n - 1@.
+--
+-- * Trees should be balanced.
+--
+valid :: SkewList a -> Bool
+valid Nil                            = True
+valid (Cons_ s  t Nil)               = validTree s t
+valid (Cons_ s1 t1 (Cons_ s2 t2 xs)) =
+    s1 <= s2 && validTree s1 t1 && validTree s2 t2 && valid' s2 xs
+
+valid' :: Word -> SkewList a -> Bool
+valid' _ Nil            = True
+valid' p (Cons_ s t xs) = p < s && validTree s t && valid' s xs
+
+validTree
+    :: Word
+    -> Tree a
+    -> Bool
+validTree size tree = popCount (size + 1) == 1 && go size tree
+  where
+    go 1 (Lf _)     = True
+    go _ (Lf _)     = False
+    go n (Nd _ l r) = go n' l && go n' r where n' = sizeDown n
+
+-------------------------------------------------------------------------------
+-- Size helpers
+-------------------------------------------------------------------------------
+
+sizeDown :: Word -> Word
+sizeDown n = unsafeShiftR n 1
+{-# INLINE sizeDown #-}
+
+-- | Double plus one. @sizeUp n = 2 * n + 1@.
+sizeUp :: Word -> Word
+sizeUp n = unsafeShiftL n 1 .|. 1
+
+-------------------------------------------------------------------------------
+-- Patterns
+-------------------------------------------------------------------------------
+
+-- | 'Cons' and 'Nil' form complete pattern match.
+pattern Cons :: a -> SkewList a -> SkewList a
+pattern Cons x xs <- (uncons -> Just (x, xs))
+  where Cons x xs = cons x xs
+
+{-# COMPLETE Cons, Nil #-}
+
+-------------------------------------------------------------------------------
+-- Instances
+-------------------------------------------------------------------------------
+
+instance I.Foldable SkewList where
+    foldMap = foldMap
+
+#if MIN_VERSION_base(4,13,0)
+    foldMap' = foldMap'
+#endif
+
+    foldr   = foldr
+    foldl'  = foldl'
+
+    length = length
+    null   = null
+
+    sum     = sum
+    product = product
+
+instance I.Foldable Tree where
+    foldMap = foldMapTree
+    foldr   = foldrTree
+    null _  = False
+
+instance NFData a => NFData (SkewList a) where
+    rnf Nil            = ()
+    rnf (Cons_ _ t xs) = rnf t `seq` rnf xs
+
+instance NFData a => NFData (Tree a) where
+    rnf (Lf a)     = rnf a
+    rnf (Nd x l r) = rnf x `seq` rnf l `seq` rnf r
+
+-- | The hash value are different then for an ordinary list:
+--
+-- >>> hash (fromList "foobar") == hash "foobar"
+-- False
+--
+-- >>> hash (fromList "foo", fromList "bar") == hash (fromList "foobar", fromList "")
+-- False
+--
+instance Hashable a => Hashable (SkewList a) where
+    hashWithSalt salt Nil            = salt
+        `hashWithSalt` (0 :: Int)
+    hashWithSalt salt (Cons_ s t xs) = salt
+        `hashWithSalt` s   -- s /= 1, acts as "constructor tag"
+        `hashWithSalt` t
+        `hashWithSalt` xs
+
+instance Hashable a => Hashable (Tree a) where
+    hashWithSalt = foldlTree' hashWithSalt
+
+-- |
+--
+-- >>> fromList "abc" <> fromList "xyz"
+-- "abcxyz"
+--
+instance Semigroup (SkewList a) where
+    (<>) = append
+
+instance Monoid (SkewList a) where
+    mempty  = empty
+    mappend = (<>)
+
+instance WI.FunctorWithIndex Int SkewList where
+    imap = imap
+
+instance WI.FoldableWithIndex Int SkewList where
+    ifoldMap = ifoldMap
+    ifoldr   = ifoldr
+
+instance WI.TraversableWithIndex Int SkewList where
+    itraverse = itraverse
+
+-------------------------------------------------------------------------------
+-- Showing
+-------------------------------------------------------------------------------
+
+instance Show a => Show (SkewList a) where
+    showsPrec d xs = showsPrec d (toList xs)
+
+explicitShow :: Show a => SkewList a -> String
+explicitShow xs = explicitShowsPrec 0 xs ""
+
+explicitShowsPrec :: Show a => Int -> SkewList a -> ShowS
+explicitShowsPrec _ Nil             = showString "Nil"
+explicitShowsPrec d (Cons_ s t Nil) = showParen (d > 10)
+    $ showString "Cons_ "
+    . showsPrec 11 s
+    . showChar ' '
+    . showsPrec 11 t
+    . showString " Nil"
+explicitShowsPrec d (Cons_ s t xs)  = showParen (d > 0)
+    $ showString "Cons_ "
+    . showsPrec 11 s
+    . showChar ' '
+    . showsPrec 11 t
+    . showString " $ "
+    . explicitShowsPrec 0 xs
+
+-------------------------------------------------------------------------------
+-- Construction
+-------------------------------------------------------------------------------
+
+-- | Empty 'SkewList'.
+--
+-- >>> empty :: SkewList Int
+-- []
+--
+empty :: SkewList a
+empty = Nil
+
+-- | Single element 'SkewList'.
+--
+-- >>> singleton True
+-- [True]
+--
+singleton :: a -> SkewList a
+singleton x = Cons_ 1 (Lf x) Nil
+
+-- |
+--
+-- >>> cons 'x' (fromList "foo")
+-- "xfoo"
+--
+cons :: a -> SkewList a -> SkewList a
+cons x (Cons_ s1 t1 (Cons_ s2 t2 xs)) | s1 == s2 = Cons_ (sizeUp s1) (Nd x t1 t2) xs
+cons x xs                                        = Cons_ 1           (Lf x)       xs
+
+-- |
+--
+-- >>> append (fromList "foo") (fromList "bar")
+-- "foobar"
+--
+append :: SkewList a -> SkewList a -> SkewList a
+-- append xs ys = foldr cons ys xs
+append Nil            ys = ys
+append (Cons_ s t xs) ys = appendTree s t (append xs ys)
+
+appendTree :: Word -> Tree a -> SkewList a -> SkewList a
+appendTree !_   (Lf x)     xs
+                = cons x xs
+appendTree s1 t@(Nd x l r) xs@(Cons_ s2 _ (Cons_ s3 _ _))
+    | s2 == s3  = cons x (appendTree s' l (appendTree s' r xs))
+    | s1 <= s2  = Cons_ s1 t xs
+    | otherwise = cons x (appendTree s' l (appendTree s' r xs))
+  where
+    s' = sizeDown s1
+appendTree s1 t@(Nd x l r) xs@(Cons_ s2 _ Nil)
+    | s1 <= s2  = Cons_ s1 t xs
+    | otherwise = cons x (appendTree s' l (appendTree s' r xs))
+  where
+    s' = sizeDown s1
+appendTree s1 t Nil
+                = Cons_ s1 t Nil
+
+-------------------------------------------------------------------------------
+-- Indexing
+-------------------------------------------------------------------------------
+
+infixl 9 !, !?
+
+-- | List index.
+--
+-- >>> fromList ['a'..'f'] ! 0
+-- 'a'
+--
+-- >>> fromList ['a'..'f'] ! 5
+-- 'f'
+--
+-- >>> fromList ['a'..'f'] ! 6
+-- *** Exception: SkewList.!
+-- CallStack (from HasCallStack):
+--   error...
+--   !, called at <interactive>...
+--
+(!) :: HasCallStack => SkewList a -> Int -> a
+(!) t i
+    | i < 0     = err
+    | otherwise = unsafeIndex err t (fromIntegral i)
+  where
+    err = error "SkewList.!"
+
+unsafeIndex :: a -> SkewList a -> Word -> a
+unsafeIndex d Nil            !_ = d
+unsafeIndex d (Cons_ s t xs) !i
+    | i < s     = unsafeIndexTree d s i t
+    | otherwise = unsafeIndex d xs (i - s)
+
+unsafeIndexTree
+    :: a       -- ^ default value
+    -> Word    -- ^ tree size
+    -> Word    -- ^ index
+    -> Tree a  -- ^ tree
+    -> a
+unsafeIndexTree _ !_ !0 (Lf x)       = x
+unsafeIndexTree d  _  _ (Lf _)       = d
+unsafeIndexTree _  _  0 (Nd x _ _)   = x
+unsafeIndexTree d  s  i (Nd _ t1 t2)
+    | i <= s'   = unsafeIndexTree d s' (i - 1)      t1
+    | otherwise = unsafeIndexTree d s' (i - 1 - s') t2
+  where
+    s' = sizeDown s
+
+-- | safe list index.
+--
+-- >>> fromList ['a'..'f'] !? 0
+-- Just 'a'
+--
+-- >>> fromList ['a'..'f'] !? 5
+-- Just 'f'
+--
+-- >>> fromList ['a'..'f'] !? 6
+-- Nothing
+--
+(!?) :: SkewList a -> Int -> Maybe a
+(!?) t i
+    | i < 0     = Nothing
+    | otherwise = safeIndex t (fromIntegral i)
+
+safeIndex :: SkewList a -> Word -> Maybe a
+safeIndex Nil            !_ = Nothing
+safeIndex (Cons_ s t xs) !i
+    | i < s     = safeIndexTree s i t
+    | otherwise = safeIndex xs (i - s)
+
+safeIndexTree
+    :: Word    -- ^ tree size
+    -> Word    -- ^ index
+    -> Tree a  -- ^ tree
+    -> Maybe a
+safeIndexTree !_ !0 (Lf x)       = Just x
+safeIndexTree  _  _ (Lf _)       = Nothing
+safeIndexTree  _  0 (Nd x _ _)   = Just x
+safeIndexTree  s  i (Nd _ t1 t2)
+    | i <= s'   = safeIndexTree s' (i - 1)      t1
+    | otherwise = safeIndexTree s' (i - 1 - s') t2
+  where
+    s' = sizeDown s
+
+-- | Inverse of 'cons'.
+--
+-- >>> uncons (fromList ['a'..'f'])
+-- Just ('a',"bcdef")
+--
+-- >>> uncons Nil
+-- Nothing
+--
+uncons :: SkewList a -> Maybe (a, SkewList a)
+uncons Nil                        = Nothing
+uncons (Cons_  _ (Lf x)       xs) = Just (x, xs)
+uncons (Cons_  s (Nd x t1 t2) xs) = Just (x, Cons_ s' t1 (Cons_ s' t2 xs)) where s' = sizeDown s
+
+-- | Length, /O(log n)/.
+length :: SkewList a -> Int
+length = go 0 where
+    go !n Nil            = n
+    go  n (Cons_ s _ xs) = let !n' = n + fromIntegral s in go n' xs
+
+-- | Is the list empty? /O(1)/.
+null :: SkewList a -> Bool
+null Nil           = True
+null (Cons_ _ _ _) = False
+
+-------------------------------------------------------------------------------
+-- Conversions
+-------------------------------------------------------------------------------
+
+instance TC.IsList (SkewList a) where
+    type Item (SkewList a) = a
+    toList   = toList
+    fromList = fromList
+
+-- | Convert 'SkewList' to ordinary list.
+toList :: SkewList a -> [a]
+toList Nil            = []
+toList (Cons_ _ t xs) = toListTree t (toList xs)
+
+toListTree :: Tree a -> [a] -> [a]
+toListTree (Lf x)       zs = x : zs
+toListTree (Nd x xs ys) zs = x : toListTree xs (toListTree ys zs)
+
+-- | Convert ordinary list to 'SkewList'.
+--
+-- >>> fromList ['a' .. 'f']
+-- "abcdef"
+--
+-- >>> explicitShow $ fromList ['a' .. 'f']
+-- "Cons_ 3 (Nd 'a' (Lf 'b') (Lf 'c')) $ Cons_ 3 (Nd 'd' (Lf 'e') (Lf 'f')) Nil"
+--
+-- >>> explicitShow $ fromList ['a' .. 'e']
+-- "Cons_ 1 (Lf 'a') $ Cons_ 1 (Lf 'b') $ Cons_ 3 (Nd 'c' (Lf 'd') (Lf 'e')) Nil"
+--
+fromList :: [a] -> SkewList a
+fromList = L.foldr cons empty
+
+-------------------------------------------------------------------------------
+-- Folding
+-------------------------------------------------------------------------------
+
+-- | 'I.foldMap'.
+foldMap :: Monoid m => (a -> m) -> SkewList a -> m
+foldMap _ Nil            = mempty
+foldMap f (Cons_ _ t xs) = foldMapTree f t <> foldMap f xs
+
+foldMapTree :: Semigroup m => (a -> m) -> Tree a -> m
+foldMapTree f (Lf x)     = f x
+foldMapTree f (Nd x l r) = f x <> foldMapTree f l <> foldMapTree f r
+
+-- | Strict 'foldMap'.
+foldMap' :: Monoid m => (a -> m) -> SkewList a -> m
+foldMap' _ Nil            = mempty
+foldMap' f (Cons_ _ t xs) =
+    a <> b
+  where
+    !a = foldMapTree' f t
+    !b = foldMap' f xs
+
+foldMapTree' :: Semigroup m => (a -> m) -> Tree a -> m
+foldMapTree' f (Lf x) = f x
+foldMapTree' f (Nd x l r) =
+    xl <> r'
+  where
+    !x' = f x
+    !l' = foldMapTree' f l
+    !r' = foldMapTree' f r
+    !xl = x' <> l'
+
+-- | Right fold.
+foldr :: (a -> b -> b) -> b -> SkewList a -> b
+foldr _ z Nil            = z
+foldr f z (Cons_ _ t xs) = foldrTree f (foldr f z xs) t
+
+foldrTree :: (a -> b -> b) -> b -> Tree a -> b
+foldrTree f z (Lf x) = f x z
+foldrTree f z (Nd x l r) = f x (foldrTree f (foldrTree f z r) l)
+
+-- | Strict left fold.
+foldl' :: (b -> a -> b) -> b -> SkewList a -> b
+foldl' _  z Nil           = z
+foldl' f z (Cons_ _ t xs) = foldl' f z' xs
+  where
+    !z' = foldlTree' f z t
+
+foldlTree' :: (b -> a -> b) -> b -> Tree a -> b
+foldlTree' f z (Lf x)     = f z x
+foldlTree' f z (Nd x l r) = foldlTree' f l' r
+  where
+    !x' = f z x
+    !l' = foldlTree' f x' l
+
+sum :: Num a => SkewList a -> a
+sum = foldl' (+) 0
+
+product :: Num a => SkewList a -> a
+product = foldl' (*) 1
+
+-------------------------------------------------------------------------------
+-- Indexed Folding
+-------------------------------------------------------------------------------
+
+-- | Indexed 'I.foldMap'.
+ifoldMap :: Monoid m => (Int -> a -> m) -> SkewList a -> m
+ifoldMap = ifoldMapOff 0
+
+ifoldMapOff :: Monoid m => Int -> (Int -> a -> m) -> SkewList a -> m
+ifoldMapOff _ _ Nil            = mempty
+ifoldMapOff o f (Cons_ s t xs) = ifoldMapTreeOff o s f t <> ifoldMapOff (o + fromIntegral s) f xs
+
+ifoldMapTreeOff :: Semigroup m => Int -> Word -> (Int -> a -> m) -> Tree a -> m
+ifoldMapTreeOff o _ f (Lf x)     = f o x
+ifoldMapTreeOff o s f (Nd x l r) = f o x <> ifoldMapTreeOff (o + 1) s' f l <> ifoldMapTreeOff (o + 1 + fromIntegral s') s' f r
+  where
+    s' = sizeDown s
+
+-- | Indexed right fold.
+ifoldr :: (Int -> a -> b -> b) -> b -> SkewList a -> b
+ifoldr = ifoldrOff 0
+
+ifoldrOff :: Int -> (Int -> a -> t -> t) -> t -> SkewList a -> t
+ifoldrOff _ _ z Nil            = z
+ifoldrOff o f z (Cons_ s t xs) = ifoldrTreeOff o s f (ifoldrOff (o + fromIntegral s) f z xs) t
+
+ifoldrTreeOff :: Int -> Word -> (Int -> a -> b -> b) -> b -> Tree a -> b
+ifoldrTreeOff o _ f z (Lf x) = f o x z
+ifoldrTreeOff o s f z (Nd x l r) = f o x (ifoldrTreeOff (o + 1) s' f (ifoldrTreeOff (o + 1 + fromIntegral s') s' f z r) l) where
+    s' = sizeDown s
+
+-------------------------------------------------------------------------------
+-- Mapping
+-------------------------------------------------------------------------------
+
+-- | Adjust a value in the list.
+--
+-- >>> adjust 3 toUpper $ fromList "bcdef"
+-- "bcdEf"
+--
+-- If index is out of bounds, the list is returned unmodified.
+--
+-- >>> adjust 10 toUpper $ fromList "bcdef"
+-- "bcdef"
+--
+-- >>> adjust (-1) toUpper $ fromList "bcdef"
+-- "bcdef"
+--
+adjust :: Int -> (a -> a) -> SkewList a -> SkewList a
+adjust i f xs
+    | i < 0     = xs
+    | otherwise = adjustOff (fromIntegral i) f xs
+
+adjustOff :: Word -> (a -> a) -> SkewList a -> SkewList a
+adjustOff _ _ Nil = Nil
+adjustOff i f (Cons_ s t xs)
+    | i < s     = Cons_ s (adjustOffTree i s f t) xs
+    | otherwise = Cons_ s t                       (adjustOff (i - s) f xs)
+
+adjustOffTree :: Word -> Word -> (a -> a) -> Tree a -> Tree a
+adjustOffTree 0 _ f   (Lf x)     = Lf (f x)
+adjustOffTree _ _ _ t@(Lf _)     = t
+adjustOffTree 0 _ f   (Nd x l r) = Nd (f x) l r
+adjustOffTree i s f   (Nd x l r)
+    | i <= s'   = Nd x (adjustOffTree (i - 1) s' f l) r
+    | otherwise = Nd x l                              (adjustOffTree (i - 1 - s') s' f r)
+  where
+    s' = sizeDown s
+
+-- | Map over elements.
+--
+-- >>> map toUpper (fromList ['a'..'f'])
+-- "ABCDEF"
+--
+map :: (a -> b) -> SkewList a -> SkewList b
+map = fmap
+
+-- | Indexed map.
+--
+-- >>> imap (,) $ fromList ['a' .. 'f']
+-- [(0,'a'),(1,'b'),(2,'c'),(3,'d'),(4,'e'),(5,'f')]
+--
+imap :: (Int -> a -> b) -> SkewList a -> SkewList b
+imap = imapOff 0
+
+imapOff :: Int -> (Int -> a -> b) -> SkewList a -> SkewList b
+imapOff _ _ Nil            = Nil
+imapOff o f (Cons_ s t xs) = Cons_ s (imapTreeOff o s f t) (imapOff (o + fromIntegral s) f xs)
+
+imapTreeOff :: Int -> Word -> (Int -> a -> b) -> Tree a -> Tree b
+imapTreeOff o _ f (Lf x)     = Lf (f o x)
+imapTreeOff o s f (Nd x l r) = Nd (f o x)
+    (imapTreeOff (o + 1)                   s' f l)
+    (imapTreeOff (o + 1 + fromIntegral s') s' f r)
+  where
+    s' = sizeDown s
+
+-- | Indexed 'I.traverse'.
+itraverse :: Applicative f => (Int -> a -> f b) -> SkewList a -> f (SkewList b)
+itraverse = itraverseOff 0
+
+itraverseOff :: Applicative f => Int -> (Int -> a -> f b) -> SkewList a -> f (SkewList b)
+itraverseOff _ _ Nil            = pure Nil
+itraverseOff o f (Cons_ s t xs) = Cons_ s <$> itraverseTreeOff o s f t <*> itraverseOff (o + fromIntegral s) f xs
+
+itraverseTreeOff :: Applicative f => Int -> Word -> (Int -> a -> f b) -> Tree a -> f (Tree b)
+itraverseTreeOff o _ f (Lf x)     = Lf <$> f o x
+itraverseTreeOff o s f (Nd x l r) = Nd <$> f o x
+    <*> itraverseTreeOff (o + 1)                   s' f l
+    <*> itraverseTreeOff (o + 1 + fromIntegral s') s' f r
+  where
+    s' = sizeDown s
+
+-------------------------------------------------------------------------------
+-- QuickCheck
+-------------------------------------------------------------------------------
+
+instance QC.Arbitrary1 SkewList where
+    liftArbitrary = fmap fromList . QC.liftArbitrary
+    liftShrink shr = fmap fromList . QC.liftShrink shr . toList
+
+instance QC.Arbitrary a => QC.Arbitrary (SkewList a) where
+    arbitrary = QC.arbitrary1
+    shrink    = QC.shrink1
+
+instance QC.CoArbitrary a => QC.CoArbitrary (SkewList a) where
+    coarbitrary = QC.coarbitrary . toList
+
+instance QC.Function a => QC.Function (SkewList a) where
+    function = QC.functionMap toList fromList
+
+-------------------------------------------------------------------------------
+-- Strict
+-------------------------------------------------------------------------------
+
+instance Strict (SkewList a) (Strict.SkewList a) where
+    toLazy Strict.Nil            = Nil
+    toLazy (Strict.Cons_ s t xs) = Cons_ s (toLazy t) (toLazy xs)
+
+    toStrict Nil            = Strict.Nil
+    toStrict (Cons_ s t xs) = Strict.Cons_ s (toStrict t) (toStrict xs)
+
+instance Strict (Tree a) (Strict.Tree a) where
+    toLazy (Strict.Lf x)     = Lf x
+    toLazy (Strict.Nd x l r) = Nd x (toLazy l) (toLazy r)
+
+    toStrict (Lf x)     = Strict.Lf x
+    toStrict (Nd x l r) = Strict.Nd x (toStrict l) (toStrict r)
diff --git a/src/Data/SkewList/Strict.hs b/src/Data/SkewList/Strict.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SkewList/Strict.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE Safe #-}
+
+-- | Skewed binary lists.
+--
+-- This module is intended to be imported qualified, to avoid name clashes with Prelude functions:
+--
+-- @
+-- import qualified Data.SkewList.Strict as Skew
+-- @
+--
+module Data.SkewList.Strict (
+    SkewList (Cons, Nil),
+    -- * Construction
+    empty,
+    singleton,
+    cons,
+    append,
+    -- * Indexing
+    (!),
+    (!?),
+    uncons,
+    length,
+    null,
+    -- * Conversions
+    toList,
+    fromList,
+    -- * Folding
+    foldMap,
+    foldMap',
+    foldr,
+    foldl',
+    -- ** Indexed
+    ifoldMap,
+    ifoldr,
+    -- * Mapping
+    adjust,
+    map,
+    -- ** Indexed
+    imap,
+    itraverse,
+    -- * Debug
+    valid,
+    explicitShow,
+    explicitShowsPrec,
+) where
+
+import Data.SkewList.Strict.Internal
+import Prelude ()
diff --git a/src/Data/SkewList/Strict/Internal.hs b/src/Data/SkewList/Strict/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SkewList/Strict/Internal.hs
@@ -0,0 +1,658 @@
+{-# LANGUAGE BangPatterns           #-}
+{-# LANGUAGE CPP                    #-}
+{-# LANGUAGE DeriveTraversable      #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE PatternSynonyms        #-}
+{-# LANGUAGE RoleAnnotations        #-}
+{-# LANGUAGE Safe                   #-}
+{-# LANGUAGE StandaloneDeriving     #-}
+{-# LANGUAGE TypeFamilies           #-}
+{-# LANGUAGE ViewPatterns           #-}
+module Data.SkewList.Strict.Internal (
+    SkewList (Cons_, Cons, Nil), Tree (..),
+    -- * Construction
+    empty,
+    singleton,
+    cons,
+    append,
+    -- * Indexing
+    (!),
+    (!?),
+    uncons,
+    length,
+    null,
+    -- * Conversions
+    toList,
+    fromList,
+    -- * Folding
+    foldMap,
+    foldMap',
+    foldr,
+    foldl',
+    -- ** Indexed
+    ifoldMap,
+    ifoldr,
+    -- * Mapping
+    adjust,
+    map,
+    -- ** Indexed
+    imap,
+    itraverse,
+    -- * Debug
+    valid,
+    explicitShow,
+    explicitShowsPrec,
+) where
+
+import Prelude
+       (Bool (..), Eq ((==)), Functor (..), Int, Maybe (..), Num (..), Ord (..),
+       Show (..), ShowS, String, error, fromIntegral, otherwise, seq, showChar,
+       showParen, showString, ($), (&&), (.))
+
+import Control.Applicative (Applicative (..), (<$>))
+import Control.DeepSeq     (NFData (..))
+import Data.Bits           (popCount, unsafeShiftL, unsafeShiftR, (.|.))
+import Data.Hashable       (Hashable (..))
+import Data.Monoid         (Monoid (..))
+import Data.Semigroup      (Semigroup (..))
+import Data.Word           (Word)
+import GHC.Stack           (HasCallStack)
+
+import qualified Data.Foldable    as I (Foldable (..))
+import qualified Data.List        as L
+import qualified Data.Traversable as I (Traversable (..))
+import qualified Test.QuickCheck  as QC
+
+import qualified Data.Foldable.WithIndex    as WI (FoldableWithIndex (..))
+import qualified Data.Functor.WithIndex     as WI (FunctorWithIndex (..))
+import qualified Data.Traversable.WithIndex as WI (TraversableWithIndex (..))
+
+import qualified TrustworthyCompat as TC
+
+-- $setup
+-- >>> import Prelude (Int, ($), (<>), (==), Bool (..), error)
+-- >>> import Data.Char (toUpper)
+-- >>> import Data.Hashable (hash)
+
+-------------------------------------------------------------------------------
+-- Type
+-------------------------------------------------------------------------------
+
+-- | List with efficient random access.
+--
+-- Implemented using skewed binary.
+--
+-- Strict spine, strict elements variant:
+--
+-- >>> length $ fromList [True, error "bar"]
+-- *** Exception: bar
+-- ...
+--
+data SkewList a
+    = Nil
+
+    -- | Internal constructor. If you use it, maintain invariants (see 'valid').
+    | Cons_
+        {-# UNPACK #-} !Word -- ^ size of the head tree
+        !(Tree a)
+        !(SkewList a)
+  deriving (Eq, Functor, I.Traversable)
+
+type role SkewList representational
+
+-- |
+-- This instance provides total ordering, but this ordering /is not lexicographic/.
+-- I.e. it is different order than on ordinary lists.
+deriving instance Ord a => Ord (SkewList a)
+
+-- | A complete binary tree (completeness not enforced)
+data Tree a
+    = Lf !a
+    | Nd !a !(Tree a) !(Tree a)
+  deriving (Eq, Ord, Show, Functor, I.Traversable)
+
+-------------------------------------------------------------------------------
+-- Validity
+-------------------------------------------------------------------------------
+
+-- | Check invariants.
+--
+-- * Trees are stored in increasing order.
+--
+-- * Only first two trees can have the same size.
+--
+-- * Tree sizes should be of form @2^n - 1@.
+--
+-- * Trees should be balanced.
+--
+valid :: SkewList a -> Bool
+valid Nil                            = True
+valid (Cons_ s  t Nil)               = validTree s t
+valid (Cons_ s1 t1 (Cons_ s2 t2 xs)) =
+    s1 <= s2 && validTree s1 t1 && validTree s2 t2 && valid' s2 xs
+
+valid' :: Word -> SkewList a -> Bool
+valid' _ Nil            = True
+valid' p (Cons_ s t xs) = p < s && validTree s t && valid' s xs
+
+validTree
+    :: Word
+    -> Tree a
+    -> Bool
+validTree size tree = popCount (size + 1) == 1 && go size tree
+  where
+    go 1 (Lf _)     = True
+    go _ (Lf _)     = False
+    go n (Nd _ l r) = go n' l && go n' r where n' = sizeDown n
+
+-------------------------------------------------------------------------------
+-- Size helpers
+-------------------------------------------------------------------------------
+
+sizeDown :: Word -> Word
+sizeDown n = unsafeShiftR n 1
+{-# INLINE sizeDown #-}
+
+-- | Double plus one. @sizeUp n = 2 * n + 1@.
+sizeUp :: Word -> Word
+sizeUp n = unsafeShiftL n 1 .|. 1
+
+-------------------------------------------------------------------------------
+-- Patterns
+-------------------------------------------------------------------------------
+
+-- | 'Cons' and 'Nil' form complete pattern match.
+pattern Cons :: a -> SkewList a -> SkewList a
+pattern Cons x xs <- (uncons -> Just (x, xs))
+  where Cons x xs = cons x xs
+
+{-# COMPLETE Cons, Nil #-}
+
+-------------------------------------------------------------------------------
+-- Instances
+-------------------------------------------------------------------------------
+
+instance I.Foldable SkewList where
+    foldMap = foldMap
+
+#if MIN_VERSION_base(4,13,0)
+    foldMap' = foldMap'
+#endif
+
+    foldr   = foldr
+    foldl'  = foldl'
+
+    length = length
+    null   = null
+
+    sum     = sum
+    product = product
+
+instance I.Foldable Tree where
+    foldMap = foldMapTree
+    foldr   = foldrTree
+    null _  = False
+
+instance NFData a => NFData (SkewList a) where
+    rnf Nil            = ()
+    rnf (Cons_ _ t xs) = rnf t `seq` rnf xs
+
+instance NFData a => NFData (Tree a) where
+    rnf (Lf a)     = rnf a
+    rnf (Nd x l r) = rnf x `seq` rnf l `seq` rnf r
+
+-- | The hash value are different then for an ordinary list:
+--
+-- >>> hash (fromList "foobar") == hash "foobar"
+-- False
+--
+-- >>> hash (fromList "foo", fromList "bar") == hash (fromList "foobar", fromList "")
+-- False
+--
+instance Hashable a => Hashable (SkewList a) where
+    hashWithSalt salt Nil            = salt
+        `hashWithSalt` (0 :: Int)
+    hashWithSalt salt (Cons_ s t xs) = salt
+        `hashWithSalt` s   -- s /= 1, acts as "constructor tag"
+        `hashWithSalt` t
+        `hashWithSalt` xs
+
+instance Hashable a => Hashable (Tree a) where
+    hashWithSalt = foldlTree' hashWithSalt
+
+-- |
+--
+-- >>> fromList "abc" <> fromList "xyz"
+-- "abcxyz"
+--
+instance Semigroup (SkewList a) where
+    (<>) = append
+
+instance Monoid (SkewList a) where
+    mempty  = empty
+    mappend = (<>)
+
+instance WI.FunctorWithIndex Int SkewList where
+    imap = imap
+
+instance WI.FoldableWithIndex Int SkewList where
+    ifoldMap = ifoldMap
+    ifoldr   = ifoldr
+
+instance WI.TraversableWithIndex Int SkewList where
+    itraverse = itraverse
+
+-------------------------------------------------------------------------------
+-- Showing
+-------------------------------------------------------------------------------
+
+instance Show a => Show (SkewList a) where
+    showsPrec d xs = showsPrec d (toList xs)
+
+explicitShow :: Show a => SkewList a -> String
+explicitShow xs = explicitShowsPrec 0 xs ""
+
+explicitShowsPrec :: Show a => Int -> SkewList a -> ShowS
+explicitShowsPrec _ Nil             = showString "Nil"
+explicitShowsPrec d (Cons_ s t Nil) = showParen (d > 10)
+    $ showString "Cons_ "
+    . showsPrec 11 s
+    . showChar ' '
+    . showsPrec 11 t
+    . showString " Nil"
+explicitShowsPrec d (Cons_ s t xs)  = showParen (d > 0)
+    $ showString "Cons_ "
+    . showsPrec 11 s
+    . showChar ' '
+    . showsPrec 11 t
+    . showString " $ "
+    . explicitShowsPrec 0 xs
+
+-------------------------------------------------------------------------------
+-- Construction
+-------------------------------------------------------------------------------
+
+-- | Empty 'SkewList'.
+--
+-- >>> empty :: SkewList Int
+-- []
+--
+empty :: SkewList a
+empty = Nil
+
+-- | Single element 'SkewList'.
+--
+-- >>> singleton True
+-- [True]
+--
+singleton :: a -> SkewList a
+singleton x = Cons_ 1 (Lf x) Nil
+
+-- |
+--
+-- >>> cons 'x' (fromList "foo")
+-- "xfoo"
+--
+cons :: a -> SkewList a -> SkewList a
+cons x (Cons_ s1 t1 (Cons_ s2 t2 xs)) | s1 == s2 = Cons_ (sizeUp s1) (Nd x t1 t2) xs
+cons x xs                                        = Cons_ 1           (Lf x)       xs
+
+-- |
+--
+-- >>> append (fromList "foo") (fromList "bar")
+-- "foobar"
+--
+append :: SkewList a -> SkewList a -> SkewList a
+-- append xs ys = foldr cons ys xs
+append Nil            ys = ys
+append (Cons_ s t xs) ys = appendTree s t (append xs ys)
+
+appendTree :: Word -> Tree a -> SkewList a -> SkewList a
+appendTree !_   (Lf x)     xs
+                = cons x xs
+appendTree s1 t@(Nd x l r) xs@(Cons_ s2 _ (Cons_ s3 _ _))
+    | s2 == s3  = cons x (appendTree s' l (appendTree s' r xs))
+    | s1 <= s2  = Cons_ s1 t xs
+    | otherwise = cons x (appendTree s' l (appendTree s' r xs))
+  where
+    s' = sizeDown s1
+appendTree s1 t@(Nd x l r) xs@(Cons_ s2 _ Nil)
+    | s1 <= s2  = Cons_ s1 t xs
+    | otherwise = cons x (appendTree s' l (appendTree s' r xs))
+  where
+    s' = sizeDown s1
+appendTree s1 t Nil
+                = Cons_ s1 t Nil
+
+-------------------------------------------------------------------------------
+-- Indexing
+-------------------------------------------------------------------------------
+
+infixl 9 !, !?
+
+-- | List index.
+--
+-- >>> fromList ['a'..'f'] ! 0
+-- 'a'
+--
+-- >>> fromList ['a'..'f'] ! 5
+-- 'f'
+--
+-- >>> fromList ['a'..'f'] ! 6
+-- *** Exception: SkewList.!
+-- CallStack (from HasCallStack):
+--   error...
+--   !, called at <interactive>...
+--
+(!) :: HasCallStack => SkewList a -> Int -> a
+(!) t i
+    | i < 0     = err
+    | otherwise = unsafeIndex err t (fromIntegral i)
+  where
+    err = error "SkewList.!"
+
+unsafeIndex :: a -> SkewList a -> Word -> a
+unsafeIndex d Nil            !_ = d
+unsafeIndex d (Cons_ s t xs) !i
+    | i < s     = unsafeIndexTree d s i t
+    | otherwise = unsafeIndex d xs (i - s)
+
+unsafeIndexTree
+    :: a       -- ^ default value
+    -> Word    -- ^ tree size
+    -> Word    -- ^ index
+    -> Tree a  -- ^ tree
+    -> a
+unsafeIndexTree _ !_ !0 (Lf x)       = x
+unsafeIndexTree d  _  _ (Lf _)       = d
+unsafeIndexTree _  _  0 (Nd x _ _)   = x
+unsafeIndexTree d  s  i (Nd _ t1 t2)
+    | i <= s'   = unsafeIndexTree d s' (i - 1)      t1
+    | otherwise = unsafeIndexTree d s' (i - 1 - s') t2
+  where
+    s' = sizeDown s
+
+-- | safe list index.
+--
+-- >>> fromList ['a'..'f'] !? 0
+-- Just 'a'
+--
+-- >>> fromList ['a'..'f'] !? 5
+-- Just 'f'
+--
+-- >>> fromList ['a'..'f'] !? 6
+-- Nothing
+--
+(!?) :: SkewList a -> Int -> Maybe a
+(!?) t i
+    | i < 0     = Nothing
+    | otherwise = safeIndex t (fromIntegral i)
+
+safeIndex :: SkewList a -> Word -> Maybe a
+safeIndex Nil            !_ = Nothing
+safeIndex (Cons_ s t xs) !i
+    | i < s     = safeIndexTree s i t
+    | otherwise = safeIndex xs (i - s)
+
+safeIndexTree
+    :: Word    -- ^ tree size
+    -> Word    -- ^ index
+    -> Tree a  -- ^ tree
+    -> Maybe a
+safeIndexTree !_ !0 (Lf x)       = Just x
+safeIndexTree  _  _ (Lf _)       = Nothing
+safeIndexTree  _  0 (Nd x _ _)   = Just x
+safeIndexTree  s  i (Nd _ t1 t2)
+    | i <= s'   = safeIndexTree s' (i - 1)      t1
+    | otherwise = safeIndexTree s' (i - 1 - s') t2
+  where
+    s' = sizeDown s
+
+-- | Inverse of 'cons'.
+--
+-- >>> uncons (fromList ['a'..'f'])
+-- Just ('a',"bcdef")
+--
+-- >>> uncons Nil
+-- Nothing
+--
+uncons :: SkewList a -> Maybe (a, SkewList a)
+uncons Nil                        = Nothing
+uncons (Cons_  _ (Lf x)       xs) = Just (x, xs)
+uncons (Cons_  s (Nd x t1 t2) xs) = Just (x, Cons_ s' t1 (Cons_ s' t2 xs)) where s' = sizeDown s
+
+-- | Length, /O(log n)/.
+length :: SkewList a -> Int
+length = go 0 where
+    go !n Nil            = n
+    go  n (Cons_ s _ xs) = let !n' = n + fromIntegral s in go n' xs
+
+-- | Is the list empty? /O(1)/.
+null :: SkewList a -> Bool
+null Nil           = True
+null (Cons_ _ _ _) = False
+
+-------------------------------------------------------------------------------
+-- Conversions
+-------------------------------------------------------------------------------
+
+instance TC.IsList (SkewList a) where
+    type Item (SkewList a) = a
+    toList   = toList
+    fromList = fromList
+
+-- | Convert 'SkewList' to ordinary list.
+toList :: SkewList a -> [a]
+toList Nil            = []
+toList (Cons_ _ t xs) = toListTree t (toList xs)
+
+toListTree :: Tree a -> [a] -> [a]
+toListTree (Lf x)       zs = x : zs
+toListTree (Nd x xs ys) zs = x : toListTree xs (toListTree ys zs)
+
+-- | Convert ordinary list to 'SkewList'.
+--
+-- >>> fromList ['a' .. 'f']
+-- "abcdef"
+--
+-- >>> explicitShow $ fromList ['a' .. 'f']
+-- "Cons_ 3 (Nd 'a' (Lf 'b') (Lf 'c')) $ Cons_ 3 (Nd 'd' (Lf 'e') (Lf 'f')) Nil"
+--
+-- >>> explicitShow $ fromList ['a' .. 'e']
+-- "Cons_ 1 (Lf 'a') $ Cons_ 1 (Lf 'b') $ Cons_ 3 (Nd 'c' (Lf 'd') (Lf 'e')) Nil"
+--
+fromList :: [a] -> SkewList a
+fromList = L.foldr cons empty
+
+-------------------------------------------------------------------------------
+-- Folding
+-------------------------------------------------------------------------------
+
+-- | 'I.foldMap'.
+foldMap :: Monoid m => (a -> m) -> SkewList a -> m
+foldMap _ Nil            = mempty
+foldMap f (Cons_ _ t xs) = foldMapTree f t <> foldMap f xs
+
+foldMapTree :: Semigroup m => (a -> m) -> Tree a -> m
+foldMapTree f (Lf x)     = f x
+foldMapTree f (Nd x l r) = f x <> foldMapTree f l <> foldMapTree f r
+
+-- | Strict 'foldMap'.
+foldMap' :: Monoid m => (a -> m) -> SkewList a -> m
+foldMap' _ Nil            = mempty
+foldMap' f (Cons_ _ t xs) =
+    a <> b
+  where
+    !a = foldMapTree' f t
+    !b = foldMap' f xs
+
+foldMapTree' :: Semigroup m => (a -> m) -> Tree a -> m
+foldMapTree' f (Lf x) = f x
+foldMapTree' f (Nd x l r) =
+    xl <> r'
+  where
+    !x' = f x
+    !l' = foldMapTree' f l
+    !r' = foldMapTree' f r
+    !xl = x' <> l'
+
+-- | Right fold.
+foldr :: (a -> b -> b) -> b -> SkewList a -> b
+foldr _ z Nil            = z
+foldr f z (Cons_ _ t xs) = foldrTree f (foldr f z xs) t
+
+foldrTree :: (a -> b -> b) -> b -> Tree a -> b
+foldrTree f z (Lf x) = f x z
+foldrTree f z (Nd x l r) = f x (foldrTree f (foldrTree f z r) l)
+
+-- | Strict left fold.
+foldl' :: (b -> a -> b) -> b -> SkewList a -> b
+foldl' _  z Nil           = z
+foldl' f z (Cons_ _ t xs) = foldl' f z' xs
+  where
+    !z' = foldlTree' f z t
+
+foldlTree' :: (b -> a -> b) -> b -> Tree a -> b
+foldlTree' f z (Lf x)     = f z x
+foldlTree' f z (Nd x l r) = foldlTree' f l' r
+  where
+    !x' = f z x
+    !l' = foldlTree' f x' l
+
+sum :: Num a => SkewList a -> a
+sum = foldl' (+) 0
+
+product :: Num a => SkewList a -> a
+product = foldl' (*) 1
+
+-------------------------------------------------------------------------------
+-- Indexed Folding
+-------------------------------------------------------------------------------
+
+-- | Indexed 'I.foldMap'.
+ifoldMap :: Monoid m => (Int -> a -> m) -> SkewList a -> m
+ifoldMap = ifoldMapOff 0
+
+ifoldMapOff :: Monoid m => Int -> (Int -> a -> m) -> SkewList a -> m
+ifoldMapOff _ _ Nil            = mempty
+ifoldMapOff o f (Cons_ s t xs) = ifoldMapTreeOff o s f t <> ifoldMapOff (o + fromIntegral s) f xs
+
+ifoldMapTreeOff :: Semigroup m => Int -> Word -> (Int -> a -> m) -> Tree a -> m
+ifoldMapTreeOff o _ f (Lf x)     = f o x
+ifoldMapTreeOff o s f (Nd x l r) = f o x <> ifoldMapTreeOff (o + 1) s' f l <> ifoldMapTreeOff (o + 1 + fromIntegral s') s' f r
+  where
+    s' = sizeDown s
+
+-- | Indexed right fold.
+ifoldr :: (Int -> a -> b -> b) -> b -> SkewList a -> b
+ifoldr = ifoldrOff 0
+
+ifoldrOff :: Int -> (Int -> a -> t -> t) -> t -> SkewList a -> t
+ifoldrOff _ _ z Nil            = z
+ifoldrOff o f z (Cons_ s t xs) = ifoldrTreeOff o s f (ifoldrOff (o + fromIntegral s) f z xs) t
+
+ifoldrTreeOff :: Int -> Word -> (Int -> a -> b -> b) -> b -> Tree a -> b
+ifoldrTreeOff o _ f z (Lf x) = f o x z
+ifoldrTreeOff o s f z (Nd x l r) = f o x (ifoldrTreeOff (o + 1) s' f (ifoldrTreeOff (o + 1 + fromIntegral s') s' f z r) l) where
+    s' = sizeDown s
+
+-------------------------------------------------------------------------------
+-- Mapping
+-------------------------------------------------------------------------------
+
+-- | Adjust a value in the list.
+--
+-- >>> adjust 3 toUpper $ fromList "bcdef"
+-- "bcdEf"
+--
+-- If index is out of bounds, the list is returned unmodified.
+--
+-- >>> adjust 10 toUpper $ fromList "bcdef"
+-- "bcdef"
+--
+-- >>> adjust (-1) toUpper $ fromList "bcdef"
+-- "bcdef"
+--
+adjust :: Int -> (a -> a) -> SkewList a -> SkewList a
+adjust i f xs
+    | i < 0     = xs
+    | otherwise = adjustOff (fromIntegral i) f xs
+
+adjustOff :: Word -> (a -> a) -> SkewList a -> SkewList a
+adjustOff _ _ Nil = Nil
+adjustOff i f (Cons_ s t xs)
+    | i < s     = Cons_ s (adjustOffTree i s f t) xs
+    | otherwise = Cons_ s t                       (adjustOff (i - s) f xs)
+
+adjustOffTree :: Word -> Word -> (a -> a) -> Tree a -> Tree a
+adjustOffTree 0 _ f   (Lf x)     = Lf (f x)
+adjustOffTree _ _ _ t@(Lf _)     = t
+adjustOffTree 0 _ f   (Nd x l r) = Nd (f x) l r
+adjustOffTree i s f   (Nd x l r)
+    | i <= s'   = Nd x (adjustOffTree (i - 1) s' f l) r
+    | otherwise = Nd x l                              (adjustOffTree (i - 1 - s') s' f r)
+  where
+    s' = sizeDown s
+
+-- | Map over elements.
+--
+-- >>> map toUpper (fromList ['a'..'f'])
+-- "ABCDEF"
+--
+map :: (a -> b) -> SkewList a -> SkewList b
+map = fmap
+
+-- | Indexed map.
+--
+-- >>> imap (,) $ fromList ['a' .. 'f']
+-- [(0,'a'),(1,'b'),(2,'c'),(3,'d'),(4,'e'),(5,'f')]
+--
+imap :: (Int -> a -> b) -> SkewList a -> SkewList b
+imap = imapOff 0
+
+imapOff :: Int -> (Int -> a -> b) -> SkewList a -> SkewList b
+imapOff _ _ Nil            = Nil
+imapOff o f (Cons_ s t xs) = Cons_ s (imapTreeOff o s f t) (imapOff (o + fromIntegral s) f xs)
+
+imapTreeOff :: Int -> Word -> (Int -> a -> b) -> Tree a -> Tree b
+imapTreeOff o _ f (Lf x)     = Lf (f o x)
+imapTreeOff o s f (Nd x l r) = Nd (f o x)
+    (imapTreeOff (o + 1)                   s' f l)
+    (imapTreeOff (o + 1 + fromIntegral s') s' f r)
+  where
+    s' = sizeDown s
+
+-- | Indexed 'I.traverse'.
+itraverse :: Applicative f => (Int -> a -> f b) -> SkewList a -> f (SkewList b)
+itraverse = itraverseOff 0
+
+itraverseOff :: Applicative f => Int -> (Int -> a -> f b) -> SkewList a -> f (SkewList b)
+itraverseOff _ _ Nil            = pure Nil
+itraverseOff o f (Cons_ s t xs) = Cons_ s <$> itraverseTreeOff o s f t <*> itraverseOff (o + fromIntegral s) f xs
+
+itraverseTreeOff :: Applicative f => Int -> Word -> (Int -> a -> f b) -> Tree a -> f (Tree b)
+itraverseTreeOff o _ f (Lf x)     = Lf <$> f o x
+itraverseTreeOff o s f (Nd x l r) = Nd <$> f o x
+    <*> itraverseTreeOff (o + 1)                   s' f l
+    <*> itraverseTreeOff (o + 1 + fromIntegral s') s' f r
+  where
+    s' = sizeDown s
+
+-------------------------------------------------------------------------------
+-- QuickCheck
+-------------------------------------------------------------------------------
+
+instance QC.Arbitrary1 SkewList where
+    liftArbitrary = fmap fromList . QC.liftArbitrary
+    liftShrink shr = fmap fromList . QC.liftShrink shr . toList
+
+instance QC.Arbitrary a => QC.Arbitrary (SkewList a) where
+    arbitrary = QC.arbitrary1
+    shrink    = QC.shrink1
+
+instance QC.CoArbitrary a => QC.CoArbitrary (SkewList a) where
+    coarbitrary = QC.coarbitrary . toList
+
+instance QC.Function a => QC.Function (SkewList a) where
+    function = QC.functionMap toList fromList
+
diff --git a/src/TrustworthyCompat.hs b/src/TrustworthyCompat.hs
new file mode 100644
--- /dev/null
+++ b/src/TrustworthyCompat.hs
@@ -0,0 +1,6 @@
+{-# LANGUAGE Trustworthy #-}
+module TrustworthyCompat (
+    IsList (..),
+) where
+
+import GHC.Exts (IsList (..))
diff --git a/tests/Lazy.hs b/tests/Lazy.hs
new file mode 100644
--- /dev/null
+++ b/tests/Lazy.hs
@@ -0,0 +1,156 @@
+module Lazy (tests) where
+
+import Test.QuickCheck
+       (Arbitrary (..), Fun, Gen, Property, applyFun, chooseInt, elements,
+       label, oneof, property, sized, vector, (.&&.), (===))
+import Test.QuickCheck.Poly  (A, B)
+import Test.Tasty            (TestTree, testGroup)
+import Test.Tasty.QuickCheck (testProperty)
+
+import qualified Data.Foldable.WithIndex as WI
+import qualified Data.Functor.WithIndex  as WI
+import qualified Data.List               as L
+import qualified Data.SkewList.Lazy      as S
+
+tests :: TestTree
+tests = testGroup "Strict"
+    [ testProperty "valid" valid_prop
+    , testProperty "fromList . toList" $ \xs ->
+        xs === S.fromList (S.toList (xs :: S.SkewList Int))
+    , testProperty "toList . fromList" $ \xs ->
+        xs === S.toList (S.fromList (xs :: [Int]))
+
+    , testProperty "uncons" $ \xs ->
+        L.uncons xs === fmap (fmap S.toList) (S.uncons (S.fromList (xs :: [Int])))
+    , testProperty "length" $ \xs ->
+        L.length xs === S.length (S.fromList (xs :: [A]))
+    , testProperty "null" $ \xs ->
+        L.null xs === S.null (S.fromList (xs :: [A]))
+
+    , testProperty "eq" eq_prop
+    , testProperty "compare" compare_prop
+
+    , testProperty "map"  map_prop
+    , testProperty "imap" imap_prop
+    , testProperty "append" append_prop
+    , testProperty "append" append_prop_valid
+
+    , testProperty "foldr" foldr_prop
+    , testProperty "foldMap" foldMap_prop
+    , testProperty "ifoldr" ifoldr_prop
+    , testProperty "ifoldMap" ifoldMap_prop
+
+    , testProperty "model" model_prop
+    ]
+
+data SmallA = A0 | A1 | A2 deriving (Eq, Ord, Show)
+
+instance Arbitrary SmallA where
+    arbitrary = elements [A0,A1,A2]
+
+valid_prop :: S.SkewList A -> Property
+valid_prop xs = property (S.valid xs)
+
+eq_prop :: [SmallA] -> [SmallA] -> Property
+eq_prop xs ys = label (show (xs == ys)) $
+    (xs == ys) === (S.fromList xs == S.fromList ys)
+
+compare_prop :: [SmallA] -> [SmallA] -> [SmallA] -> Property
+compare_prop xs ys zs = label (show (compare xs' ys', compare ys' zs')) $ trans
+    (compare xs' ys')
+    (compare ys' zs')
+    (compare xs' zs')
+  where
+    xs' = S.fromList xs
+    ys' = S.fromList ys
+    zs' = S.fromList zs
+
+    trans :: Ordering -> Ordering -> Ordering -> Bool
+    trans LT LT o = o == LT
+    trans LT EQ o = o == LT
+    trans LT GT _ = True
+    trans EQ LT o = o == LT
+    trans EQ EQ o = o == EQ
+    trans EQ GT o = o == GT
+    trans GT LT _ = True
+    trans GT EQ o = o == GT
+    trans GT GT o = o == GT
+
+map_prop :: Fun A B -> [A] -> Property
+map_prop f' xs = S.fromList (L.map f xs) === S.map f (S.fromList xs)
+  where
+    f = applyFun f'
+
+imap_prop :: Fun (Int, A) B -> [A] -> Property
+imap_prop f' xs = WI.imap f xs === S.toList (S.imap f (S.fromList xs))
+  where
+    f i x = applyFun f' (i, x)
+
+append_prop :: [A] -> [A] -> Property
+append_prop xs ys = S.fromList (xs ++ ys) === S.append (S.fromList xs) (S.fromList ys)
+
+append_prop_valid :: [A] -> [A] -> Property
+append_prop_valid xs ys = property (S.valid (S.append (S.fromList xs) (S.fromList ys)))
+
+foldr_prop :: Fun (A, B) B -> B -> [A] -> Property
+foldr_prop f' z xs = L.foldr f z xs === S.foldr f z (S.fromList xs) where
+    f a b = applyFun f' (a, b)
+
+ifoldr_prop :: Fun (Int, A, B) B -> B -> [A] -> Property
+ifoldr_prop f' z xs = WI.ifoldr f z xs === S.ifoldr f z (S.fromList xs) where
+    f i a b = applyFun f' (i, a, b)
+
+foldMap_prop :: Fun A [B] -> [A] -> Property
+foldMap_prop f' xs = foldMap f xs === S.foldMap f (S.fromList xs) where
+    f = applyFun f'
+
+ifoldMap_prop :: Fun (Int, A) [B] -> [A] -> Property
+ifoldMap_prop f' xs = WI.ifoldMap f xs === WI.ifoldMap f (S.fromList xs) where
+    f i a = applyFun f' (i, a)
+
+-- | Model of construction operators.
+data Model a
+    = Empty
+    | Singleton a
+    | FromList [a]
+    | Cons a (Model a)
+    | Uncons (Model a)
+    | Append (Model a) (Model a)
+  deriving Show
+
+instance Arbitrary a => Arbitrary (Model a) where
+    arbitrary = sized model
+
+model :: Arbitrary a => Int -> Gen (Model a)
+model n
+    | n <= 1
+    = oneof [ pure Empty, Singleton <$> arbitrary ]
+
+    | otherwise
+    = oneof
+        [ Cons <$> arbitrary <*> model (n - 2)
+        , Uncons <$> model (n - 2)
+        , FromList <$> vector n
+        , do
+            k <- chooseInt (1, n - 1)
+            Append <$> model k <*> model (n - 1 - k)
+        ]
+
+modelList :: Model a -> [a]
+modelList Empty          = []
+modelList (Singleton x)  = [x]
+modelList (FromList xs)  = xs
+modelList (Cons x xs)    = x : modelList xs
+modelList (Uncons xs)    = maybe [] snd (L.uncons (modelList xs))
+modelList (Append xs ys) = modelList xs ++ modelList ys
+
+modelSkewList :: Model a -> S.SkewList a
+modelSkewList Empty          = S.empty
+modelSkewList (Singleton x)  = S.singleton x
+modelSkewList (FromList xs)  = S.fromList xs
+modelSkewList (Cons x xs)    = S.cons x (modelSkewList xs)
+modelSkewList (Uncons xs)    = maybe S.empty snd (S.uncons (modelSkewList xs))
+modelSkewList (Append xs ys) = S.append (modelSkewList xs) (modelSkewList ys)
+
+model_prop :: Model A -> Property
+model_prop m = S.valid (modelSkewList m) .&&. S.fromList (modelList m) === modelSkewList m
diff --git a/tests/Strict.hs b/tests/Strict.hs
new file mode 100644
--- /dev/null
+++ b/tests/Strict.hs
@@ -0,0 +1,156 @@
+module Strict (tests) where
+
+import Test.QuickCheck
+       (Arbitrary (..), Fun, Gen, Property, applyFun, chooseInt, elements,
+       label, oneof, property, sized, vector, (.&&.), (===))
+import Test.QuickCheck.Poly  (A, B)
+import Test.Tasty            (TestTree, testGroup)
+import Test.Tasty.QuickCheck (testProperty)
+
+import qualified Data.Foldable.WithIndex as WI
+import qualified Data.Functor.WithIndex  as WI
+import qualified Data.List               as L
+import qualified Data.SkewList.Strict    as S
+
+tests :: TestTree
+tests = testGroup "Strict"
+    [ testProperty "valid" valid_prop
+    , testProperty "fromList . toList" $ \xs ->
+        xs === S.fromList (S.toList (xs :: S.SkewList Int))
+    , testProperty "toList . fromList" $ \xs ->
+        xs === S.toList (S.fromList (xs :: [Int]))
+
+    , testProperty "uncons" $ \xs ->
+        L.uncons xs === fmap (fmap S.toList) (S.uncons (S.fromList (xs :: [Int])))
+    , testProperty "length" $ \xs ->
+        L.length xs === S.length (S.fromList (xs :: [A]))
+    , testProperty "null" $ \xs ->
+        L.null xs === S.null (S.fromList (xs :: [A]))
+
+    , testProperty "eq" eq_prop
+    , testProperty "compare" compare_prop
+
+    , testProperty "map"  map_prop
+    , testProperty "imap" imap_prop
+    , testProperty "append" append_prop
+    , testProperty "append" append_prop_valid
+
+    , testProperty "foldr" foldr_prop
+    , testProperty "foldMap" foldMap_prop
+    , testProperty "ifoldr" ifoldr_prop
+    , testProperty "ifoldMap" ifoldMap_prop
+
+    , testProperty "model" model_prop
+    ]
+
+data SmallA = A0 | A1 | A2 deriving (Eq, Ord, Show)
+
+instance Arbitrary SmallA where
+    arbitrary = elements [A0,A1,A2]
+
+valid_prop :: S.SkewList A -> Property
+valid_prop xs = property (S.valid xs)
+
+eq_prop :: [SmallA] -> [SmallA] -> Property
+eq_prop xs ys = label (show (xs == ys)) $
+    (xs == ys) === (S.fromList xs == S.fromList ys)
+
+compare_prop :: [SmallA] -> [SmallA] -> [SmallA] -> Property
+compare_prop xs ys zs = label (show (compare xs' ys', compare ys' zs')) $ trans
+    (compare xs' ys')
+    (compare ys' zs')
+    (compare xs' zs')
+  where
+    xs' = S.fromList xs
+    ys' = S.fromList ys
+    zs' = S.fromList zs
+
+    trans :: Ordering -> Ordering -> Ordering -> Bool
+    trans LT LT o = o == LT
+    trans LT EQ o = o == LT
+    trans LT GT _ = True
+    trans EQ LT o = o == LT
+    trans EQ EQ o = o == EQ
+    trans EQ GT o = o == GT
+    trans GT LT _ = True
+    trans GT EQ o = o == GT
+    trans GT GT o = o == GT
+
+map_prop :: Fun A B -> [A] -> Property
+map_prop f' xs = S.fromList (L.map f xs) === S.map f (S.fromList xs)
+  where
+    f = applyFun f'
+
+imap_prop :: Fun (Int, A) B -> [A] -> Property
+imap_prop f' xs = WI.imap f xs === S.toList (S.imap f (S.fromList xs))
+  where
+    f i x = applyFun f' (i, x)
+
+append_prop :: [A] -> [A] -> Property
+append_prop xs ys = S.fromList (xs ++ ys) === S.append (S.fromList xs) (S.fromList ys)
+
+append_prop_valid :: [A] -> [A] -> Property
+append_prop_valid xs ys = property (S.valid (S.append (S.fromList xs) (S.fromList ys)))
+
+foldr_prop :: Fun (A, B) B -> B -> [A] -> Property
+foldr_prop f' z xs = L.foldr f z xs === S.foldr f z (S.fromList xs) where
+    f a b = applyFun f' (a, b)
+
+ifoldr_prop :: Fun (Int, A, B) B -> B -> [A] -> Property
+ifoldr_prop f' z xs = WI.ifoldr f z xs === S.ifoldr f z (S.fromList xs) where
+    f i a b = applyFun f' (i, a, b)
+
+foldMap_prop :: Fun A [B] -> [A] -> Property
+foldMap_prop f' xs = foldMap f xs === S.foldMap f (S.fromList xs) where
+    f = applyFun f'
+
+ifoldMap_prop :: Fun (Int, A) [B] -> [A] -> Property
+ifoldMap_prop f' xs = WI.ifoldMap f xs === WI.ifoldMap f (S.fromList xs) where
+    f i a = applyFun f' (i, a)
+
+-- | Model of construction operators.
+data Model a
+    = Empty
+    | Singleton a
+    | FromList [a]
+    | Cons a (Model a)
+    | Uncons (Model a)
+    | Append (Model a) (Model a)
+  deriving Show
+
+instance Arbitrary a => Arbitrary (Model a) where
+    arbitrary = sized model
+
+model :: Arbitrary a => Int -> Gen (Model a)
+model n
+    | n <= 1
+    = oneof [ pure Empty, Singleton <$> arbitrary ]
+
+    | otherwise
+    = oneof
+        [ Cons <$> arbitrary <*> model (n - 2)
+        , Uncons <$> model (n - 2)
+        , FromList <$> vector n
+        , do
+            k <- chooseInt (1, n - 1)
+            Append <$> model k <*> model (n - 1 - k)
+        ]
+
+modelList :: Model a -> [a]
+modelList Empty          = []
+modelList (Singleton x)  = [x]
+modelList (FromList xs)  = xs
+modelList (Cons x xs)    = x : modelList xs
+modelList (Uncons xs)    = maybe [] snd (L.uncons (modelList xs))
+modelList (Append xs ys) = modelList xs ++ modelList ys
+
+modelSkewList :: Model a -> S.SkewList a
+modelSkewList Empty          = S.empty
+modelSkewList (Singleton x)  = S.singleton x
+modelSkewList (FromList xs)  = S.fromList xs
+modelSkewList (Cons x xs)    = S.cons x (modelSkewList xs)
+modelSkewList (Uncons xs)    = maybe S.empty snd (S.uncons (modelSkewList xs))
+modelSkewList (Append xs ys) = S.append (modelSkewList xs) (modelSkewList ys)
+
+model_prop :: Model A -> Property
+model_prop m = S.valid (modelSkewList m) .&&. S.fromList (modelList m) === modelSkewList m
diff --git a/tests/skew-list-tests.hs b/tests/skew-list-tests.hs
new file mode 100644
--- /dev/null
+++ b/tests/skew-list-tests.hs
@@ -0,0 +1,12 @@
+module Main (main) where
+
+import Test.Tasty (defaultMain, testGroup)
+
+import qualified Lazy
+import qualified Strict
+
+main :: IO ()
+main = defaultMain $ testGroup "skewed"
+    [ Strict.tests
+    , Lazy.tests
+    ]
