packages feed

grfn (empty) → 1.0.0.0

raw patch · 9 files changed

+646/−0 lines, 9 filesdep +QuickCheckdep +arithmoidep +asyncsetup-changed

Dependencies added: QuickCheck, arithmoi, async, base, grfn, hspec, hspec-core, monad-loops, parallel, parallel-io, primes, protolude, random, tasty, tasty-bench, text, time, unamb

Files

+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# Changelog for `grfn`++All notable changes to this project will be documented in this file.++The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),+and this project adheres to the+[Haskell Package Versioning Policy](https://pvp.haskell.org/).++## Unreleased++## 0.1.0.0 - YYYY-MM-DD
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright VENKATESH NARAYANAN (c) 2024++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,41 @@+# grfn+![GitHub CI](https://img.shields.io/github/actions/workflow/status/threeeyedgod/GRFN/ci.yml)+[![MPL-2.0 license](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](https://github.com/threeeyedgod/GRFN/blob/main/LICENSE)+[![Stable Version](https://img.shields.io/github/v/tag/ThreeEyedGod/GRFN)](https://img.shields.io/github/v/tag/ThreeEyedGod/grfn)+[![Latest Release](https://img.shields.io/github/v/release/ThreeEyedGod/GRFN?color=%233D9970)](https://img.shields.io/github/v/release/ThreeEyedGod/grfn?color=%233D9970)+[![Hackage](https://img.shields.io/hackage/v/grfn.svg)](https://hackage.haskell.org/package/grfn)++Synopsis+---------++Implementation of this paper ["Get pre-factored random numbers easily"](https://twitter.com/michael_nielsen/status/1724854680990486780?s=20). The full paper may be read [here](https://link.springer.com/content/pdf/10.1007/s00145-003-0051-5.pdf).  A synopsis is available in Section 2 in this other [paper](https://math.dartmouth.edu/~carlp/kalai3.pdf) dealing with getting pre-factored random numbers for Gausian distributions.  A reference Python implemention is [here](https://www.johndcook.com/blog/2023/11/17/factored-random-numbers/).++The Adam Kalai algorithm itself is an easier (but less efficient) version of [Eric Bach's original algorithm](https://pages.cs.wisc.edu/~cs812-1/pfrn.pdf). ++### Notes+**Parallelized / Concurrent** ; Property Testing (QuickCheck), [Stan](https://hackage.haskell.org/package/stan) for Static analysis+hlint; github actions, IDE:Cursor+ormolu ; Haddock ; makefile; Benchmark (tasty) ; Verification using Refinement Types (LiquidHaskell) during development ; HPC code coverage enabled; cabal/stack profiling; Kleisli Applicative++### Performance+Development on an entry level M1 ==> Ghc settings and usable cores (rtsopts as well) set to 4.++### Usage++__Example:__ a single pre-factored number guaranteed with uniform probability may +be obtained by one of these 3 calls.  ++⚠️ Note: preFactoredNumOfBitSizePar is a concurrent +parallized implementation and may offer performance benefits.++    >>> genARandomPreFactoredNumberLTEn 20 -- will give a pre-factored number less +    than or equal to 20.+    >>> Right (8,[2,2,2,1])++    >>> preFactoredNumOfBitSize 20 -- will give a pre-factored number in the +    range [2^20, 2^21 - 1]+    >>> Right (1695177,[17123,11,3,3,1])++    >>> preFactoredNumOfBitSizePar 60 -- will give a pre-factored number in the +    range [2^60, 2^61 - 1]+    >>> Right (1245467344549977447,[332515759,233924281,179,19,3,3,1])+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Main.hs view
@@ -0,0 +1,58 @@+module Main (main) where++import Data.Time.Clock+  ( NominalDiffTime,+    diffUTCTime,+    getCurrentTime,+  )+import FactoredRandomNumbers+  ( preFactoredNumOfBitSize,+    preFactoredNumOfBitSizePar,+  )+import GHC.Environment (getFullArgs)++main :: IO ()+main = do+  putStrLn "Hello"+  putStrLn "Searching all args..."+  args <- getFullArgs+  print args+  let sizeExponent = 92 :: Integer+  putStrLn "               "+  putStrLn "Parallel --- BitSize Range 90"+  x <- timeit (preFactoredNumOfBitSize sizeExponent)+  print x+  putStrLn "Parallel-Concurrent -- Bitsize Range 90"+  y <- timeit (preFactoredNumOfBitSizePar sizeExponent)+  print y+  putStrLn "Ratio of Parallel - Concurrent / Parallel "+  print $ snd y / snd x+  putStrLn "------------"++  putStrLn "Parallel --- BitSize Range 45"+  x1 <- timeit (preFactoredNumOfBitSize $ sizeExponent `div` 2)+  print x1+  putStrLn "Parallel-Concurrent -- Bitsize Range 45"+  y1 <- timeit (preFactoredNumOfBitSizePar $ sizeExponent `div` 2)+  print y1+  putStrLn "Ratio of Parallel - Concurrent / Parallel "+  print $ snd y1 / snd x1+  putStrLn "------------"++  putStrLn "Parallel --- BitSize Range 22"+  x2 <- timeit (preFactoredNumOfBitSize $ sizeExponent `div` 4)+  print x2+  putStrLn "Parallel-Concurrent -- Bitsize Range 22"+  y2 <- timeit (preFactoredNumOfBitSizePar $ sizeExponent `div` 4)+  print y2+  putStrLn "Ratio of Parallel - Concurrent / Parallel "+  print $ snd y2 / snd x2+  putStrLn "------------"++-- | Helper function+timeit :: IO a -> IO (Maybe a, NominalDiffTime)+timeit action = do+  start <- getCurrentTime+  value <- action+  end <- getCurrentTime+  pure (Just value, diffUTCTime end start)
+ bench/BenchFactoredRandomNumbers.hs view
@@ -0,0 +1,18 @@+import FactoredRandomNumbers+import Test.Tasty.Bench++main :: IO ()+main =+  Test.Tasty.Bench.defaultMain+    [ bgroup+        "PreFactored Uniform Random Numbers -Integers"+        [ bench "2^92-Parallel+Concurrent " $ whnfIO (preFactoredNumOfBitSizePar 92),+          bench "2^92-Parallel+Concurrent " $ whnfIO (preFactoredNumOfBitSize 92)+        ],+      bgroup+        "PreFactored Uniform Random Numbers -Max Int"+        [ bench "2^62-Parallel+Concurrent " $ whnfIO (preFactoredNumOfBitSizePar 62),+          bench "2^62-Parallel+Concurrent " $ whnfIO (preFactoredNumOfBitSize 62),+          bench "10^8 Parallel " $ whnfIO (genARandomPreFactoredNumberLTEn (10 ^ 8))+        ]+    ]
+ grfn.cabal view
@@ -0,0 +1,132 @@+cabal-version:       3.6+name:                grfn+version:             1.0.0.0+homepage:            https://github.com/threeeyedgod/grfn#readme+license:             BSD-3-Clause+license-file:        LICENSE+author:              Venkatesh Narayanan+maintainer:          venkatesh.narayanan@live.in+copyright:           2024 ThreeEyedGod+synopsis:            Uniformly-random pre-factored numbers (Kalai)+description:+    @__grfn__@ is an focused library. @grfn@ is an implementation of Adam Kalai's algorithm +    to get uniform pre-factored numbers. +    See [README](https://github.com/threeeyedgod/grfn#grfn) for more details.    ++    /Example:/ a single pre-factored number guaranteed with uniform probability may be obtained by one of these 3 calls.  +    preFactoredNumOfBitSizePar is a concurrent parallized implementation and may offer performance ++      >>> genARandomPreFactoredNumberLTEn 20 -- will give a pre-factored number less than or equal to 20.+      >>> Right (8,[2,2,2,1])++      >>> preFactoredNumOfBitSize 20 -- will give a pre-factored number in the range [2^20, 2^21 - 1]+      >>> Right (1695177,[17123,11,3,3,1])++      >>> preFactoredNumOfBitSizePar 60 -- will give a pre-factored number in the range [2^60, 2^61 - 1]+      >>> Right (1245467344549977447,[332515759,233924281,179,19,3,3,1])+    +category:            Algorithm, Random, Numbers+build-type:          Simple+stability:           experimental+extra-source-files:  README.md+extra-doc-files:+                     CHANGELOG.md+tested-with:         GHC == 9.8.2+                     GHC == 9.6.5++source-repository head+  type:     git+  location: https://github.com/threeeyedgod/grfn++library+  hs-source-dirs:      src+  exposed-modules:     FactoredRandomNumbers+  build-depends:      +                       base >= 4.18.2 && <= 4.20.0.1+                      ,protolude ^>= 0.3.4+                      ,text >= 2.0.2 && <= 2.1.1+                      ,random >=1.2.1.2 && < 1.3 +                      ,arithmoi >= 0.13.0 && < 0.14+                      ,monad-loops >= 0.4.3 && < 3.3 +                      ,parallel-io >= 0.3.5 && < 0.4 +                      ,async >=2.2.5 && < 2.3 +                      ,parallel >=3.2.2 && < 3.3+                      ,unamb ^>= 0.2.7+  default-language:    Haskell2010+  ghc-options:         -Wall+                       -Wcompat+                       -Widentities+                       -Wincomplete-record-updates+                       -Wincomplete-uni-patterns+                       -Wmissing-export-lists+                       -Wmissing-home-modules+                       -Wpartial-fields+                       -Wredundant-constraints+                       -fwrite-ide-info+                       -hiedir=.hie+                       -threaded+                       -Wunused-packages++executable grfn-exe+  hs-source-dirs:      app+  main-is:             Main.hs+  build-depends:       base+                     , grfn+                     , time >= 1.12.2 && < 1.13++  default-language:    Haskell2010+  ghc-options:         -Wall+                       -Wcompat+                       -Widentities+                       -Wincomplete-record-updates+                       -Wincomplete-uni-patterns+                       -Wmissing-export-lists+                       -Wmissing-home-modules+                       -Wpartial-fields+                       -Wredundant-constraints+                       -threaded+                       -rtsopts+                       -with-rtsopts=-N4++test-suite grfn-test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  build-depends:       base+                     , grfn+                     , QuickCheck >= 2.14.3+                     , primes+                     , text >= 2.0.2 && <= 2.1.1+                     , hspec >= 2.11.7+                     , hspec-core >= 2.11.7++  default-language:    Haskell2010+  ghc-options:         +                       -- -Wall+                       -Wcompat+                       -Widentities+                       -Wincomplete-record-updates+                       -Wincomplete-uni-patterns+                       -Wmissing-export-lists+                       -Wmissing-home-modules+                       -Wpartial-fields+                       -Wredundant-constraints+                       -Wno-x-partial+                       -threaded+                       -rtsopts+                       -with-rtsopts=-N++benchmark grfn-benchmark+  type:                exitcode-stdio-1.0+  hs-source-dirs:      bench+  main-is:             BenchFactoredRandomNumbers.hs+  build-depends:       base+                     , tasty-bench+                     , grfn+                     , tasty >= 1.5+  default-language:    Haskell2010+  ghc-options:+                       -fproc-alignment=64 +                       -rtsopts+                       -threaded+                       -with-rtsopts=-N4
+ src/FactoredRandomNumbers.hs view
@@ -0,0 +1,218 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE UnicodeSyntax #-}+{-- |+-- Module: FactoredRandomNumbers+-- Description: A module of Kalai's algorithm to get uniformly pre-factored numbers+-- Copyright: (c) Venkatesh Narayanan+-- License:+-- Maintainer: venkatesh.narayanan@live.in+--+-- The module has three functions.+   The Adam Kalai Algorithm implmemented in this module (see Readme for more details)+              Input: Integer n > 0.++              Output: A uniformly random number 1 ≤ r ≤ n.++                  1. Generate a sequence n ≥ s1 ≥ s2 ≥ ··· ≥ sl = 1 by choosing+                  s1 ∈ {1, 2,..., n} and si+1 ∈ {1, 2,...,si}, until reaching 1.+                  2. Let r be the product of the prime si’s.+                  3. If r ≤ n, output r with probability r/n.+                  4. Otherwise, RESTART.+--}+{-# LANGUAGE NoImplicitPrelude #-}++-- | Module for accessing functions based on Kalai's Algorithm+module FactoredRandomNumbers+  ( genARandomPreFactoredNumberLTEn,+    preFactoredNumOfBitSize,+    preFactoredNumOfBitSizePar,+  )+where++import Control.Concurrent.Async (race)+import Control.Concurrent.ParallelIO.Local (parallelFirst, withPool)+import Control.Monad.Loops (iterateWhile)+import Control.Parallel.Strategies (NFData, parBuffer, parListChunk, parListSplitAt, rdeepseq, rpar, withStrategy)+import Data.Maybe (fromMaybe)+import Data.Text (pack)+import qualified Data.Unamb (race)+import GHC.Conc (getNumCapabilities, getNumProcessors, setNumCapabilities)+import Math.NumberTheory.Primes.Testing (bailliePSW) -- isPrime is slower+import Protolude+  ( Applicative (pure),+    Bool (False),+    Either (..),+    Eq ((==)),+    Foldable (maximum),+    IO,+    Int,+    Integer,+    Maybe (Just),+    Monad ((>>=)),+    Num ((+), (-)),+    Ord (max, min, (<), (<=), (>)),+    Text,+    abs,+    div,+    filter,+    flip,+    fst,+    length,+    maximum,+    odd,+    otherwise,+    product,+    replicate,+    snd,+    ($),+    (&&),+    (.),+    (<$>),+    (<&>),+    (<*>),+    (>=>),+    (^),+    (||),+  )+import System.Random.Stateful (globalStdGen, uniformRM)++-- | Strategies that may be used with parallel calls+data Strats+  = Chunk+  | Buffer+  | Split+  deriving (Eq)++-- | Takes an Integer for Bitsize value to operate on range [2 ^ y, 2 ^ y + 1 - 1].  This function leverages parallel execution+-- Provide an integer input and it should generate a tuple of a number in the range [2^y, 2^y+1 -1] and its prime factors+-- In the event that the concurrent call fails, a recovery through a basic parallelised call is attempted.+preFactoredNumOfBitSizePar :: Integer -> IO (Either Text (Integer, [Integer]))+preFactoredNumOfBitSizePar 1 = pure $ Right (1, [1])+preFactoredNumOfBitSizePar n | n > 1 = fromMaybe <$> preFactoredNumOfBitSize n <*> preFactoredNumOfBitSizeParMaybe n+preFactoredNumOfBitSizePar _ = pure $ Left $ pack "Invalid"++-- | Parallel preFactored Number given BitSize+-- Provide an integer input. Generate a tuple of a number in the range [2^y, 2^y+1 -1] and its prime factors.+preFactoredNumOfBitSizeParMaybe :: Integer -> IO (Maybe (Either Text (Integer, [Integer])))+preFactoredNumOfBitSizeParMaybe 1 = pure $ Just $ Right (1, [1])+preFactoredNumOfBitSizeParMaybe n | n > 1 && n < (10 :: Integer) ^ (9 :: Integer) = Just <$> preFactoredNumOfBitSize n+preFactoredNumOfBitSizeParMaybe n | n > 1 = (snd <$> coresToUse) >>= spinUpThreads (preFactoredNumOfBitSize n)+preFactoredNumOfBitSizeParMaybe _ = pure $ Just $ Left $ pack "Invalid"++-- | Spin up t threads of function f in parallel and return the one which completes first+spinUpThreads :: IO a -> Int -> IO (Maybe a)+spinUpThreads f t = withPool t $ \pool -> parallelFirst pool $ replicate t (Just <$> f)++-- | Spin up t actions of function f in parallel and return what's executed first+-- for now ignore t; fires up a 2 horse 'race' call from Control.Concurrent.Async+_spinUpActions :: IO a -> Int -> IO (Maybe a)+_spinUpActions f _ = _raceJust f++-- | Spin up t Forks of function f in parallel and return what's executed first+-- for now ignore the second paramemter; fires up a 2-horse 'race' call from Data.Unamb+_spinUpForks :: IO a -> Int -> IO (Maybe a)+_spinUpForks f _ = _raceJustU f++-- | Convert async.race from Either-Or to Maybe+_raceJust :: IO a -> IO (Maybe a)+_raceJust a =+  race a a >>= \case+    Left u -> pure $ Just u+    Right v -> pure $ Just v++-- | Convert Data.Unamb.race to Maybe+_raceJustU :: IO a -> IO (Maybe a)+_raceJustU a = Just <$> Data.Unamb.race a a++-- | Figure out # cores to use for parallelization+coresToUse :: IO (Int, Int)+coresToUse = do+  nCores <- getNumProcessors+  let nEfficiencyCores = nCores `div` 2 -- a heuristic : 50% of cores = efficiency+  nNumCapabilities <- getNumCapabilities+  setNumCapabilities $ max (nCores - nEfficiencyCores) nNumCapabilities+  nNumCapabilitiesSet <- getNumCapabilities+  pure (nCores, nNumCapabilitiesSet)++-- | Takes an Integer as a Bitsize value to operate on range [2 ^ y, 2 ^ y + 1 - 1]+-- Provide an integer input and it should generate a tuple of a number in the range [2^y, 2^y+1 -1] and its prime factors.+-- if it throws up a value below 2^n then do again. 50% of the time it should result in success.+preFactoredNumOfBitSize :: Integer -> IO (Either Text (Integer, [Integer]))+preFactoredNumOfBitSize 1 = pure $ Right (1, [1])+preFactoredNumOfBitSize n | n > 1 = iterateWhile ((2 ^ n) <|) (genARandomPreFactoredNumberLTEn (2 ^ (n + 1) - 1))+preFactoredNumOfBitSize _ = pure $ Left $ pack "Invalid"++infix 1 <|++-- | An operator to compare the Right first value of the (Int, [Int]) to an Int for Truth-value of lesser-than predicate+(<|) :: Integer -> Either Text (Integer, [Integer]) -> Bool+bound <| eOR = case eOR of+  Left _ -> False+  Right v -> fst v < bound++-- | This is the Entry Function with a Integer bound. This is the core of the Kalai algorithm+-- Provide an integer input and it should generate a tuple of a number less than the input integer and its prime factors+genARandomPreFactoredNumberLTEn :: Integer -> IO (Either Text (Integer, [Integer]))+genARandomPreFactoredNumberLTEn x | x <= 0 = pure $ Left $ pack "Invalid"+genARandomPreFactoredNumberLTEn 1 = pure $ Right (1, [1])+genARandomPreFactoredNumberLTEn n = do+  candidateTuple <- potentialResult n+  if' ((fst `is` (< n)) candidateTuple) (pure $ Right candidateTuple) (genARandomPreFactoredNumberLTEn n) -- else keep doing till success++-- | Provided an Integer List, throws up a candidate Int and its prime factors for further assessment+filterPrimesProduct :: [Integer] -> (Integer, [Integer])+filterPrimesProduct xs = result where result@(_, sq) = (product sq, onlyPrimesFrom xs) -- note: product [] = 1++-- | parallel filter with 3 optional strategies+parFilter :: (NFData a) => Strats -> Int -> (a -> Bool) -> [a] -> [a]+parFilter strat stratParm p = case strat of+  Chunk -> withStrategy (parListChunk stratParm rdeepseq) . filter p+  Buffer -> withStrategy (parBuffer stratParm rpar) . filter p+  Split -> withStrategy (parListSplitAt stratParm rdeepseq rdeepseq) . filter p++-- | Reduction of a composite list of integers into primefactors+-- Select the parallel (or not) strategy based on the size range. Use Parallel > Billion+onlyPrimesFrom :: [Integer] -> [Integer]+onlyPrimesFrom xs+  | maximum xs < (10 :: Integer) ^ (9 :: Integer) = filter isPrimeOr1 xs -- at a billion try parallelzing and concurrency options+  | otherwise = parFilter Split (length xs `div` 3) isPrimeOr1 xs++-- assuming the first 3rd of the list comprise larger numbers and their processing workload = the residual 2/3rd++-- | Provided an Integer, throws up a candidate Int and its factors for further evaluation+potentialResult :: Integer -> IO (Integer, [Integer])+potentialResult n = mkList n <&> filterPrimesProduct++-- | Provided an Integer, creates a sequence of random integers LTE n in decreasing order,+-- possibly with multiples ending at 1+mkList :: Integer -> IO [Integer]+mkList 1 = pure []+mkList n = (getRndMInt >=>: mkList) (1, n)++-- | Get a Random Integer with uniform probability in the range (l,u)+getRndMInt :: (Integer, Integer) -> IO Integer+getRndMInt (l, u) = max l . min u <$> uniformRM (l, u) globalStdGen -- uniformRM (a, b) = uniformRM (b, a) holds as per fn defn++infixr 1 >=>:++-- | Left-to-right Kleisli composition of monads plus prepend elem to List using applicative+-- Late edit: there may be something in Control.arrow that already does exactly this+(>=>:) :: (Monad m) => (a -> m b) -> (b -> m [b]) -> (a -> m [b])+f >=>: g = f >=> \u -> (u :) <$> g u++-- | True if input is prime or 1+-- Primality testing is one key to peformance of this algo+-- the isOdd and greater than 3 is for the use of bailliePSW primality+-- using bailliePSW in place of the standard isPrime leads to 75% reduction in time !!!+isPrimeOr1 :: Integer -> Bool+isPrimeOr1 n = (i == 1 || i == 3) || (i > 3 && odd i && bailliePSW i) where i = abs n -- bailliePSW requires that n > 3 and that input is Odd++-- | from Data.Function.predicate+is :: (a -> b) -> (b -> Bool) -> (a -> Bool)+is = flip (.)++-- | @if then else@ made concise+if' :: Bool -> b -> b -> b+if' p u v+  | p = u+  | otherwise = v
+ test/Spec.hs view
@@ -0,0 +1,136 @@+import Data.Numbers.Primes (primeFactors, primes)+import Data.Text (pack)+import FactoredRandomNumbers+import Test.Hspec (Spec, describe, hspec)+import Test.Hspec.Core.QuickCheck (modifyMaxDiscardRatio, modifyMaxSuccess)+import Test.Hspec.QuickCheck (prop)+import Test.QuickCheck (Arbitrary, Args (..), Gen, Negative (..), NonNegative (..), Positive (..), Property, arbitrary, chatty, choose, classify, collect, counterexample, cover, elements, expectFailure, forAll, forAllProperties, listOf, printTestCase, quickCheck, quickCheckWithResult, suchThat, verbose, verboseCheckWithResult, withMaxSuccess, (==>))+import Test.QuickCheck.Monadic (assert, monadicIO, run)++main :: IO ()+main = hspec $ do+  libH++libH :: Spec+libH = describe "All Property Tests" $ do+  libHProperty8+  libHProperty9+  libHProperty10+  libHProperty11+  libHProperty12+  libHProperty13+  libHProperty14++libHProperty8 :: Spec+libHProperty8 = do+  modifyMaxSuccess (const 100) $+    prop+      "prop_checkIfLTEn"+      prop_checkIfLTEn++libHProperty9 :: Spec+libHProperty9 = do+  modifyMaxSuccess (const 5) $+    modifyMaxDiscardRatio (const 10) $+      prop+        "prop_checkIffiltersInValidInput"+        prop_checkIffiltersInValidInput++libHProperty10 :: Spec+libHProperty10 = do+  modifyMaxSuccess (const 100) $+    prop+      "prop_checkValidOutput"+      prop_checkValidOutput++libHProperty11 :: Spec+libHProperty11 = do+  modifyMaxSuccess (const 100) $+    prop+      "prop_checkAccurateOutput"+      prop_checkAccurateOutput++libHProperty12 :: Spec+libHProperty12 = do+  modifyMaxSuccess (const 100) $+    prop+      "prop_checkAccurateOutputVal"+      prop_checkAccurateOutputVal++libHProperty13 :: Spec+libHProperty13 = do+  modifyMaxSuccess (const 2) $+    modifyMaxDiscardRatio (const 50) $+      prop+        "prop_checkAccurateOutputValBitSize"+        prop_checkAccurateOutputValBitSize++libHProperty14 :: Spec+libHProperty14 = do+  modifyMaxSuccess (const 2) $+    modifyMaxDiscardRatio (const 50) $+      prop+        "prop_checkAccurateOutputValBitSizePar"+        prop_checkAccurateOutputValBitSizePar++------------+prop_checkIfLTEn :: Positive Integer -> Property+prop_checkIfLTEn (Positive n) = n > 2 && n < 30 ==> monadicIO $ do+  x <- run $ genARandomPreFactoredNumberLTEn n+  case x of+    Left _ -> assert False+    Right y -> assert (fst y <= n)++prop_checkIffiltersInValidInput :: Negative Integer -> Property+prop_checkIffiltersInValidInput (Negative n) = n > -10000 && n < 1 ==> monadicIO $ do+  -- Constraining n to be within a "bad range"+  x <- run $ genARandomPreFactoredNumberLTEn n+  case x of+    Left err -> assert (err == pack "Invalid")+    Right _ -> assert False++-- input should be GTE to the head value of a valid pre-factored list+-- (6, [3,2]) ==> 6 >= 3 (5, [5, 1]) ==> 5 >= 5+prop_checkValidOutput :: Positive Integer -> Property+prop_checkValidOutput (Positive n) = n > 2 && n < 50 ==> classify (n < 30) "n LT 30" $ collect n $ monadicIO $ do+  -- if n upper end is set at 100 then it results in an error https://www.cnblogs.com/BlogOfASBOIER/p/13096167.html+  x <- run $ genARandomPreFactoredNumberLTEn n+  case x of+    Left err -> assert (err == pack "Invalid")+    Right y -> assert (fst y >= head (snd y))++prop_checkAccurateOutput :: Positive Integer -> Property+prop_checkAccurateOutput (Positive n) = n > 2 && n < 50 ==> classify (n < 30) "n LT 30" $ collect n $ counterexample "Failed case" $ monadicIO $ do+  -- if n upper end is set at 100 then it results in an error https://www.cnblogs.com/BlogOfASBOIER/p/13096167.html+  x <- run $ genARandomPreFactoredNumberLTEn n+  case x of+    Left err -> assert (err == pack "Invalid")+    Right y -> assert (primeFactorsOr1 (fst y) == snd y)++prop_checkAccurateOutputVal :: Positive Integer -> Property+prop_checkAccurateOutputVal (Positive n) = n > 2 && n < 50 ==> classify (n < 30) "n LT 30" $ collect n $ counterexample "Failed case" $ monadicIO $ do+  -- if n upper end is set at 100 then it results in an error https://www.cnblogs.com/BlogOfASBOIER/p/13096167.html+  x <- run $ genARandomPreFactoredNumberLTEn n+  case x of+    Left err -> assert (err == pack "Invalid")+    Right y -> assert (fst y == product (snd y))++prop_checkAccurateOutputValBitSize :: Positive Integer -> Property+prop_checkAccurateOutputValBitSize (Positive n) = n > 2 && n < 70 ==> classify (n < 50) "n LT 50" $ collect n $ counterexample "Failed case" $ monadicIO $ do+  -- if n upper end is set at 100 then it results in an error https://www.cnblogs.com/BlogOfASBOIER/p/13096167.html+  x <- run $ preFactoredNumOfBitSize n+  case x of+    Left err -> assert (err == pack "Invalid")+    Right y -> assert (fst y == product (snd y))++prop_checkAccurateOutputValBitSizePar :: Positive Integer -> Property+prop_checkAccurateOutputValBitSizePar (Positive n) = n > 2 && n < 70 ==> classify (n < 50) "n LT 50" $ collect n $ counterexample "Failed case" $ monadicIO $ do+  -- if n upper end is set at 100 then it results in an error https://www.cnblogs.com/BlogOfASBOIER/p/13096167.html+  x <- run $ preFactoredNumOfBitSizePar n+  case x of+    Left err -> assert (err == pack "Invalid")+    Right y -> assert (fst y == product (snd y))++primeFactorsOr1 :: Integer -> [Integer]+primeFactorsOr1 1 = [1]+primeFactorsOr1 n = reverse (1 : primeFactors n)