diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2026, Nikita Volkov
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/quickcheck-extras.cabal b/quickcheck-extras.cabal
new file mode 100644
--- /dev/null
+++ b/quickcheck-extras.cabal
@@ -0,0 +1,99 @@
+cabal-version: 3.0
+name: quickcheck-extras
+version: 0.1
+category: Testing, Utilities, QuickCheck
+synopsis: Extra utilities for QuickCheck
+homepage: https://github.com/nikita-volkov/quickcheck-extras
+bug-reports: https://github.com/nikita-volkov/quickcheck-extras/issues
+author: Nikita Volkov <nikita.y.volkov@mail.ru>
+maintainer: Nikita Volkov <nikita.y.volkov@mail.ru>
+copyright: (c) 2026, Nikita Volkov
+license: MIT
+license-file: LICENSE
+extra-doc-files:
+  LICENSE
+
+source-repository head
+  type: git
+  location: https://github.com/nikita-volkov/quickcheck-extras
+
+common base
+  default-language: Haskell2010
+  default-extensions:
+    ApplicativeDo
+    BangPatterns
+    BinaryLiterals
+    BlockArguments
+    ConstraintKinds
+    DataKinds
+    DefaultSignatures
+    DeriveDataTypeable
+    DeriveFoldable
+    DeriveFunctor
+    DeriveTraversable
+    DerivingStrategies
+    DerivingVia
+    DuplicateRecordFields
+    EmptyDataDecls
+    FlexibleContexts
+    FlexibleInstances
+    FunctionalDependencies
+    GADTs
+    GeneralizedNewtypeDeriving
+    LambdaCase
+    LiberalTypeSynonyms
+    MagicHash
+    MultiParamTypeClasses
+    MultiWayIf
+    NamedFieldPuns
+    NoFieldSelectors
+    NoImplicitPrelude
+    NoMonomorphismRestriction
+    NumericUnderscores
+    OverloadedRecordDot
+    OverloadedStrings
+    ParallelListComp
+    PatternGuards
+    QuasiQuotes
+    RankNTypes
+    RecordWildCards
+    ScopedTypeVariables
+    StandaloneDeriving
+    StrictData
+    TemplateHaskell
+    TupleSections
+    TypeApplications
+    TypeFamilies
+    TypeOperators
+    UnboxedTuples
+    ViewPatterns
+
+common executable
+  import: base
+  ghc-options:
+    -O2
+    -threaded
+    -with-rtsopts=-N
+    -rtsopts
+    -funbox-strict-fields
+
+common test
+  import: base
+  ghc-options:
+    -threaded
+    -with-rtsopts=-N
+
+library
+  import: base
+  hs-source-dirs: src/library
+  exposed-modules:
+    QuickCheckExtras.Arbitrary
+    QuickCheckExtras.Gen
+    QuickCheckExtras.Random
+
+  other-modules:
+  build-depends:
+    QuickCheck >=2.14 && <3,
+    base >=4.11 && <5,
+    containers >=0.6 && <0.9,
+    splitmix ^>=0.1,
diff --git a/src/library/QuickCheckExtras/Arbitrary.hs b/src/library/QuickCheckExtras/Arbitrary.hs
new file mode 100644
--- /dev/null
+++ b/src/library/QuickCheckExtras/Arbitrary.hs
@@ -0,0 +1,8 @@
+module QuickCheckExtras.Arbitrary where
+
+import qualified QuickCheckExtras.Gen as Gen
+import Test.QuickCheck
+import Prelude
+
+fromInt :: (Arbitrary a) => Int -> a
+fromInt = Gen.run arbitrary 10
diff --git a/src/library/QuickCheckExtras/Gen.hs b/src/library/QuickCheckExtras/Gen.hs
new file mode 100644
--- /dev/null
+++ b/src/library/QuickCheckExtras/Gen.hs
@@ -0,0 +1,41 @@
+module QuickCheckExtras.Gen where
+
+import Data.Function
+import qualified Data.Set as Set
+import qualified QuickCheckExtras.Random
+import Test.QuickCheck
+import Test.QuickCheck.Gen
+import qualified Test.QuickCheck.Random as Random
+import Prelude hiding (filter)
+
+-- * Execution
+
+-- | Execute the QuickCheck generator deterministically.
+run ::
+  Gen a ->
+  -- | Size.
+  Int ->
+  -- | Seed.
+  Int ->
+  a
+run gen size seed = unGen gen (Random.mkQCGen seed) size
+
+-- * Combinators
+
+filter :: (a -> Bool) -> Gen a -> Gen a
+filter = flip suchThat
+
+setOfSize :: (Ord a) => Int -> Gen a -> Gen (Set.Set a)
+setOfSize n elementGen = go Set.empty 0
+  where
+    go !currentSet !size
+      | size >= n = pure currentSet
+      | otherwise = do
+          newElement <- suchThat elementGen \element -> Set.notMember element currentSet
+          go (Set.insert newElement currentSet) (succ size)
+
+-- | Get the current seed from the underlying generator.
+getSeed :: Gen Int
+getSeed =
+  MkGen \rnd _size ->
+    QuickCheckExtras.Random.toSeed rnd
diff --git a/src/library/QuickCheckExtras/Random.hs b/src/library/QuickCheckExtras/Random.hs
new file mode 100644
--- /dev/null
+++ b/src/library/QuickCheckExtras/Random.hs
@@ -0,0 +1,14 @@
+-- |
+-- Refs:
+-- - https://github.com/nick8325/quickcheck/blob/master/src/Test/QuickCheck/Random.hs
+-- - https://hackage-content.haskell.org/package/splitmix-0.1.3.1/docs/System-Random-SplitMix.html
+module QuickCheckExtras.Random where
+
+import qualified System.Random.SplitMix
+import Test.QuickCheck.Random
+import Prelude
+
+toSeed :: QCGen -> Int
+toSeed (QCGen smGen) =
+  let (seed, _gamma) = System.Random.SplitMix.unseedSMGen smGen
+   in fromIntegral seed
