diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,12 @@
+# Changelog
+
+`slist` uses [PVP Versioning][1].
+The changelog is available [on GitHub][2].
+
+0.0.0
+=====
+
+* Initially created.
+
+[1]: https://pvp.haskell.org
+[2]: https://github.com/vrom911/slist/releases
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+    means each individual or legal entity that creates, contributes to
+    the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+    means the combination of the Contributions of others (if any) used
+    by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+    means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+    means Source Code Form to which the initial Contributor has attached
+    the notice in Exhibit A, the Executable Form of such Source Code
+    Form, and Modifications of such Source Code Form, in each case
+    including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+    means
+
+    (a) that the initial Contributor has attached the notice described
+        in Exhibit B to the Covered Software; or
+
+    (b) that the Covered Software was made available under the terms of
+        version 1.1 or earlier of the License, but not also under the
+        terms of a Secondary License.
+
+1.6. "Executable Form"
+    means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+    means a work that combines Covered Software with other material, in
+    a separate file or files, that is not Covered Software.
+
+1.8. "License"
+    means this document.
+
+1.9. "Licensable"
+    means having the right to grant, to the maximum extent possible,
+    whether at the time of the initial grant or subsequently, any and
+    all of the rights conveyed by this License.
+
+1.10. "Modifications"
+    means any of the following:
+
+    (a) any file in Source Code Form that results from an addition to,
+        deletion from, or modification of the contents of Covered
+        Software; or
+
+    (b) any new file in Source Code Form that contains any Covered
+        Software.
+
+1.11. "Patent Claims" of a Contributor
+    means any patent claim(s), including without limitation, method,
+    process, and apparatus claims, in any patent Licensable by such
+    Contributor that would be infringed, but for the grant of the
+    License, by the making, using, selling, offering for sale, having
+    made, import, or transfer of either its Contributions or its
+    Contributor Version.
+
+1.12. "Secondary License"
+    means either the GNU General Public License, Version 2.0, the GNU
+    Lesser General Public License, Version 2.1, the GNU Affero General
+    Public License, Version 3.0, or any later versions of those
+    licenses.
+
+1.13. "Source Code Form"
+    means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+    means an individual or a legal entity exercising rights under this
+    License. For legal entities, "You" includes any entity that
+    controls, is controlled by, or is under common control with You. For
+    purposes of this definition, "control" means (a) the power, direct
+    or indirect, to cause the direction or management of such entity,
+    whether by contract or otherwise, or (b) ownership of more than
+    fifty percent (50%) of the outstanding shares or beneficial
+    ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+    Licensable by such Contributor to use, reproduce, make available,
+    modify, display, perform, distribute, and otherwise exploit its
+    Contributions, either on an unmodified basis, with Modifications, or
+    as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+    for sale, have made, import, and otherwise transfer either its
+    Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+    or
+
+(b) for infringements caused by: (i) Your and any other third party's
+    modifications of Covered Software, or (ii) the combination of its
+    Contributions with other software (except as part of its Contributor
+    Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+    its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+    Form, as described in Section 3.1, and You must inform recipients of
+    the Executable Form how they can obtain a copy of such Source Code
+    Form by reasonable means in a timely manner, at a charge no more
+    than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+    License, or sublicense it under different terms, provided that the
+    license for the Executable Form does not attempt to limit or alter
+    the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+*                                                                      *
+*  6. Disclaimer of Warranty                                           *
+*  -------------------------                                           *
+*                                                                      *
+*  Covered Software is provided under this License on an "as is"       *
+*  basis, without warranty of any kind, either expressed, implied, or  *
+*  statutory, including, without limitation, warranties that the       *
+*  Covered Software is free of defects, merchantable, fit for a        *
+*  particular purpose or non-infringing. The entire risk as to the     *
+*  quality and performance of the Covered Software is with You.        *
+*  Should any Covered Software prove defective in any respect, You     *
+*  (not any Contributor) assume the cost of any necessary servicing,   *
+*  repair, or correction. This disclaimer of warranty constitutes an   *
+*  essential part of this License. No use of any Covered Software is   *
+*  authorized under this License except under this disclaimer.         *
+*                                                                      *
+************************************************************************
+
+************************************************************************
+*                                                                      *
+*  7. Limitation of Liability                                          *
+*  --------------------------                                          *
+*                                                                      *
+*  Under no circumstances and under no legal theory, whether tort      *
+*  (including negligence), contract, or otherwise, shall any           *
+*  Contributor, or anyone who distributes Covered Software as          *
+*  permitted above, be liable to You for any direct, indirect,         *
+*  special, incidental, or consequential damages of any character      *
+*  including, without limitation, damages for lost profits, loss of    *
+*  goodwill, work stoppage, computer failure or malfunction, or any    *
+*  and all other commercial damages or losses, even if such party      *
+*  shall have been informed of the possibility of such damages. This   *
+*  limitation of liability shall not apply to liability for death or   *
+*  personal injury resulting from such party's negligence to the       *
+*  extent applicable law prohibits such limitation. Some               *
+*  jurisdictions do not allow the exclusion or limitation of           *
+*  incidental or consequential damages, so this exclusion and          *
+*  limitation may not apply to You.                                    *
+*                                                                      *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+  This Source Code Form is "Incompatible With Secondary Licenses", as
+  defined by the Mozilla Public License, v. 2.0.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,57 @@
+# slist
+
+[![Hackage](https://img.shields.io/hackage/v/slist.svg)](https://hackage.haskell.org/package/slist)
+[![MPL-2.0 license](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](LICENSE)
+[![Build status](https://secure.travis-ci.org/vrom911/slist.svg)](https://travis-ci.org/vrom911/slist)
+
+This package introduces sized list data type — `Slist`. The data type
+has the following shape:
+
+```haskell
+data Slist a = Slist
+    { sList :: [a]
+    , sSize :: Size
+    }
+```
+
+As you can see along with the familiar list, it contains `Size` field that
+represents the size of the structure. Slists can be finite or infinite, and this
+is expressed with `Size`.
+
+```haskell
+data Size
+    = Size Int
+    | Infinity
+```
+
+This representation of the list gives some additional advantages. Getting the
+length of the list is the "free" operation (runs in `O(1)`). This property
+helps to improve the performance for a bunch of functions like `take`, `drop`,
+`at`, etc. But also it doesn't actually add any overhead on the existing
+functions.
+
+Also, this allows to write a number of safe functions like `safeReverse`,
+`safeHead`, `safeLast`, `safeIsSuffixOf`, etc.
+
+## Comparison
+
+Check out the comparison table between lists and slists performance.
+
+| Function          | list (finite)                     | list (infinite)             | Slist (finite)                         | Slist (infinite) |
+|-------------------|-----------------------------------|-----------------------------|----------------------------------------|------------------|
+| `length`          | `O(n)`                            | <_hangs_>                   | `O(1)`                                 | `O(1)`           |
+| `safeLast`        | `O(n)`                            | <_hangs_>                   | `O(n)`                                 | `O(1)`           |
+| `init`            | `O(n)`                            | <_hangs_>                   | `O(n)`                                 | `O(1)`           |
+| `take`            | `O(min i n)`                      | `O(i)`                      | `0 < i < n`: `O(i)`; otherwise: `O(1)` | `O(i)`           |
+| `at`              | `O(min i n)` (run-time exception) | `O(i)` (run-time exception) | `0 < i < n`: `O(i)`; otherwise: `O(1)` | `O(i)`           |
+| `safeStripPrefix` | `O(m)`                            | `O(m)` (can hang)           | `O(m)`                                 | `O(m)`           |
+
+## Potential usage cases
+
+* When you ask the length of the list too frequently.
+* When you need to convert to data structures that require to know the list
+  size in advance for allocating an array of the elements.
+  _Example:_ [Vector data structure](https://hackage.haskell.org/package/vector).
+* When you need to serialised lists.
+* When you need to control the behaviour depending on the finiteness of the list.
+* When you need a more efficient or safe implementation of some functions.
diff --git a/slist.cabal b/slist.cabal
new file mode 100644
--- /dev/null
+++ b/slist.cabal
@@ -0,0 +1,100 @@
+cabal-version:       2.0
+name:                slist
+version:             0.0.0
+synopsis:            Sized list
+description:         This package implements @Slist@ data structure that stores the size
+                     of the list along with the list itself.
+homepage:            https://github.com/vrom911/slist
+bug-reports:         https://github.com/vrom911/slist/issues
+license:             MPL-2.0
+license-file:        LICENSE
+author:              Veronika Romashkina
+maintainer:          vrom911@gmail.com
+copyright:           2019 Veronika Romashkina
+category:            Data Structures, List
+build-type:          Simple
+extra-doc-files:     README.md
+                   , CHANGELOG.md
+tested-with:         GHC == 8.2.2
+                   , GHC == 8.4.4
+                   , GHC == 8.6.3
+
+source-repository head
+  type:                git
+  location:            https://github.com/vrom911/slist.git
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Slist
+                         Slist.Size
+
+  build-depends:       base >= 4.10.1.0 && < 4.13
+
+  ghc-options:         -Wall
+                       -Wincomplete-uni-patterns
+                       -Wincomplete-record-updates
+                       -Wcompat
+                       -Widentities
+                       -Wredundant-constraints
+                       -fhide-source-paths
+
+  default-language:    Haskell2010
+  default-extensions:  ConstraintKinds
+                       DeriveGeneric
+                       GeneralizedNewtypeDeriving
+                       InstanceSigs
+                       KindSignatures
+                       LambdaCase
+                       OverloadedStrings
+                       RecordWildCards
+                       ScopedTypeVariables
+                       StandaloneDeriving
+                       TupleSections
+                       TypeApplications
+                       ViewPatterns
+
+test-suite slist-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+
+  build-depends:       base >= 4.10.1.0 && < 4.13
+                     , slist
+
+  ghc-options:         -Wall
+                       -threaded
+                       -rtsopts
+                       -with-rtsopts=-N
+                       -Wincomplete-uni-patterns
+                       -Wincomplete-record-updates
+                       -Wcompat
+                       -Widentities
+                       -Wredundant-constraints
+                       -fhide-source-paths
+
+  default-language:    Haskell2010
+  default-extensions:  ConstraintKinds
+                       DeriveGeneric
+                       GeneralizedNewtypeDeriving
+                       InstanceSigs
+                       KindSignatures
+                       LambdaCase
+                       OverloadedStrings
+                       RecordWildCards
+                       ScopedTypeVariables
+                       StandaloneDeriving
+                       TupleSections
+                       TypeApplications
+                       ViewPatterns
+
+test-suite slist-doctest
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Doctest.hs
+
+  build-depends:       base >= 4.9 && < 5
+                     , doctest
+                     , Glob
+
+  ghc-options:         -threaded
+  default-language:    Haskell2010
diff --git a/src/Slist.hs b/src/Slist.hs
new file mode 100644
--- /dev/null
+++ b/src/Slist.hs
@@ -0,0 +1,1868 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE CPP          #-}
+{-# LANGUAGE TypeFamilies #-}
+
+{- |
+Copyright:  (c) 2019 vrom911
+License:    MPL-2.0
+Maintainer: Veronika Romashkina <vrom911@gmail.com>
+
+This module introduces sized list data type — 'Slist'. The data type
+has the following shape:
+
+@
+__data__ 'Slist' a = Slist
+    { sList :: [a]
+    , sSize :: 'Size'
+    }
+@
+
+As you can see along with the familiar list, it contains 'Size' field that
+represents the size of the structure. Slists can be finite or infinite, and this
+is expressed with 'Size'.
+
+@
+__data__ 'Size'
+    = Size 'Int'
+    | Infinity
+@
+
+This representation of the list gives some additional advantages. Getting the
+length of the list is the "free" operation (runs in \( O(1) \)). This property
+helps to improve the performance for a bunch of functions like 'take', 'drop',
+'at', etc. But also it doesn't actually add any overhead on the existing
+functions.
+
+Also, this allows to write a number of safe functions like 'safeReverse',
+'safeHead', 'safeLast', 'safeIsSuffixOf', etc.
+
+== Comparison
+
+Check out the comparison table between lists and slists performance.
+
++-------------------+--------------------+--------------------+-----------------------+-----------------------+
+| Function          | list (finite)      | list (infinite)    | Slist (finite)        | Slist (infinite)      |
++===================+====================+====================+=======================+=======================+
+| 'length'          | \( O(n) \)         | \</hangs/\>        |  \( O(1) \)           |   \( O(1) \)          |
++-------------------+--------------------+--------------------+-----------------------+-----------------------+
+| 'safeLast'        | \( O(n) \)         | \</hangs/\>        |  \( O(n) \)           |   \( O(1) \)          |
++-------------------+--------------------+--------------------+-----------------------+-----------------------+
+| 'init'            | \( O(n) \)         | \</hangs/\>        |  \( O(n) \)           |   \( O(1) \)          |
++-------------------+--------------------+--------------------+-----------------------+-----------------------+
+| 'take'            | \( O(min\ i\ n) \) | \( O(i) \)         | @0 < i < n@: \(O(i)\) |              \(O(i)\) |
+|                   |                    |                    | otherwise:   \(O(1)\) |                       |
++-------------------+--------------------+--------------------+-----------------------+-----------------------+
+| 'at'              | \( O(min\ i\ n) \) | \( O(i) \)         | @0 < i < n@: \(O(i)\) |   \( O(i) \)          |
+|                   | run-time exception | run-time exception | otherwise:   \(O(1)\) |                       |
++-------------------+--------------------+--------------------+-----------------------+-----------------------+
+| 'safeStripPrefix' | \( O(m) \)         |  \( O(m) \)        |  \( O(m) \)           |   \( O(m) \)          |
+|                   |                    | can hang           |                       |                       |
++-------------------+--------------------+--------------------+-----------------------+-----------------------+
+
+== Potential usage cases
+
+* When you ask the length of the list too frequently.
+* When you need to convert to data structures that require to know the list
+  size in advance for allocating an array of the elements. /Example:/ [Vector data
+  structure](https://hackage.haskell.org/package/vector).
+* When you need to serialised lists.
+* When you need to control the behaviour depending on the finiteness of the list.
+* When you need a more efficient or safe implementation of some functions.
+
+-}
+
+module Slist
+       ( -- * Types
+         Slist
+       , Size
+         -- ** Smart constructors
+       , slist
+       , infiniteSlist
+       , one
+       , iterate
+#if ( __GLASGOW_HASKELL__ > 802 )
+       , iterate'
+#endif
+       , repeat
+       , replicate
+       , cycle
+         -- * Basic functions
+       , len
+       , size
+       , isEmpty
+       , head
+       , safeHead
+       , last
+       , safeLast
+       , init
+       , tail
+       , uncons
+
+         -- * Transformations
+       , map
+       , reverse
+       , safeReverse
+       , intersperse
+       , intercalate
+       , transpose
+       , subsequences
+       , permutations
+
+         -- *  Reducing slists (folds)
+       , concat
+       , concatMap
+
+         -- * Building slists
+         -- ** Scans
+       , scanl
+       , scanl'
+       , scanl1
+       , scanr
+       , scanr1
+
+         -- ** Unfolding
+       , unfoldr
+
+         -- * Subslists
+         -- ** Extracting
+       , take
+       , drop
+       , splitAt
+       , takeWhile
+       , dropWhile
+       , span
+       , break
+       , stripPrefix
+       , safeStripPrefix
+       , group
+       , groupBy
+       , inits
+       , tails
+         -- ** Predicates
+       , isPrefixOf
+       , safeIsPrefixOf
+       , isSuffixOf
+       , safeIsSuffixOf
+       , isInfixOf
+       , safeIsInfixOf
+       , isSubsequenceOf
+       , safeIsSubsequenceOf
+
+         -- * Searching
+         -- ** Searching by equality
+       , lookup
+         -- ** Searching with a predicate
+       , filter
+       , partition
+
+         -- * Indexing
+       , at
+       , unsafeAt
+       , elemIndex
+       , elemIndices
+       , findIndex
+       , findIndices
+
+         -- * Zipping and unzipping
+       , zip
+       , zip3
+       , zipWith
+       , zipWith3
+       , unzip
+       , unzip3
+
+         -- * Sets
+         -- $sets
+       , nub
+       , nubBy
+       , delete
+       , deleteBy
+       , deleteFirstsBy
+       , diff
+       , union
+       , unionBy
+       , intersect
+       , intersectBy
+
+         -- * Ordered slists
+       , sort
+       , sortBy
+       , sortOn
+       , insert
+       , insertBy
+       ) where
+
+import Control.Applicative (Alternative (empty, (<|>)), liftA2)
+import Data.Bifunctor (bimap, first, second)
+#if ( __GLASGOW_HASKELL__ == 802 )
+import Data.Semigroup (Semigroup (..))
+#endif
+import Prelude hiding (break, concat, concatMap, cycle, drop, dropWhile, filter, head, init,
+                iterate, last, lookup, map, repeat, replicate, reverse, scanl, scanl1, scanr,
+                scanr1, span, splitAt, tail, take, takeWhile, unzip, unzip3, zip, zip3, zipWith,
+                zipWith3)
+
+import Slist.Size (Size (..), sizes)
+
+import qualified Data.Foldable as F (Foldable (..))
+import qualified Data.List as L
+import qualified GHC.Exts as L (IsList (..))
+import qualified Prelude as P
+
+{- | Data type that represents sized list.
+Size can be both finite or infinite, it is established using
+'Size' data type.
+-}
+data Slist a = Slist
+    { sList :: [a]
+    , sSize :: Size
+    } deriving (Show, Read)
+
+{- | Equality of sized lists is checked more efficiently
+due to the fact that the check on the list sizes can be
+done first for the constant time.
+-}
+instance (Eq a) => Eq (Slist a) where
+    (Slist l1 s1) == (Slist l2 s2) = s1 == s2 && l1 == l2
+    {-# INLINE (==) #-}
+
+-- | Lexicographical comparison of the lists.
+instance (Ord a) => Ord (Slist a) where
+    compare (Slist l1 _) (Slist l2 _) = compare l1 l2
+    {-# INLINE compare #-}
+
+{- | List appending. Use '<>' for 'Slist' concatenation instead of
+'L.++' operator that is common in ordinary list concatenations.
+-}
+instance Semigroup (Slist a) where
+    (<>) :: Slist a -> Slist a -> Slist a
+    (Slist l1 s1) <> (Slist l2 s2) = Slist (l1 <> l2) (s1 + s2)
+    {-# INLINE (<>) #-}
+
+instance Monoid (Slist a) where
+    mempty :: Slist a
+    mempty = Slist [] 0
+    {-# INLINE mempty #-}
+
+    mappend :: Slist a -> Slist a -> Slist a
+    mappend = (<>)
+    {-# INLINE mappend #-}
+
+    mconcat :: [Slist a] -> Slist a
+    mconcat ls = let (l, s) = foldr f ([], 0) ls in Slist l s
+      where
+        -- foldr :: (a -> ([a], Size) -> ([a], Size)) -> ([a], Size) -> [Slist a] -> ([a], Size)
+        f :: Slist a -> ([a], Size) -> ([a], Size)
+        f (Slist l s) (xL, !xS) = (xL ++ l, s + xS)
+    {-# INLINE mconcat #-}
+
+instance Functor Slist where
+    fmap :: (a -> b) -> Slist a -> Slist b
+    fmap = map
+    {-# INLINE fmap #-}
+
+instance Applicative Slist where
+    pure :: a -> Slist a
+    pure = one
+    {-# INLINE pure #-}
+
+    (<*>) :: Slist (a -> b) -> Slist a -> Slist b
+    fsl <*> sl = Slist
+        { sList = sList fsl <*> sList sl
+        , sSize = sSize fsl  *  sSize sl
+        }
+    {-# INLINE (<*>) #-}
+
+    liftA2 :: (a -> b -> c) -> Slist a -> Slist b -> Slist c
+    liftA2 f sla slb = Slist
+        { sList = liftA2 f (sList sla) (sList slb)
+        , sSize = sSize sla * sSize slb
+        }
+    {-# INLINE liftA2 #-}
+
+instance Alternative Slist where
+    empty :: Slist a
+    empty = mempty
+    {-# INLINE empty #-}
+
+    (<|>) :: Slist a -> Slist a -> Slist a
+    (<|>) = (<>)
+    {-# INLINE (<|>) #-}
+
+instance Monad Slist where
+    return :: a -> Slist a
+    return = pure
+    {-# INLINE return #-}
+
+    (>>=) :: Slist a -> (a -> Slist b) -> Slist b
+    sl >>= f = mconcat $ P.map f $ sList sl
+    {-# INLINE (>>=) #-}
+
+{- | Efficient implementation of 'sum' and 'product' functions.
+'length' returns 'Int's 'maxBound' on infinite lists.
+-}
+instance Foldable Slist where
+    foldMap :: (Monoid m) => (a -> m) -> Slist a -> m
+    foldMap f = foldMap f . sList
+    {-# INLINE foldMap #-}
+
+    foldr :: (a -> b -> b) -> b -> Slist a -> b
+    foldr f b = foldr f b . sList
+    {-# INLINE foldr #-}
+
+    -- | Is the element in the structure?
+    elem :: (Eq a) => a -> Slist a -> Bool
+    elem a = elem a . sList
+    {-# INLINE elem #-}
+
+    maximum :: (Ord a) => Slist a -> a
+    maximum = maximum . sList
+    {-# INLINE maximum #-}
+
+    minimum :: (Ord a) => Slist a -> a
+    minimum = minimum . sList
+    {-# INLINE minimum #-}
+
+    sum :: (Num a) => Slist a -> a
+    sum = F.foldl' (+) 0 . sList
+    {-# INLINE sum #-}
+
+    product :: (Num a) => Slist a -> a
+    product = F.foldl' (*) 1 . sList
+    {-# INLINE product #-}
+
+    null :: Slist a -> Bool
+    null = isEmpty
+    {-# INLINE null #-}
+
+    length :: Slist a -> Int
+    length = len
+    {-# INLINE length #-}
+
+    toList :: Slist a -> [a]
+    toList = sList
+    {-# INLINE toList #-}
+
+instance Traversable Slist where
+    traverse :: (Applicative f) => (a -> f b) -> Slist a -> f (Slist b)
+    traverse f (Slist l s) = (\x -> Slist x s) <$> traverse f l
+    {-# INLINE traverse #-}
+
+instance L.IsList (Slist a) where
+    type (Item (Slist a)) = a
+    fromList :: [a] -> Slist a
+    fromList = slist
+    {-# INLINE fromList #-}
+
+    toList :: Slist a -> [a]
+    toList = sList
+    {-# INLINE toList #-}
+
+    fromListN :: Int -> [a] -> Slist a
+    fromListN n l = Slist l $ Size n
+    {-# INLINE fromListN #-}
+
+{- | @O(n)@. Constructs 'Slist' from the given list.
+
+>>> slist [1..5]
+Slist {sList = [1,2,3,4,5], sSize = Size 5}
+
+/Note:/ works with finite lists. Use 'infiniteSlist'
+to construct infinite lists.
+-}
+slist :: [a] -> Slist a
+slist l = Slist l (Size $ length l)
+{-# INLINE slist #-}
+
+{- | @O(1)@. Constructs 'Slist' from the given list.
+
+@
+>> infiniteSlist [1..]
+Slist {sList = [1..], sSize = Infinity}
+@
+
+/Note:/ works with infinite lists. Use 'slist'
+to construct finite lists.
+-}
+infiniteSlist :: [a] -> Slist a
+infiniteSlist l = Slist l Infinity
+{-# INLINE infiniteSlist #-}
+
+{- | @O(1)@. Creates 'Slist' with a single element.
+The size of such 'Slist' is always equals to @Size 1@.
+
+>>> one "and only"
+Slist {sList = ["and only"], sSize = Size 1}
+
+-}
+one :: a -> Slist a
+one a = Slist [a] 1
+{-# INLINE one #-}
+
+{- | Returns an infinite slist of repeated applications
+of the given function to the start element:
+
+> iterate f x == [x, f x, f (f x), ...]
+
+@
+>> __iterate (+1) 0__
+Slist {sList = [0..], sSize = 'Infinity'}
+@
+
+>>> take 5 $ iterate ('a':) "a"
+Slist {sList = ["a","aa","aaa","aaaa","aaaaa"], sSize = Size 5}
+
+/Note:/ 'L.iterate' is lazy, potentially leading to thunk build-up if
+the consumer doesn't force each iterate.
+See 'iterate'' for a strict variant of this function.
+-}
+iterate :: (a -> a) -> a -> Slist a
+iterate f = infiniteSlist . L.iterate f
+{-# INLINE iterate #-}
+
+#if ( __GLASGOW_HASKELL__ > 802 )
+{- | Returns an infinite slist of repeated applications
+of the given function to the start element:
+
+> iterate' f x == [x, f x, f (f x), ...]
+
+@
+>> __iterate' (+1) 0__
+Slist {sList = [0..], sSize = 'Infinity'}
+@
+
+>>> take 5 $ iterate' ('a':) "a"
+Slist {sList = ["a","aa","aaa","aaaa","aaaaa"], sSize = Size 5}
+
+'iterate'' is the strict version of 'iterate'.
+
+It ensures that the result of each application of force to weak head normal
+form before proceeding.
+-}
+iterate' :: (a -> a) -> a -> Slist a
+iterate' f = infiniteSlist . L.iterate' f
+{-# INLINE iterate' #-}
+#endif
+
+{- | @O(1)@. Creates an infinite slist with the given element
+at each position.
+
+@
+>> __repeat 42__
+Slist {sList = [42, 42 ..], sSize = 'Infinity'}
+@
+
+>>> take 6 $ repeat 'm'
+Slist {sList = "mmmmmm", sSize = Size 6}
+
+-}
+repeat :: a -> Slist a
+repeat = infiniteSlist . L.repeat
+{-# INLINE repeat #-}
+
+{- | @O(n)@. Creates a finite slist with the given value at each position.
+
+>>> replicate 3 'o'
+Slist {sList = "ooo", sSize = Size 3}
+>>> replicate (-11) "hmm"
+Slist {sList = [], sSize = Size 0}
+-}
+replicate :: Int -> a -> Slist a
+replicate n x
+  | n <= 0 = mempty
+  | otherwise = Slist (L.replicate n x) $ Size n
+{-# INLINE replicate #-}
+
+{- | Ties a finite list into a circular one, or equivalently,
+the infinite repetition of the original list.
+It is the identity on infinite lists.
+
+>>> take 23 $ cycle (slist "pam ")
+Slist {sList = "pam pam pam pam pam pam", sSize = Size 23}
+
+@
+>> __cycle $ 'infiniteSlist' [1..]__
+Slist {sList = [1..], sSize = 'Infinity'}
+@
+-}
+cycle :: Slist a -> Slist a
+cycle sl@(Slist _ Infinity) = sl
+cycle Slist{..}             = infiniteSlist $ L.cycle sList
+{-# INLINE cycle #-}
+
+----------------------------------------------------------------------------
+-- Basic functions
+----------------------------------------------------------------------------
+
+{- | @O(1)@. Returns the length of a structure as an 'Int'.
+On infinite lists returns the 'Int's 'maxBound'.
+
+>>> len $ one 42
+1
+>>> len $ slist [1..3]
+3
+>>> len $ infiniteSlist [1..]
+9223372036854775807
+-}
+len :: Slist a -> Int
+len Slist{..} = case sSize of
+    Infinity -> maxBound
+    Size n   -> n
+{-# INLINE len #-}
+
+{- | @O(1)@. Returns the 'Size' of the slist.
+
+>>> size $ slist "Hello World!"
+Size 12
+>>> size $ infiniteSlist [1..]
+Infinity
+-}
+size :: Slist a -> Size
+size = sSize
+{-# INLINE size #-}
+
+{- | @O(1)@. Checks if 'Slist' is empty
+
+>>> isEmpty mempty
+True
+>>> isEmpty $ slist []
+True
+>>> isEmpty $ slist "Not Empty"
+False
+-}
+isEmpty :: Slist a -> Bool
+isEmpty = (== 0) . size
+{-# INLINE isEmpty #-}
+
+{- | @O(1)@. Extracts the first element of a slist.
+Uses not total 'L.head' function, so use wisely.
+
+It is recommended to use 'safeHead' instead.
+
+>>> head $ slist "qwerty"
+'q'
+>>> head $ infiniteSlist [1..]
+1
+>>> head mempty
+*** Exception: Prelude.head: empty list
+
+-}
+head :: Slist a -> a
+head = P.head . sList
+{-# INLINE head #-}
+
+{- | @O(1)@. Extracts the first element of a slist if possible.
+
+>>> safeHead $ slist "qwerty"
+Just 'q'
+>>> safeHead $ infiniteSlist [1..]
+Just 1
+>>> safeHead mempty
+Nothing
+-}
+safeHead :: Slist a -> Maybe a
+safeHead Slist{..} = case sSize of
+    Size 0 -> Nothing
+    _      -> Just $ P.head sList
+{-# INLINE safeHead #-}
+
+{- | @O(n)@. Extracts the last element of a list.
+Uses not total 'L.last' function, so use wisely.
+
+It is recommended to use 'safeLast' instead
+
+>>> last $ slist "qwerty"
+'y'
+>>> last mempty
+*** Exception: Prelude.last: empty list
+
+@
+>> last $ infiniteSlist [1..]
+\</hangs/\>
+@
+-}
+last :: Slist a -> a
+last = P.last . sList
+{-# INLINE last #-}
+
+{- | @O(n)@. Extracts the last element of a list if possible.
+
+>>> safeLast $ slist "qwerty"
+Just 'y'
+>>> safeLast mempty
+Nothing
+>>> safeLast $ infiniteSlist [1..]
+Nothing
+-}
+safeLast :: Slist a -> Maybe a
+safeLast Slist{..} = case sSize of
+    Infinity -> Nothing
+    Size 0   -> Nothing
+    _        -> Just $ P.last sList
+{-# INLINE safeLast #-}
+
+{- | @O(1)@. Returns a slist with all the elements after
+the head of a given slist.
+
+>>> tail mempty
+Slist {sList = [], sSize = Size 0}
+>>> tail $ slist "Hello"
+Slist {sList = "ello", sSize = Size 4}
+
+@
+>> __tail $ 'infiniteSlist' [0..]__
+Slist {sList = [1..], sSize = 'Infinity'}
+@
+-}
+tail :: Slist a -> Slist a
+tail Slist{..} = case sSize of
+    Size 0 -> mempty
+    _      -> Slist (P.drop 1 sList) (sSize - 1)
+{-# INLINE tail #-}
+
+{- | @O(n)@. Return all the elements of a list except the last one.
+
+>>> init mempty
+Slist {sList = [], sSize = Size 0}
+>>> init $ slist "Hello"
+Slist {sList = "Hell", sSize = Size 4}
+
+@
+>> __init $ 'infiniteSlist' [0..]__
+Slist {sList = [0..], sSize = 'Infinity'}
+@
+-}
+init :: Slist a -> Slist a
+init sl@Slist{..} = case sSize of
+    Infinity -> sl
+    Size 0   -> mempty
+    _        -> Slist (P.init sList) (sSize - 1)
+{-# INLINE init #-}
+
+{- | @O(1)@. Decomposes a slist into its head and tail.
+If the slist is empty, returns 'Nothing'.
+
+>>> uncons mempty
+Nothing
+>>> uncons $ one 'a'
+Just ('a',Slist {sList = "", sSize = Size 0})
+
+@
+>> __uncons $ 'infiniteSlist' [0..]__
+Just (0, Slist {sList = [1..], sSize = 'Infinity'})
+@
+-}
+uncons :: Slist a -> Maybe (a, Slist a)
+uncons (Slist [] _)     = Nothing
+uncons (Slist (x:xs) s) = Just (x, Slist xs $ s - 1)
+{-# INLINE uncons #-}
+
+----------------------------------------------------------------------------
+-- Transformations
+----------------------------------------------------------------------------
+
+{- | @O(n)@. Applies the given function to each element of the slist.
+
+> map f (slist [x1, x2, ..., xn])     == slist [f x1, f x2, ..., f xn]
+> map f (infiniteSlist [x1, x2, ...]) == infiniteSlist [f x1, f x2, ...]
+
+-}
+map :: (a -> b) -> Slist a -> Slist b
+map f Slist{..} = Slist (P.map f sList) sSize
+{-# INLINE map #-}
+
+{- | @O(n)@. Returns the elements of the slist in reverse order.
+
+>>> reverse $ slist "Hello"
+Slist {sList = "olleH", sSize = Size 5}
+>>> reverse $ slist "wow"
+Slist {sList = "wow", sSize = Size 3}
+
+/Note:/ 'reverse' slist can not be calculated on infinite slists.
+
+@
+>> __reverse $ 'infiniteSlist' [1..]__
+\</hangs/\>
+@
+
+Use 'safeReverse' to not hang on infinite slists.
+-}
+reverse :: Slist a -> Slist a
+reverse Slist{..} = Slist (L.reverse sList) sSize
+{-# INLINE reverse #-}
+
+{- | @O(n)@. Returns the elements of the slist in reverse order.
+On infinite slists returns the initial slist.
+
+>>> safeReverse $ slist "Hello"
+Slist {sList = "olleH", sSize = Size 5}
+
+@
+>> __reverse $ 'infiniteSlist' [1..]__
+Slist {sList = [1..], sSize = 'Infinity'}
+@
+-}
+safeReverse :: Slist a -> Slist a
+safeReverse sl@(Slist _ Infinity) = sl
+safeReverse sl                    = reverse sl
+{-# INLINE safeReverse #-}
+
+{- | @O(n)@. Takes an element and a list and intersperses
+that element between the elements of the list.
+
+>>> intersperse ',' $ slist "abcd"
+Slist {sList = "a,b,c,d", sSize = Size 7}
+>>> intersperse '!' mempty
+Slist {sList = "", sSize = Size 0}
+
+@
+>> __intersperse 0 $ 'infiniteSlist' [1,1..]__
+Slist {sList = [1,0,1,0..], sSize = 'Infinity'}
+@
+-}
+intersperse :: a -> Slist a -> Slist a
+intersperse _ sl@(Slist _ (Size 0)) = sl
+intersperse a Slist{..}             = Slist (L.intersperse a sList) (2 * sSize - 1)
+{-# INLINE intersperse #-}
+
+{- | @O(n)@. Inserts the given slist in between the slists and concatenates the result.
+
+> intercalate x xs = concat (intersperse x xs)
+
+>>> intercalate (slist ", ") $ slist [slist "Lorem", slist "ipsum", slist "dolor"]
+Slist {sList = "Lorem, ipsum, dolor", sSize = Size 19}
+
+-}
+intercalate :: Slist a -> Slist (Slist a) -> Slist a
+intercalate x = foldr (<>) mempty . intersperse x
+{-# INLINE intercalate #-}
+
+{- | @O(n * m)@. Transposes the rows and columns of the slist.
+
+>>> transpose $ slist [slist [1,2]]
+Slist {sList = [Slist {sList = [1], sSize = Size 1},Slist {sList = [2], sSize = Size 1}], sSize = Size 2}
+
+@
+>> __transpose $ slist [slist [1,2,3], slist [4,5,6]]__
+Slist { sList =
+          [ Slist {sList = [1,4], sSize = Size 2}
+          , Slist {sList = [2,5], sSize = Size 2}
+          , Slist {sList = [3,6], sSize = Size 2}
+          ]
+      , sSize = Size 3
+      }
+@
+
+If some of the rows are shorter than the following rows, their elements are skipped:
+
+>>> transpose $ slist [slist [10,11], slist [20], mempty]
+Slist {sList = [Slist {sList = [10,20], sSize = Size 2},Slist {sList = [11], sSize = Size 1}], sSize = Size 2}
+
+If some of the rows is an infinite slist, then the resulting slist is going to be infinite.
+-}
+transpose :: Slist (Slist a) -> Slist (Slist a)
+transpose (Slist l _) = Slist
+    { sList = P.map slist $ L.transpose $ P.map sList l
+    , sSize = maximum $ P.map sSize l
+    }
+{-# INLINE transpose #-}
+
+{- | @O(2 ^ n)@. Returns the list of all subsequences of the argument.
+
+>>> subsequences mempty
+Slist {sList = [Slist {sList = [], sSize = Size 0}], sSize = Size 1}
+>>> subsequences $ slist "ab"
+Slist {sList = [Slist {sList = "", sSize = Size 0},Slist {sList = "a", sSize = Size 1},Slist {sList = "b", sSize = Size 1},Slist {sList = "ab", sSize = Size 2}], sSize = Size 4}
+>>> take 4 $ subsequences $ infiniteSlist [1..]
+Slist {sList = [Slist {sList = [], sSize = Size 0},Slist {sList = [1], sSize = Size 1},Slist {sList = [2], sSize = Size 1},Slist {sList = [1,2], sSize = Size 2}], sSize = Size 4}
+
+-}
+subsequences :: Slist a -> Slist (Slist a)
+subsequences Slist{..} = Slist
+    { sList = P.map slist $ L.subsequences sList
+    , sSize = newSize sSize
+    }
+  where
+    newSize :: Size -> Size
+    newSize Infinity = Infinity
+    newSize (Size n) = Size $ 2 ^ toInteger n
+{-# INLINE subsequences #-}
+
+{- | @O(n!)@. Returns the list of all permutations of the argument.
+
+>>> permutations mempty
+Slist {sList = [Slist {sList = [], sSize = Size 0}], sSize = Size 1}
+>>> permutations $ slist "abc"
+Slist {sList = [Slist {sList = "abc", sSize = Size 3},Slist {sList = "bac", sSize = Size 3},Slist {sList = "cba", sSize = Size 3},Slist {sList = "bca", sSize = Size 3},Slist {sList = "cab", sSize = Size 3},Slist {sList = "acb", sSize = Size 3}], sSize = Size 6}
+
+-}
+permutations :: Slist a -> Slist (Slist a)
+permutations (Slist l s) = Slist
+    { sList = P.map (\a -> Slist a s) $ L.permutations l
+    , sSize = fact s
+    }
+  where
+    fact :: Size -> Size
+    fact Infinity = Infinity
+    fact (Size n) = Size $ go 1 n
+
+    go :: Int -> Int -> Int
+    go !acc 0 = acc
+    go !acc n = go (acc * n) (n - 1)
+{-# INLINE permutations #-}
+
+----------------------------------------------------------------------------
+-- Reducing slists (folds)
+----------------------------------------------------------------------------
+
+{- | \( O(\sum n_i) \) The concatenation of all the elements of a container of slists.
+
+>>> concat [slist [1,2], slist [3..5], slist [6..10]]
+Slist {sList = [1,2,3,4,5,6,7,8,9,10], sSize = Size 10}
+
+@
+>> __ concat $ slist [slist [1,2], 'infiniteSlist' [3..]]__
+Slist {sList = [1..], sSize = 'Infinity'}
+@
+-}
+concat :: Foldable t => t (Slist a) -> Slist a
+concat = foldr (<>) mempty
+{-# INLINE concat #-}
+
+{- | Maps a function over all the elements of a container
+and concatenates the resulting slists.
+
+>>> concatMap one "abc"
+Slist {sList = "abc", sSize = Size 3}
+-}
+concatMap :: Foldable t => (a -> Slist b) -> t a -> Slist b
+concatMap = foldMap
+{-# INLINE concatMap #-}
+
+----------------------------------------------------------------------------
+-- Building lists
+----------------------------------------------------------------------------
+
+{- | @O(n)@. Similar to 'foldl', but returns a slist of successive
+reduced values from the left:
+
+> scanl f z $ slist [x1, x2, ...] == slist [z, z `f` x1, (z `f` x1) `f` x2, ...]
+
+Note that
+
+> last (scanl f z xs) == foldl f z xs.
+
+This peculiar arrangement is necessary to prevent scanl being rewritten in
+its own right-hand side.
+
+>>> scanl (+) 0 $ slist [1..10]
+Slist {sList = [0,1,3,6,10,15,21,28,36,45,55], sSize = Size 11}
+
+-}
+scanl :: (b -> a -> b) -> b -> Slist a -> Slist b
+scanl f b Slist{..} = Slist (L.scanl f b sList) (sSize + 1)
+{-# INLINE scanl #-}
+
+-- | @O(n)@. A strictly accumulating version of 'scanl'
+scanl' :: (b -> a -> b) -> b -> Slist a -> Slist b
+scanl' f b Slist{..} = Slist (L.scanl' f b sList) (sSize + 1)
+{-# INLINE scanl' #-}
+
+{- | @O(n)@. 'scanl1' is a variant of 'scanl' that has no starting value argument:
+
+> scanl1 f $ slist [x1, x2, ...] == slist [x1, x1 `f` x2, ...]
+-}
+scanl1 :: (a -> a -> a) -> Slist a -> Slist a
+scanl1 f Slist{..} = Slist (L.scanl1 f sList) sSize
+{-# INLINE scanl1 #-}
+
+{- | @O(n)@. The right-to-left dual of 'scanl'.
+
+Note that
+
+> head (scanr f z xs) == foldr f z xs.
+
+>>> scanr (+) 0 $ slist [1..10]
+Slist {sList = [55,54,52,49,45,40,34,27,19,10,0], sSize = Size 11}
+
+-}
+scanr :: (a -> b -> b) -> b -> Slist a -> Slist b
+scanr f b Slist{..} = Slist (L.scanr f b sList) (sSize + 1)
+{-# INLINE scanr #-}
+
+-- | A variant of 'scanr' that has no starting value argument.
+scanr1 :: (a -> a -> a) -> Slist a -> Slist a
+scanr1 f Slist{..} = Slist (L.scanr1 f sList) sSize
+{-# INLINE scanr1 #-}
+
+{- | @O(n)@. A \`dual\' to 'foldr': while 'foldr'
+reduces a list to a summary value, 'unfoldr' builds a list from
+a seed value.  The function takes the element and returns 'Nothing'
+if it is done producing the list or returns 'Just' @(a,b)@, in which
+case, @a@ is a prepended to the list and @b@ is used as the next
+element in a recursive call.
+
+In some cases, 'unfoldr' can undo a 'foldr' operation:
+
+> unfoldr f' (foldr f z xs) == xs
+
+if the following holds:
+
+> f' (f x y) = Just (x,y)
+> f' z       = Nothing
+
+A simple use of unfoldr:
+
+>>> unfoldr (\b -> if b == 0 then Nothing else Just (b, b-1)) 10
+Slist {sList = [10,9,8,7,6,5,4,3,2,1], sSize = Size 10}
+-}
+unfoldr :: forall a b . (b -> Maybe (a, b)) -> b -> Slist a
+unfoldr f def = let (s, l) = go def in Slist l $ Size s
+  where
+    go :: b -> (Int, [a])
+    go b = case f b of
+        Just (a, newB) -> bimap (+ 1) (a:) $ go newB
+        Nothing        -> (0, [])
+{-# INLINE unfoldr #-}
+
+----------------------------------------------------------------------------
+-- Sublists
+----------------------------------------------------------------------------
+
+{- | @O(i) | i < n@ and @O(1) | otherwise@.
+
+Returns the prefix of the slist of the given length.
+If the given @i@ is non-positive then the empty structure is returned.
+If @i@ is exceeds the length of the structure the initial slist is returned.
+
+>>> take 5 $ slist "Hello world!"
+Slist {sList = "Hello", sSize = Size 5}
+>>> take 20 $ slist "small"
+Slist {sList = "small", sSize = Size 5}
+>>> take 0 $ slist "none"
+Slist {sList = "", sSize = Size 0}
+>>> take (-11) $ slist "hmm"
+Slist {sList = "", sSize = Size 0}
+>>> take 4 $ infiniteSlist [1..]
+Slist {sList = [1,2,3,4], sSize = Size 4}
+-}
+take :: Int -> Slist a -> Slist a
+take i sl@Slist{..}
+    | Size i >= sSize = sl
+    | i <= 0 = mempty
+    | otherwise = Slist
+        { sList = P.take i sList
+        , sSize = Size i
+        }
+{-# INLINE take #-}
+
+{- | @O(i) | i < n@ and @O(1) | otherwise@.
+
+Returns the suffix of the slist after the first @i@ elements.
+If @i@ exceeds the length of the slist then the empty structure is returned.
+If @i@ is non-positive the initial structure is returned.
+
+>>> drop 6 $ slist "Hello World"
+Slist {sList = "World", sSize = Size 5}
+>>> drop 42 $ slist "oops!"
+Slist {sList = "", sSize = Size 0}
+>>> drop 0 $ slist "Hello World!"
+Slist {sList = "Hello World!", sSize = Size 12}
+>>> drop (-4) $ one 42
+Slist {sList = [42], sSize = Size 1}
+
+@
+>> __drop 5 $ 'infiniteSlist' [1..]__
+Slist {sList = [6..], sSize = 'Infinity'}
+@
+
+-}
+drop :: Int -> Slist a -> Slist a
+drop i sl@Slist{..}
+    | i <= 0 = sl
+    | Size i >= sSize = mempty
+    | otherwise = Slist
+        { sList = P.drop i sList
+        , sSize = sSize - Size i
+        }
+{-# INLINE drop #-}
+
+{- | @O(i) | i < n@ and @O(1) | otherwise@.
+
+Returns a tuple where the first element is the prefix
+of the given length and the second element is the remainder
+of the slist.
+
+>>> splitAt 5 $ slist "Hello World!"
+(Slist {sList = "Hello", sSize = Size 5},Slist {sList = " World!", sSize = Size 7})
+>>> splitAt 0 $ slist "abc"
+(Slist {sList = "", sSize = Size 0},Slist {sList = "abc", sSize = Size 3})
+>>> splitAt 4 $ slist "abc"
+(Slist {sList = "abc", sSize = Size 3},Slist {sList = "", sSize = Size 0})
+>>>splitAt (-42) $ slist "??"
+(Slist {sList = "", sSize = Size 0},Slist {sList = "??", sSize = Size 2})
+
+@
+>> __splitAt 2 $ 'infiniteSlist' [1..]__
+(Slist {sList = [1,2], sSize = 'Size' 2}, Slist {sList = [3..], sSize = 'Infinity'})
+@
+-}
+splitAt :: Int -> Slist a -> (Slist a, Slist a)
+splitAt i sl@Slist{..}
+    | i <= 0 = (mempty, sl)
+    | Size i >= sSize = (sl, mempty)
+    | otherwise =
+        let (l1, l2) = P.splitAt i sList
+            s2 = sSize - Size i
+        in (Slist l1 $ Size i, Slist l2 s2)
+{-# INLINE splitAt #-}
+
+{- | @O(n)@. Returns the longest prefix (possibly empty)
+of elements that satisfy the given predicate.
+
+>>> takeWhile (<3) $ slist [1..10]
+Slist {sList = [1,2], sSize = Size 2}
+>>> takeWhile (<3) $ infiniteSlist [1..]
+Slist {sList = [1,2], sSize = Size 2}
+>>> takeWhile (<=10) $ slist [1..10]
+Slist {sList = [1,2,3,4,5,6,7,8,9,10], sSize = Size 10}
+>>> takeWhile (<0) $ slist [1..10]
+Slist {sList = [], sSize = Size 0}
+
+-}
+takeWhile :: forall a . (a -> Bool) -> Slist a -> Slist a
+takeWhile p Slist{..} = let (s, l) = go 0 sList in
+    Slist l $ Size s
+  where
+    go :: Int -> [a] -> (Int, [a])
+    go !n [] = (n, [])
+    go !n (x:xs) =
+        if p x
+        then let (i, l) = go (n + 1) xs in (i, x:l)
+        else (n, [])
+{-# INLINE takeWhile #-}
+
+{- | @O(n)@. Returns the suffix (possibly empty) of the remaining
+elements after dropping elements that satisfy the given predicate.
+
+>>> dropWhile (<3) $ slist [1..10]
+Slist {sList = [3,4,5,6,7,8,9,10], sSize = Size 8}
+>>> dropWhile (<=10) $ slist [1..10]
+Slist {sList = [], sSize = Size 0}
+>>> dropWhile (<0) $ slist [1..10]
+Slist {sList = [1,2,3,4,5,6,7,8,9,10], sSize = Size 10}
+>>> take 5 $ dropWhile (<3) $ infiniteSlist [1..]
+Slist {sList = [3,4,5,6,7], sSize = Size 5}
+-}
+dropWhile :: forall a . (a -> Bool) -> Slist a -> Slist a
+dropWhile p Slist{..} = let (s, l) = go 0 sList in
+    Slist l (sSize - Size s)
+  where
+    go :: Int -> [a] -> (Int, [a])
+    go !n [] = (n, [])
+    go !n (x:xs) =
+        if p x
+        then go (n + 1) xs
+        else (n, x:xs)
+{-# INLINE dropWhile #-}
+
+{- | @O(n)@. Returns a tuple where first element is longest prefix (possibly empty)
+of the slist of elements that satisfy the given predicate
+and second element is the remainder of the list.
+
+>>> span (<3) $ slist [1,2,3,4,1,2,3,4]
+(Slist {sList = [1,2], sSize = Size 2},Slist {sList = [3,4,1,2,3,4], sSize = Size 6})
+>>> span (<=10) $ slist [1..3]
+(Slist {sList = [1,2,3], sSize = Size 3},Slist {sList = [], sSize = Size 0})
+>>> span (<0) $ slist [1..3]
+(Slist {sList = [], sSize = Size 0},Slist {sList = [1,2,3], sSize = Size 3})
+
+@
+>> __span (<3) $ 'infiniteSlist' [1..]__
+(Slist {sList = [1,2], sSize = Size 2}, Slist {sList = [3..], sSize = 'Infinity'})
+@
+-}
+span :: forall a . (a -> Bool) -> Slist a -> (Slist a, Slist a)
+span p Slist{..} = let (s, l, r) = go 0 sList in
+    ( Slist l $ Size s
+    , Slist r (sSize - Size s)
+    )
+  where
+    go :: Int -> [a] -> (Int, [a], [a])
+    go !n [] = (n, [], [])
+    go !n (x:xs) =
+        if p x
+        then let (s, l, r) = go (n + 1) xs in (s, x:l, r)
+        else (n, [], x:xs)
+{-# INLINE span #-}
+
+{- | @O(n)@.  Returns a tuple where first element is longest prefix (possibly empty)
+of the slist of elements that /do not/ satisfy the given predicate
+and second element is the remainder of the list.
+
+@
+> break p = 'span' ('not' . p)
+@
+-}
+break :: (a -> Bool) -> Slist a -> (Slist a, Slist a)
+break p = span (not . p)
+{-# INLINE break #-}
+
+{- | @O(m)@. Drops the given prefix from a list.
+It returns 'Nothing' if the slist did not start with the given prefix,
+or 'Just' the slist after the prefix, if it does.
+
+>>> stripPrefix (slist "foo") (slist "foobar")
+Just (Slist {sList = "bar", sSize = Size 3})
+>>> stripPrefix (slist "foo") (slist "foo")
+Just (Slist {sList = "", sSize = Size 0})
+>>> stripPrefix (slist "foo") (slist "barfoo")
+Nothing
+>>> stripPrefix mempty  (slist "foo")
+Just (Slist {sList = "foo", sSize = Size 3})
+>>> stripPrefix (infiniteSlist [0..]) (infiniteSlist [1..])
+Nothing
+
+/Note:/ this function could hang on the infinite slists.
+
+@
+>> __stripPrefix (infiniteSlist [1..]) (infiniteSlist [1..])__
+\</hangs/\>
+@
+
+Use 'safeStripPrefix' instead.
+-}
+stripPrefix :: Eq a => Slist a -> Slist a -> Maybe (Slist a)
+stripPrefix (Slist l1 s1) f@(Slist l2 s2)
+    | s1 == Size 0 = Just f
+    | s1 > s2 = Nothing
+    | otherwise = (\l -> Slist l $ s2 - s1) <$> L.stripPrefix l1 l2
+{-# INLINE stripPrefix #-}
+
+{- | Similar to 'stripPrefix', but never hangs on infinite lists
+and returns 'Nothing' in that case.
+
+>>> safeStripPrefix (infiniteSlist [1..]) (infiniteSlist [1..])
+Nothing
+>>> safeStripPrefix (infiniteSlist [0..]) (infiniteSlist [1..])
+Nothing
+
+-}
+safeStripPrefix :: Eq a => Slist a -> Slist a -> Maybe (Slist a)
+safeStripPrefix (Slist _ Infinity) (Slist _ Infinity) = Nothing
+safeStripPrefix sl1 sl2                               = stripPrefix sl1 sl2
+{-# INLINE safeStripPrefix #-}
+
+{- | @O(n)@. Takes a slist and returns a slist of slists such
+that the concatenation of the result is equal to the argument.
+Moreover, each sublist in the result contains only equal elements.
+
+It is a special case of 'groupBy', which allows
+to supply their own equality test.
+
+>>> group $ slist "Mississippi"
+Slist {sList = [Slist {sList = "M", sSize = Size 1},Slist {sList = "i", sSize = Size 1},Slist {sList = "ss", sSize = Size 2},Slist {sList = "i", sSize = Size 1},Slist {sList = "ss", sSize = Size 2},Slist {sList = "i", sSize = Size 1},Slist {sList = "pp", sSize = Size 2},Slist {sList = "i", sSize = Size 1}], sSize = Size 8}
+>>> group mempty
+Slist {sList = [], sSize = Size 0}
+
+-}
+group :: Eq a => Slist a -> Slist (Slist a)
+group = groupBy (==)
+{-# INLINE group #-}
+
+{- | @O(n)@. Non-overloaded version of the 'group' function.
+
+>>> groupBy (>) $ slist "Mississippi"
+Slist {sList = [Slist {sList = "M", sSize = Size 1},Slist {sList = "i", sSize = Size 1},Slist {sList = "s", sSize = Size 1},Slist {sList = "si", sSize = Size 2},Slist {sList = "s", sSize = Size 1},Slist {sList = "sippi", sSize = Size 5}], sSize = Size 6}
+
+-}
+groupBy :: (a -> a -> Bool) -> Slist a -> Slist (Slist a)
+groupBy p (Slist l Infinity) = infiniteSlist $ P.map slist $ L.groupBy p l
+groupBy p Slist{..}          = slist $ P.map slist $ L.groupBy p sList
+{-# INLINE groupBy #-}
+
+{- | @O(n)@. Returns all initial segments of the argument, shortest first.
+
+>>> inits $ slist "abc"
+Slist {sList = [Slist {sList = "", sSize = Size 0},Slist {sList = "a", sSize = Size 1},Slist {sList = "ab", sSize = Size 2},Slist {sList = "abc", sSize = Size 3}], sSize = Size 4}
+>>> inits mempty
+Slist {sList = [Slist {sList = [], sSize = Size 0}], sSize = Size 1}
+
+-}
+inits :: Slist a -> Slist (Slist a)
+inits (Slist l s) = Slist
+    { sList = L.zipWith Slist (L.inits l) $ sizes s
+    , sSize = s + 1
+    }
+{-# INLINE inits #-}
+
+{- | @O(n)@. Returns all final segments of the argument, shortest first.
+
+>>> tails $ slist "abc"
+Slist {sList = [Slist {sList = "abc", sSize = Size 3},Slist {sList = "bc", sSize = Size 2},Slist {sList = "c", sSize = Size 1},Slist {sList = "", sSize = Size 0}], sSize = Size 4}
+>>> tails mempty
+Slist {sList = [Slist {sList = [], sSize = Size 0}], sSize = Size 1}
+
+-}
+tails :: Slist a -> Slist (Slist a)
+tails (Slist l Infinity) = infiniteSlist $ P.map infiniteSlist (L.tails l)
+tails (Slist l s@(Size n)) = Slist
+    { sList = L.zipWith (\li i -> Slist li $ Size i) (L.tails l) [n, n - 1 .. 0]
+    , sSize = s + 1
+    }
+{-# INLINE tails #-}
+
+{- | @O(m)@.
+Takes two slists and returns 'True' iff the first slist
+is a prefix of the second.
+
+>>> isPrefixOf (slist "Hello") (slist "Hello World!")
+True
+>>> isPrefixOf (slist "Hello World!") (slist "Hello")
+False
+>>> isPrefixOf mempty (slist "hey")
+True
+
+/Note:/ this function could hang on the infinite slists.
+
+@
+>> __isPrefixOf (infiniteSlist [1..]) (infiniteSlist [1..])__
+\</hangs/\>
+@
+
+Use 'safeIsPrefixOf' instead.
+
+-}
+isPrefixOf :: Eq a => Slist a -> Slist a -> Bool
+isPrefixOf (Slist l1 s1) (Slist l2 s2)
+    | s1 > s2 = False
+    | otherwise = L.isPrefixOf l1 l2
+{-# INLINE isPrefixOf #-}
+
+{- | Similar to 'isPrefixOf', but never hangs on infinite lists
+and returns 'False' in that case.
+
+>>> safeIsPrefixOf (infiniteSlist [1..]) (infiniteSlist [1..])
+False
+>>> safeIsPrefixOf (infiniteSlist [0..]) (infiniteSlist [1..])
+False
+-}
+safeIsPrefixOf :: Eq a => Slist a -> Slist a -> Bool
+safeIsPrefixOf sl1@(Slist _ s1) sl2@(Slist _ s2)
+    | s1 == Infinity && s2 == Infinity = False
+    | otherwise = isPrefixOf sl1 sl2
+{-# INLINE safeIsPrefixOf #-}
+
+{- |
+Takes two slists and returns 'True' iff the first slist
+is a suffix of the second.
+
+>>> isSuffixOf (slist "World!") (slist "Hello World!")
+True
+>>> isSuffixOf (slist "Hello World!") (slist "Hello")
+False
+>>> isSuffixOf mempty (slist "hey")
+True
+
+/Note:/ this function hangs if the second slist is infinite.
+
+@
+>> __isSuffixOf /anything/ (infiniteSlist [1..])__
+\</hangs/\>
+@
+
+Use 'safeIsSuffixOf' instead.
+-}
+isSuffixOf :: Eq a => Slist a -> Slist a -> Bool
+isSuffixOf (Slist l1 s1) (Slist l2 s2)
+    | s1 > s2 = False
+    | otherwise = L.isSuffixOf l1 l2
+{-# INLINE isSuffixOf #-}
+
+{- | Similar to 'isSuffixOf', but never hangs on infinite lists
+and returns 'False' in that case.
+
+>>> safeIsSuffixOf (slist [1,2]) (infiniteSlist [1..])
+False
+>>> safeIsSuffixOf (infiniteSlist [1..]) (infiniteSlist [1..])
+False
+-}
+safeIsSuffixOf :: Eq a => Slist a -> Slist a -> Bool
+safeIsSuffixOf sl1 sl2@(Slist _ s2)
+    | s2 == Infinity = False
+    | otherwise = isSuffixOf sl1 sl2
+{-# INLINE safeIsSuffixOf #-}
+
+{- |
+Takes two slists and returns 'True' iff the first slist
+is contained, wholly and intact, anywhere within the second.
+
+>>> isInfixOf (slist "ll") (slist "Hello World!")
+True
+>>> isInfixOf (slist " Hello") (slist "Hello")
+False
+>>> isInfixOf (slist "Hello World!") (slist "Hello")
+False
+
+/Note:/ this function could hang on the infinite slists.
+
+@
+>> __isPrefixOf (infiniteSlist [1..]) (infiniteSlist [1..])__
+\</hangs/\>
+@
+
+Use 'safeIsInfixOf' instead.
+-}
+isInfixOf :: Eq a => Slist a -> Slist a -> Bool
+isInfixOf (Slist l1 s1) (Slist l2 s2)
+    | s1 > s2 = False
+    | otherwise = L.isInfixOf l1 l2
+{-# INLINE isInfixOf #-}
+
+{- | Similar to 'isInfixOf', but never hangs on infinite lists
+and returns 'False' in that case.
+
+>>> safeIsInfixOf (infiniteSlist [1..]) (infiniteSlist [1..])
+False
+>>> safeIsInfixOf (infiniteSlist [0..]) (infiniteSlist [1..])
+False
+-}
+safeIsInfixOf :: Eq a => Slist a -> Slist a -> Bool
+safeIsInfixOf sl1@(Slist _ s1) sl2@(Slist _ s2)
+    | s1 == Infinity && s2 == Infinity = False
+    | otherwise = isInfixOf sl1 sl2
+{-# INLINE safeIsInfixOf #-}
+
+{- |
+Takes two slists and returns 'True' if all the elements
+of the first slist occur, in order, in the second.
+The elements do not have to occur consecutively.
+
+@isSubsequenceOf x y@ is equivalent to @'elem' x ('subsequences' y)@.
+
+>>> isSubsequenceOf (slist "Hll") (slist "Hello World!")
+True
+>>> isSubsequenceOf (slist "") (slist "Hello World!")
+True
+>>> isSubsequenceOf (slist "Hallo") (slist "Hello World!")
+False
+
+/Note:/ this function hangs if the second slist is infinite.
+
+@
+>> __isSuffixOf /anything/ (infiniteSlist [1..])__
+\</hangs/\>
+@
+
+Use 'safeIsSuffixOf' instead.
+-}
+isSubsequenceOf :: Eq a => Slist a -> Slist a -> Bool
+isSubsequenceOf (Slist l1 s1) (Slist l2 s2)
+    | s1 > s2 = False
+    | otherwise = L.isSubsequenceOf l1 l2
+{-# INLINE isSubsequenceOf #-}
+
+{- | Similar to 'isSubsequenceOf', but never hangs on infinite lists
+and returns 'False' in that case.
+
+>>> safeIsSubsequenceOf (infiniteSlist [1..]) (infiniteSlist [1..])
+False
+>>> safeIsSubsequenceOf (infiniteSlist [0..]) (infiniteSlist [1..])
+False
+-}
+safeIsSubsequenceOf :: Eq a => Slist a -> Slist a -> Bool
+safeIsSubsequenceOf sl1@(Slist _ s1) sl2@(Slist _ s2)
+    | s1 == Infinity && s2 == Infinity = False
+    | otherwise = isSubsequenceOf sl1 sl2
+{-# INLINE safeIsSubsequenceOf #-}
+
+----------------------------------------------------------------------------
+-- Searching
+----------------------------------------------------------------------------
+
+{- | @O(n)@.
+Looks up by the given key in the slist of key-value pairs.
+
+>>> lookup 42 $ slist $ [(1, "one"), (2, "two")]
+Nothing
+>>> lookup 42 $ slist $ [(1, "one"), (2, "two"), (42, "life, the universe and everything")]
+Just "life, the universe and everything"
+>>> lookup 1 $ zip (infiniteSlist  [1..]) (infiniteSlist [0..])
+Just 0
+-}
+lookup :: Eq a => a -> Slist (a, b) -> Maybe b
+lookup a = L.lookup a . sList
+{-# INLINE lookup #-}
+
+{- | @O(n)@.
+Returns the slist of the elements that satisfy the given predicate.
+
+>>> filter (<3) $ slist [1..5]
+Slist {sList = [1,2], sSize = Size 2}
+>>> take 5 $ filter odd $ infiniteSlist [1..]
+Slist {sList = [1,3,5,7,9], sSize = Size 5}
+-}
+filter :: forall a . (a -> Bool) -> Slist a -> Slist a
+filter p (Slist l Infinity) = infiniteSlist $ L.filter p l
+filter p Slist{..} = let (newS, newL) = go 0 sList in
+    Slist newL (Size newS)
+  where
+    go :: Int -> [a] -> (Int, [a])
+    go !n [] = (n, [])
+    go n (x:xs) =
+        if p x
+        then second (x:) $ go (n + 1) xs
+        else go n xs
+{-# INLINE filter #-}
+
+{- | @O(n)@.
+Returns the pair of slists of elements which do and do not satisfy
+the given predicate.
+
+>>> partition (<3) $ slist [1..5]
+(Slist {sList = [1,2], sSize = Size 2},Slist {sList = [3,4,5], sSize = Size 3})
+-}
+partition :: forall a . (a -> Bool) -> Slist a -> (Slist a, Slist a)
+partition p (Slist l Infinity) = bimap infiniteSlist infiniteSlist $ L.partition p l
+partition p Slist{..} = let (s1, l1, l2) = go 0 sList in
+    (Slist l1 $ Size s1, Slist l2 $ sSize - Size s1)
+  where
+    go :: Int -> [a] -> (Int, [a], [a])
+    go !n [] = (n, [], [])
+    go n (x:xs) =
+        if p x
+        then first (x:) $ go (n + 1) xs
+        else second (x:) $ go n xs
+{-# INLINE partition #-}
+
+----------------------------------------------------------------------------
+-- Indexing
+----------------------------------------------------------------------------
+
+{- | @O(i) | i < n@ and @O(1) | otherwise@.
+
+Returns the element of the slist at the given position.
+If the @i@ exceeds the length of the slist the function returns 'Nothing'.
+
+>>> let sl = slist [1..10]
+>>> at 0 sl
+Just 1
+>>> at (-1) sl
+Nothing
+>>> at 11 sl
+Nothing
+>>> at 9 sl
+Just 10
+-}
+at :: Int -> Slist a -> Maybe a
+at n Slist{..}
+    | n < 0 || Size n >= sSize = Nothing
+    | otherwise = Just $ sList L.!! n
+{-# INLINE at #-}
+
+{- | @O(min i n)@.
+Unsafe version of the 'at' function.
+If the element on the given position does not exist
+it throws the exception at run-time.
+
+>>> let sl = slist [1..10]
+>>> unsafeAt 0 sl
+1
+>>> unsafeAt (-1) sl
+*** Exception: Prelude.!!: negative index
+>>> unsafeAt 11 sl
+*** Exception: Prelude.!!: index too large
+>>> unsafeAt 9 sl
+10
+-}
+unsafeAt :: Int -> Slist a -> a
+unsafeAt n Slist{..} = sList L.!! n
+{-# INLINE unsafeAt #-}
+
+{- | @O(n)@.
+Returns the index of the first element in the given slist which is equal
+(by '==') to the query element, or 'Nothing' if there is no such element.
+
+>>> elemIndex 5 $ slist [1..10]
+Just 4
+>>> elemIndex 0 $ slist [1..10]
+Nothing
+-}
+elemIndex :: Eq a => a -> Slist a -> Maybe Int
+elemIndex a = L.elemIndex a . sList
+{-# INLINE elemIndex #-}
+
+{- | @O(n)@.
+Extends 'elemIndex', by returning the indices of all elements equal
+to the query element, in ascending order.
+
+>>> elemIndices 1 $ slist [1,1,1,2,2,4,5,1,9,1]
+Slist {sList = [0,1,2,7,9], sSize = Size 5}
+>>> take 5 $ elemIndices 1 $ repeat 1
+Slist {sList = [0,1,2,3,4], sSize = Size 5}
+-}
+elemIndices :: Eq a => a -> Slist a -> Slist Int
+elemIndices a = findIndices (a ==)
+{-# INLINE elemIndices #-}
+
+{- | @O(n)@.
+Returns the index of the first element in the slist satisfying
+the given predicate, or 'Nothing' if there is no such element.
+
+>>> findIndex (>3) $ slist [1..5]
+Just 3
+>>> findIndex (<0) $ slist [1..5]
+Nothing
+-}
+findIndex :: (a -> Bool) -> Slist a -> Maybe Int
+findIndex p = L.findIndex p . sList
+{-# INLINE findIndex #-}
+
+{- | @O(n)@.
+Extends 'findIndex', by returning the indices of all elements
+satisfying the given predicate, in ascending order.
+
+>>> findIndices (<3) $ slist [1..5]
+Slist {sList = [0,1], sSize = Size 2}
+>>> findIndices (<0) $ slist [1..5]
+Slist {sList = [], sSize = Size 0}
+>>> take 5 $ findIndices (<=10) $ infiniteSlist [1..]
+Slist {sList = [0,1,2,3,4], sSize = Size 5}
+-}
+findIndices :: forall a . (a -> Bool) -> Slist a -> Slist Int
+findIndices p (Slist l Infinity) = infiniteSlist $ L.findIndices p l
+findIndices p Slist{..} = let (newS, newL) = go 0 0 sList in
+    Slist newL (Size newS)
+  where
+    go :: Int -> Int -> [a] -> (Int, [Int])
+    go !n _ [] = (n, [])
+    go n !cur (x:xs) =
+        if p x
+        then second (cur:) $ go (n + 1) (cur + 1) xs
+        else go n (cur + 1) xs
+{-# INLINE findIndices #-}
+
+----------------------------------------------------------------------------
+-- Zipping
+----------------------------------------------------------------------------
+
+{- | @O(min n m)@.
+Takes two slists and returns a slist of corresponding pairs.
+
+>>> zip (slist [1,2]) (slist ["one", "two"])
+Slist {sList = [(1,"one"),(2,"two")], sSize = Size 2}
+>>> zip (slist [1,2,3]) (slist ["one", "two"])
+Slist {sList = [(1,"one"),(2,"two")], sSize = Size 2}
+>>> zip (slist [1,2]) (slist ["one", "two", "three"])
+Slist {sList = [(1,"one"),(2,"two")], sSize = Size 2}
+>>> zip mempty (slist [1..5])
+Slist {sList = [], sSize = Size 0}
+>>> zip (infiniteSlist [1..]) (slist ["one", "two"])
+Slist {sList = [(1,"one"),(2,"two")], sSize = Size 2}
+-}
+zip :: Slist a -> Slist b -> Slist (a, b)
+zip (Slist l1 s1) (Slist l2 s2) = Slist
+    { sList = L.zip l1 l2
+    , sSize = min s1 s2
+    }
+{-# INLINE zip #-}
+
+{- | @O(minimum [n1, n2, n3])@.
+Takes three slists and returns a slist of triples, analogous to 'zip'.
+-}
+zip3 :: Slist a -> Slist b -> Slist c -> Slist (a, b, c)
+zip3 (Slist l1 s1) (Slist l2 s2) (Slist l3 s3) = Slist
+    { sList = L.zip3 l1 l2 l3
+    , sSize = minimum [s1, s2, s3]
+    }
+{-# INLINE zip3 #-}
+
+{- | @O(min n m)@.
+Generalises 'zip' by zipping with the given function, instead of a tupling function.
+
+For example, @zipWith (+)@ is applied to two lists to produce the list of corresponding sums.
+-}
+zipWith :: (a -> b -> c) -> Slist a -> Slist b -> Slist c
+zipWith f (Slist l1 s1) (Slist l2 s2) = Slist
+    { sList = L.zipWith f l1 l2
+    , sSize = min s1 s2
+    }
+{-# INLINE zipWith #-}
+
+{- | @O(minimum [n1, n2, n3])@.
+Takes a function which combines three elements, as well as three slists
+and returns a slist of their point-wise combination, analogous to 'zipWith'.
+-}
+zipWith3 :: (a -> b -> c -> d) -> Slist a -> Slist b -> Slist c -> Slist d
+zipWith3 f (Slist l1 s1) (Slist l2 s2) (Slist l3 s3) = Slist
+    { sList = L.zipWith3 f l1 l2 l3
+    , sSize = minimum [s1, s2, s3]
+    }
+{-# INLINE zipWith3 #-}
+
+{- | @O(n)@.
+Transforms a slist of pairs into a slist of first components
+and a slist of second components.
+
+>>> unzip $ slist [(1,"one"),(2,"two")]
+(Slist {sList = [1,2], sSize = Size 2},Slist {sList = ["one","two"], sSize = Size 2})
+-}
+unzip :: Slist (a, b) -> (Slist a, Slist b)
+unzip Slist{..} = let (as, bs) = L.unzip sList in (l as, l bs)
+  where
+    l :: [x] -> Slist x
+    l x = Slist x sSize
+{-# INLINE unzip #-}
+
+{- | @O(n)@.
+Takes a slist of triples and returns three slists, analogous to 'unzip'.
+-}
+unzip3 :: Slist (a, b, c) -> (Slist a, Slist b, Slist c)
+unzip3 Slist{..} = let (as, bs, cs) = L.unzip3 sList in (l as, l bs, l cs)
+  where
+    l :: [x] -> Slist x
+    l x = Slist x sSize
+{-# INLINE unzip3 #-}
+
+----------------------------------------------------------------------------
+-- Sets
+----------------------------------------------------------------------------
+
+{- $sets
+
+Set is a special case of slists so that it consist of the unique elements.
+
+Example of set:
+
+@
+Slist {sList = "qwerty", sSize = Size 6}
+Slist {sList = [1..], sSize = Infinity}
+@
+-}
+
+{- | @O(n^2)@.
+Removes duplicate elements from a slist. In particular,
+it keeps only the first occurrence of each element.
+
+It is a special case of 'nubBy', which allows to supply your own equality test.
+
+>>> nub $ replicate 5 'a'
+Slist {sList = "a", sSize = Size 1}
+>>> nub mempty
+Slist {sList = [], sSize = Size 0}
+>>> nub $ slist [1,2,3,4,3,2,1,2,4,3,5]
+Slist {sList = [1,2,3,4,5], sSize = Size 5}
+-}
+nub :: Eq a => Slist a -> Slist a
+nub = nubBy (==)
+{-# INLINE nub #-}
+
+{- | @O(n^2)@.
+Behaves just like 'nub', except it uses a user-supplied equality predicate
+instead of the overloaded '==' function.
+
+>>> nubBy (\x y -> mod x 3 == mod y 3) $ slist [1,2,4,5,6]
+Slist {sList = [1,2,6], sSize = Size 3}
+-}
+nubBy :: forall a . (a -> a -> Bool) -> Slist a -> Slist a
+nubBy f Slist{..} = let (s, l) = go 0 [] sList in case sSize of
+    Infinity -> infiniteSlist l
+    _        -> Slist l $ Size s
+  where
+    go :: Int -> [a] -> [a] -> (Int, [a])
+    go !n res [] = (n, res)
+    go n res (x:xs) =
+        if any (f x) res
+        then go n res xs
+        else go (n + 1) (res ++ [x]) xs
+{-# INLINE nubBy #-}
+
+{- | @O(n)@.
+Removes the first occurrence of the given element from its slist argument.
+
+>>> delete 'h' $ slist "hahaha"
+Slist {sList = "ahaha", sSize = Size 5}
+>>> delete 0 $ slist [1..3]
+Slist {sList = [1,2,3], sSize = Size 3}
+-}
+delete :: Eq a => a -> Slist a -> Slist a
+delete = deleteBy (==)
+{-# INLINE delete #-}
+
+{- | @O(n)@.
+Behaves like 'delete', but takes a user-supplied equality predicate.
+
+>>> deleteBy (>=) 4 $ slist [1..10]
+Slist {sList = [2,3,4,5,6,7,8,9,10], sSize = Size 9}
+-}
+deleteBy :: forall a . (a -> a -> Bool) -> a -> Slist a -> Slist a
+deleteBy f a (Slist l Infinity) = infiniteSlist $ L.deleteBy f a l
+deleteBy f a Slist{..} = let (del, res) = go sList in
+    Slist res $ sSize - del
+  where
+    go :: [a] -> (Size, [a])
+    go [] = (Size 0, [])
+    go (x:xs) = if f a x
+        then (Size 1, xs)
+        else second (x:) $ go xs
+{-# INLINE deleteBy #-}
+
+{- | @O(n*m)@.
+Takes a predicate and two slists and returns the first slist
+with the first occurrence of each element of the second slist removed.
+
+>>> deleteFirstsBy (==) (slist [1..5]) (slist [2,8,4,10,1])
+Slist {sList = [3,5], sSize = Size 2}
+-}
+deleteFirstsBy :: (a -> a -> Bool) -> Slist a -> Slist a -> Slist a
+deleteFirstsBy f = foldr (deleteBy f)
+{-# INLINE deleteFirstsBy #-}
+
+{- | @O(n*m)@.
+Returns the difference between two slists. The operation is non-associative.
+In the result of @diff xs ys@, the first occurrence of each element of @ys@
+in turn (if any) has been removed from @xs@. Thus
+
+> diff (xs <> ys) ys == xs
+
+>>> diff (slist [1..10]) (slist [1,3..10])
+Slist {sList = [2,4,6,8,10], sSize = Size 5}
+>>> diff (slist [1,3..10]) (slist [2,4..10])
+Slist {sList = [1,3,5,7,9], sSize = Size 5}
+-}
+diff :: Eq a => Slist a -> Slist a -> Slist a
+diff = foldr delete
+{-# INLINE diff #-}
+
+{- | @O(n*m)@.
+Returns the list union of the two slists.
+
+>>> union (slist "pen") (slist "apple")
+Slist {sList = "penal", sSize = Size 5}
+
+Duplicates, and elements of the first slist, are removed from the the second slist,
+but if the first slist contains duplicates, so will the result.
+
+>>> union (slist "apple") (slist "pen")
+Slist {sList = "applen", sSize = Size 6}
+
+It is a special case of 'unionBy'.
+-}
+union :: Eq a => Slist a -> Slist a -> Slist a
+union = unionBy (==)
+{-# INLINE union #-}
+
+{- | @O(n*m)@.
+Non-overloaded version of 'union'.
+-}
+unionBy :: (a -> a -> Bool) -> Slist a -> Slist a -> Slist a
+unionBy f xs ys = xs <> deleteFirstsBy f (nubBy f ys) xs
+{-# INLINE unionBy #-}
+
+{- | @O(n*m)@.
+Returns the slist intersection of two slists.
+
+>>> intersect (slist [1,2,3,4]) (slist [2,4,6,8])
+Slist {sList = [2,4], sSize = Size 2}
+
+If the first list contains duplicates, so will the result.
+
+>>> intersect (slist [1,2,2,3,4]) (slist [6,4,4,2])
+Slist {sList = [2,2,4], sSize = Size 3}
+
+If the first slist is infinite, so will be the result.
+
+If the element is found in both the first and the second slist,
+the element from the first slist will be used.
+
+It is a special case of 'intersectBy'.
+-}
+intersect :: Eq a => Slist a -> Slist a -> Slist a
+intersect = intersectBy (==)
+{-# INLINE intersect #-}
+
+{- | @O(n*m)@.
+Non-overloaded version of 'intersect'.
+-}
+intersectBy :: forall a . (a -> a -> Bool) -> Slist a -> Slist a -> Slist a
+intersectBy _ (Slist _ (Size 0)) _ = mempty
+intersectBy _ _ (Slist _ (Size 0)) = mempty
+intersectBy f (Slist l1 Infinity) (Slist l2 _) = infiniteSlist $ L.intersectBy f l1 l2
+intersectBy f (Slist l1 _) (Slist l2 _) =
+    let (s, l) = go 0 l1 in Slist l $ Size s
+  where
+    go :: Int -> [a] -> (Int, [a])
+    go n [] = (n, [])
+    go n (x:xs) =
+        if any (f x) l2
+        then second (x:) $ go (n + 1) xs
+        else go n xs
+{-# INLINE intersectBy #-}
+
+----------------------------------------------------------------------------
+-- Ordered slists
+----------------------------------------------------------------------------
+
+{- | @O(n log n)@.
+implements a stable sorting algorithm. It is a special case of 'sortBy'.
+
+Elements are arranged from from lowest to highest, keeping duplicates
+in the order they appeared in the input.
+
+>>> sort $ slist [10, 9..1]
+Slist {sList = [1,2,3,4,5,6,7,8,9,10], sSize = Size 10}
+
+/Note:/ this function hangs on infinite slists.
+-}
+sort :: Ord a => Slist a -> Slist a
+sort = sortBy compare
+{-# INLINE sort #-}
+
+{- | @O(n log n)@.
+Non-overloaded version of 'sort'.
+
+>>> sortBy (\(a,_) (b,_) -> compare a b) $ slist [(2, "world"), (4, "!"), (1, "Hello")]
+Slist {sList = [(1,"Hello"),(2,"world"),(4,"!")], sSize = Size 3}
+
+/Note:/ this function hangs on infinite slists.
+-}
+sortBy :: (a -> a -> Ordering) -> Slist a -> Slist a
+sortBy f Slist{..} = Slist (L.sortBy f sList) sSize
+{-# INLINE sortBy #-}
+
+{- | @O(n log n)@.
+Sorts a list by comparing the results of a key function applied to each
+element.  @sortOn f@ is equivalent to @'sortBy' (comparing f)@, but has the
+performance advantage of only evaluating @f@ once for each element in the
+input list.  This is called the decorate-sort-undecorate paradigm, or
+Schwartzian transform.
+
+Elements are arranged from lowest to highest, keeping duplicates in
+the order they appeared in the input.
+
+>>> sortOn fst $ slist [(2, "world"), (4, "!"), (1, "Hello")]
+Slist {sList = [(1,"Hello"),(2,"world"),(4,"!")], sSize = Size 3}
+
+/Note:/ this function hangs on infinite slists.
+-}
+sortOn :: Ord b => (a -> b) -> Slist a -> Slist a
+sortOn f Slist{..} = Slist (L.sortOn f sList) sSize
+{-# INLINE sortOn #-}
+
+{- | @O(n)@.
+Takes an element and a slist and inserts the element into the slist
+at the first position where it is less than or equal to the next element.
+In particular, if the list is sorted before the call, the result will also
+be sorted. It is a special case of 'insertBy'.
+
+>>> insert 4 $ slist [1,2,3,5,6]
+Slist {sList = [1,2,3,4,5,6], sSize = Size 6}
+-}
+insert :: Ord a => a -> Slist a -> Slist a
+insert = insertBy compare
+{-# INLINE insert #-}
+
+-- | @O(n)@. The non-overloaded version of 'insert'.
+insertBy :: (a -> a -> Ordering) -> a -> Slist a -> Slist a
+insertBy f a Slist{..} = Slist (L.insertBy f a sList) (sSize + 1)
+{-# INLINE insertBy #-}
diff --git a/src/Slist/Size.hs b/src/Slist/Size.hs
new file mode 100644
--- /dev/null
+++ b/src/Slist/Size.hs
@@ -0,0 +1,92 @@
+-- | Lists size representation
+
+module Slist.Size
+       ( Size (..)
+       , sizes
+       ) where
+
+
+{- | Data type that represents lists size/lengths.
+
++-----------+----------+------------+
+| List      | @length@ | Size       |
++===========+==========+============+
+| @[]@      | @0@      | @Size 0@   |
++-----------+----------+------------+
+| @[1..10]@ | @10@     | @Size 10@  |
++-----------+----------+------------+
+| @[1..]@   | /hangs/  | @Infinity@ |
++-----------+----------+------------+
+
+Note, that size is not suppose to have negative value, so use
+the 'Size' constructor carefully.
+-}
+data Size
+    -- | Finite size
+    = Size !Int
+    -- | Infinite size.
+    | Infinity
+    deriving (Show, Read, Eq, Ord)
+
+{- | Efficient implementations of numeric operations with 'Size's.
+
+Any operations with 'Infinity' size results into 'Infinity'.
+
+TODO: checking on overflow when '+' or '*' sizes.
+-}
+instance Num Size where
+    (+) :: Size -> Size -> Size
+    Infinity + _ = Infinity
+    _ + Infinity = Infinity
+    (Size x) + (Size y) = Size $ x + y
+    {-# INLINE (+) #-}
+
+    (-) :: Size -> Size -> Size
+    Infinity - _ = Infinity
+    _ - Infinity = Infinity
+    (Size x) - (Size y) = Size (x - y)
+    {-# INLINE (-) #-}
+
+    (*) :: Size -> Size -> Size
+    Infinity * _ = Infinity
+    _ * Infinity = Infinity
+    (Size x) * (Size y) = Size (x * y)
+    {-# INLINE (*) #-}
+
+    abs :: Size -> Size
+    abs Infinity = Infinity
+    abs (Size x) = Size $ abs x
+    {-# INLINE abs #-}
+
+    signum :: Size -> Size
+    signum Infinity = Infinity
+    signum (Size x) = Size (signum x)
+    {-# INLINE signum #-}
+
+    fromInteger :: Integer -> Size
+    fromInteger = Size . fromInteger
+    {-# INLINE fromInteger #-}
+
+{- | The minimum possible size for the list is empty list: @Size 0@
+The maximum possible size is 'Infinity'.
+-}
+instance Bounded Size where
+    minBound :: Size
+    minBound = Size 0
+
+    maxBound :: Size
+    maxBound = Infinity
+
+{- | Returns the list of sizes from zero to the given one (including).
+
+>>> sizes $ Size 3
+[Size 0,Size 1,Size 2,Size 3]
+
+@
+>> __sizes Infinity__
+[Size 0, Size 1, ..., Size 'maxBound', Infinity]
+@
+-}
+sizes :: Size -> [Size]
+sizes (Size n) = map Size [0..n]
+sizes Infinity = map Size [0..maxBound] ++ [Infinity]
diff --git a/test/Doctest.hs b/test/Doctest.hs
new file mode 100644
--- /dev/null
+++ b/test/Doctest.hs
@@ -0,0 +1,13 @@
+module Main (main) where
+
+import System.FilePath.Glob (glob)
+import Test.DocTest (doctest)
+
+main :: IO ()
+main = do
+    sourceFiles <- glob "src/**/*.hs"
+    doctest
+        $ "-XInstanceSigs"
+        : "-XScopedTypeVariables"
+        : "-XRecordWildCards"
+        : sourceFiles
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,4 @@
+module Main (main) where
+
+main :: IO ()
+main = putStrLn ("Test suite not yet implemented" :: String)
