ppad-eproc 0.5.0 → 0.5.1
raw patch · 7 files changed
+61/−50 lines, 7 files
Files
- CHANGELOG +4/−0
- bench/Main.hs +7/−6
- bench/Weight.hs +7/−6
- lib/Numeric/Eproc/ConfSeq.hs +3/−2
- lib/Numeric/Eproc/Mixture.hs +3/−2
- ppad-eproc.cabal +2/−2
- test/Main.hs +35/−32
CHANGELOG view
@@ -1,5 +1,9 @@ # Changelog +- 0.5.1 (2026-10-11)+ * Supports GHC 9.8 (base 4.19), where Prelude does not export+ foldl'.+ - 0.5.0 (2026-08-21) * Adds Numeric.Eproc.Bounded.configInterval: interval nulls H_0: m_lo <= E[x | F] <= m_hi for the two-sided bounded-mean
bench/Main.hs view
@@ -4,6 +4,7 @@ module Main where import Control.DeepSeq+import qualified Data.List as L import qualified Numeric.Eproc.Bernoulli as Bern import qualified Numeric.Eproc.Bernoulli.TwoSided as BernTS import qualified Numeric.Eproc.Bounded as Bounded@@ -73,7 +74,7 @@ !cfg_f = ok (Bounded.config 0.5 0.0 1.0 1.0e-3 (Bounded.Fixed 0.5)) !cfg_a = ok (Bounded.config 0.5 0.0 1.0 1.0e-3 Bounded.Adaptive) !cfg_o = ok (Bounded.config 0.5 0.0 1.0 1.0e-3 Bounded.Newton)- run_m cfg = foldl' (Bounded.update cfg) (Bounded.initial cfg)+ run_m cfg = L.foldl' (Bounded.update cfg) (Bounded.initial cfg) in bgroup "Bounded.update (1000-sample fold)" [ bench "fixed" $ nf (run_m cfg_f) xs , bench "adaptive" $ nf (run_m cfg_a) xs@@ -86,7 +87,7 @@ !cfg_f = ok (P.config 0.0 1.0 1.0e-3 (Bounded.Fixed 0.5)) !cfg_a = ok (P.config 0.0 1.0 1.0e-3 Bounded.Adaptive) !cfg_o = ok (P.config 0.0 1.0 1.0e-3 Bounded.Newton)- run_t cfg = foldl' (P.update cfg) (P.initial cfg)+ run_t cfg = L.foldl' (P.update cfg) (P.initial cfg) in bgroup "Paired.update (1000-sample fold)" [ bench "fixed" $ nf (run_t cfg_f) ps , bench "adaptive" $ nf (run_t cfg_a) ps@@ -113,7 +114,7 @@ !cfg_f = ok (Bern.config 0.05 1.0e-3 (Bern.Fixed 5.0)) !cfg_a = ok (Bern.config 0.05 1.0e-3 Bern.Adaptive) !cfg_o = ok (Bern.config 0.05 1.0e-3 Bern.Newton)- run_b cfg = foldl' (Bern.update cfg) (Bern.initial cfg)+ run_b cfg = L.foldl' (Bern.update cfg) (Bern.initial cfg) in bgroup "Bernoulli.update (1000-sample fold)" [ bench "fixed" $ nf (run_b cfg_f) xs , bench "adaptive" $ nf (run_b cfg_a) xs@@ -140,7 +141,7 @@ !cfg_f = ok (BernTS.config 0.5 1.0e-3 (BernTS.Fixed 1.0)) !cfg_a = ok (BernTS.config 0.5 1.0e-3 BernTS.Adaptive) !cfg_o = ok (BernTS.config 0.5 1.0e-3 BernTS.Newton)- run_b cfg = foldl' (BernTS.update cfg) (BernTS.initial cfg)+ run_b cfg = L.foldl' (BernTS.update cfg) (BernTS.initial cfg) in bgroup "Bernoulli.TwoSided.update (1000-sample fold)" [ bench "fixed" $ nf (run_b cfg_f) xs , bench "adaptive" $ nf (run_b cfg_a) xs@@ -161,7 +162,7 @@ let !vs = force (take 1000 (cycle [[0.1, -0.2, 0.3, 0.0], [-0.3, 0.2, 0.0, 0.1]])) !cfg = ok (Mix.config 4 1.0e-3)- run_x c = foldl' (Mix.update c) (Mix.initial c)+ run_x c = L.foldl' (Mix.update c) (Mix.initial c) in bgroup "Mixture.update (1000-step fold)" [ bench "K=4" $ nf (run_x cfg) vs ]@@ -184,7 +185,7 @@ confseq_stream = let !xs = force (take 1000 (cycle [0.3, 0.7])) !cfg = ok (CS.config 0.0 1.0 0.05 200)- run_c = foldl' (CS.update cfg) (CS.initial cfg)+ run_c = L.foldl' (CS.update cfg) (CS.initial cfg) in bgroup "ConfSeq.update (1000-sample fold, g = 200)" [ bench "plug-in" $ nf run_c xs ]
bench/Weight.hs view
@@ -4,6 +4,7 @@ module Main where import Control.DeepSeq+import qualified Data.List as L import qualified Numeric.Eproc.Bernoulli as Bern import qualified Numeric.Eproc.Bernoulli.TwoSided as BernTS import qualified Numeric.Eproc.Bounded as Bounded@@ -66,7 +67,7 @@ !cfg_f = ok (Bounded.config 0.5 0.0 1.0 1.0e-3 (Bounded.Fixed 0.5)) !cfg_a = ok (Bounded.config 0.5 0.0 1.0 1.0e-3 Bounded.Adaptive) !cfg_o = ok (Bounded.config 0.5 0.0 1.0 1.0e-3 Bounded.Newton)- run_m cfg = foldl' (Bounded.update cfg) (Bounded.initial cfg)+ run_m cfg = L.foldl' (Bounded.update cfg) (Bounded.initial cfg) in wgroup "Bounded.update (1000-sample fold)" $ do func "fixed" (run_m cfg_f) xs func "adaptive" (run_m cfg_a) xs@@ -78,7 +79,7 @@ !cfg_f = ok (P.config 0.0 1.0 1.0e-3 (Bounded.Fixed 0.5)) !cfg_a = ok (P.config 0.0 1.0 1.0e-3 Bounded.Adaptive) !cfg_o = ok (P.config 0.0 1.0 1.0e-3 Bounded.Newton)- run_t cfg = foldl' (P.update cfg) (P.initial cfg)+ run_t cfg = L.foldl' (P.update cfg) (P.initial cfg) in wgroup "Paired.update (1000-sample fold)" $ do func "fixed" (run_t cfg_f) ps func "adaptive" (run_t cfg_a) ps@@ -103,7 +104,7 @@ !cfg_f = ok (Bern.config 0.05 1.0e-3 (Bern.Fixed 5.0)) !cfg_a = ok (Bern.config 0.05 1.0e-3 Bern.Adaptive) !cfg_o = ok (Bern.config 0.05 1.0e-3 Bern.Newton)- run_b cfg = foldl' (Bern.update cfg) (Bern.initial cfg)+ run_b cfg = L.foldl' (Bern.update cfg) (Bern.initial cfg) in wgroup "Bernoulli.update (1000-sample fold)" $ do func "fixed" (run_b cfg_f) xs func "adaptive" (run_b cfg_a) xs@@ -128,7 +129,7 @@ !cfg_f = ok (BernTS.config 0.5 1.0e-3 (BernTS.Fixed 1.0)) !cfg_a = ok (BernTS.config 0.5 1.0e-3 BernTS.Adaptive) !cfg_o = ok (BernTS.config 0.5 1.0e-3 BernTS.Newton)- run_b cfg = foldl' (BernTS.update cfg) (BernTS.initial cfg)+ run_b cfg = L.foldl' (BernTS.update cfg) (BernTS.initial cfg) in wgroup "Bernoulli.TwoSided.update (1000-sample fold)" $ do func "fixed" (run_b cfg_f) xs func "adaptive" (run_b cfg_a) xs@@ -147,7 +148,7 @@ let !vs = force (take 1000 (cycle [[0.1, -0.2, 0.3, 0.0], [-0.3, 0.2, 0.0, 0.1]])) !cfg = ok (Mix.config 4 1.0e-3)- run_x c = foldl' (Mix.update c) (Mix.initial c)+ run_x c = L.foldl' (Mix.update c) (Mix.initial c) in wgroup "Mixture.update (1000-step fold)" $ do func "K=4" (run_x cfg) vs @@ -167,6 +168,6 @@ confseq_stream = let !xs = force (take 1000 (cycle [0.3, 0.7])) !cfg = ok (CS.config 0.0 1.0 0.05 200)- run_c = foldl' (CS.update cfg) (CS.initial cfg)+ run_c = L.foldl' (CS.update cfg) (CS.initial cfg) in wgroup "ConfSeq.update (1000-sample fold, g = 200)" $ do func "plug-in" run_c xs
lib/Numeric/Eproc/ConfSeq.hs view
@@ -89,6 +89,7 @@ , samples ) where +import qualified Data.List as L import GHC.Float (log1p) import Numeric.Eproc.Common (ConfigError(..), finite) @@ -305,8 +306,8 @@ interval Config{..} State{..} = case st_live of [] -> Nothing (Point j0 _ _ : ps) ->- let !jmin = foldl' (\acc (Point j _ _) -> min acc j) j0 ps- !jmax = foldl' (\acc (Point j _ _) -> max acc j) j0 ps+ let !jmin = L.foldl' (\acc (Point j _ _) -> min acc j) j0 ps+ !jmax = L.foldl' (\acc (Point j _ _) -> max acc j) j0 ps !gp1 = fromIntegral (cfg_grid + 1) !w = cfg_hi - cfg_lo !l | jmin == 1 = cfg_lo
lib/Numeric/Eproc/Mixture.hs view
@@ -105,6 +105,7 @@ , samples ) where +import qualified Data.List as L import Numeric.Eproc.Common (Verdict(..), ConfigError(..), finite) -- types ----------------------------------------------------------------------@@ -201,8 +202,8 @@ update _ st@State{..} les = case les of [] -> st (l : ls) ->- let !m = foldl' max l ls- !s = foldl' (\ !acc v -> acc + exp (v - m)) 0 les+ let !m = L.foldl' max l ls+ !s = L.foldl' (\ !acc v -> acc + exp (v - m)) 0 les -- all components at e-value zero: the mixture log-sum is -- -Infinity, and (m +) would poison it into NaN. !cur | isInfinite m && m < 0 = m
ppad-eproc.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: ppad-eproc-version: 0.5.0+version: 0.5.1 synopsis: Anytime-valid sequential testing via e-processes. license: MIT license-file: LICENSE@@ -8,7 +8,7 @@ maintainer: jared@ppad.tech category: Statistics build-type: Simple-tested-with: GHC == 9.10.3+tested-with: GHC == { 9.8.4, 9.10.3 } extra-doc-files: CHANGELOG description: Anytime-valid sequential hypothesis testing and estimation for
test/Main.hs view
@@ -3,6 +3,7 @@ module Main where import Data.Bits+import qualified Data.List as L import Data.Word import qualified Numeric.Eproc.Bernoulli as Bern import qualified Numeric.Eproc.Bernoulli.TwoSided as BernTS@@ -159,7 +160,7 @@ testCase "degenerate input never rejects" $ do let cfg = ok (Bounded.config 0.5 0.0 1.0 1.0e-6 Bounded.Newton) xs = replicate 5000 0.5- st = foldl' (Bounded.update cfg) (Bounded.initial cfg) xs+ st = L.foldl' (Bounded.update cfg) (Bounded.initial cfg) xs Bounded.decide cfg st @?= Bounded.Continue , testCase "two-sided thresholds applied symmetrically" $ do let cfg = ok (Bounded.config 0.5 0.0 1.0 1.0e-6 Bounded.Newton)@@ -213,8 +214,8 @@ QC.forAll (QC.listOf unit_double) $ \xs -> let cfgP = ok (Bounded.config 0.5 0.0 1.0 1.0e-3 b) cfgI = ok (Bounded.configInterval 0.5 0.5 0.0 1.0 1.0e-3 b)- stP = foldl' (Bounded.update cfgP) (Bounded.initial cfgP) xs- stI = foldl' (Bounded.update cfgI) (Bounded.initial cfgI) xs+ stP = L.foldl' (Bounded.update cfgP) (Bounded.initial cfgP) xs+ stI = L.foldl' (Bounded.update cfgI) (Bounded.initial cfgI) xs in Bounded.log_wealth stP == Bounded.log_wealth stI && Bounded.log_wealth_sup stP == Bounded.log_wealth_sup stI , testCase "interior systematic tolerated where point null rejects" $ do@@ -225,8 +226,8 @@ 1.0e-6 Bounded.Newton) cfgP = ok (Bounded.config 0.5 0.0 1.0 1.0e-6 Bounded.Newton) xs = replicate 5000 0.52- stI = foldl' (Bounded.update cfgI) (Bounded.initial cfgI) xs- stP = foldl' (Bounded.update cfgP) (Bounded.initial cfgP) xs+ stI = L.foldl' (Bounded.update cfgI) (Bounded.initial cfgI) xs+ stP = L.foldl' (Bounded.update cfgP) (Bounded.initial cfgP) xs Bounded.decide cfgI stI @?= Bounded.Continue Bounded.decide cfgP stP @?= Bounded.Reject , testCase "calibration at the null boundary (p = m_hi)" $ do@@ -332,7 +333,7 @@ testCase "all-zero stream never rejects" $ do let cfg = ok (Bern.config 0.05 1.0e-6 Bern.Newton) xs = replicate 5000 False- st = foldl' (Bern.update cfg) (Bern.initial cfg) xs+ st = L.foldl' (Bern.update cfg) (Bern.initial cfg) xs Bern.decide cfg st @?= Bern.Continue , testCase "Newton FPR under H_0 (p = p_0 = 0.05)" $ do let cfg = ok (Bern.config 0.05 0.05 Bern.Newton)@@ -365,32 +366,32 @@ testCase "fixed bettor runs without error (bounded)" $ do let cfg = ok (Bounded.config 0.5 0.0 1.0 1.0e-3 (Bounded.Fixed 0.5)) xs = take 100 (cycle [0.0, 1.0])- st = foldl' (Bounded.update cfg) (Bounded.initial cfg) xs+ st = L.foldl' (Bounded.update cfg) (Bounded.initial cfg) xs assertBool "samples advanced" (Bounded.samples st == 100) , testCase "Newton bettor runs without error (bounded)" $ do let cfg = ok (Bounded.config 0.5 0.0 1.0 1.0e-3 Bounded.Newton) xs = take 100 (cycle [0.0, 1.0])- st = foldl' (Bounded.update cfg) (Bounded.initial cfg) xs+ st = L.foldl' (Bounded.update cfg) (Bounded.initial cfg) xs assertBool "samples advanced" (Bounded.samples st == 100) , testCase "Adaptive bettor runs without error (bounded)" $ do let cfg = ok (Bounded.config 0.5 0.0 1.0 1.0e-3 Bounded.Adaptive) xs = take 100 (cycle [0.0, 1.0])- st = foldl' (Bounded.update cfg) (Bounded.initial cfg) xs+ st = L.foldl' (Bounded.update cfg) (Bounded.initial cfg) xs assertBool "samples advanced" (Bounded.samples st == 100) , testCase "fixed bettor runs without error (bernoulli)" $ do let cfg = ok (Bern.config 0.5 1.0e-3 (Bern.Fixed 0.5)) xs = take 100 (cycle [True, False])- st = foldl' (Bern.update cfg) (Bern.initial cfg) xs+ st = L.foldl' (Bern.update cfg) (Bern.initial cfg) xs assertBool "samples advanced" (Bern.samples st == 100) , testCase "Newton bettor runs without error (bernoulli)" $ do let cfg = ok (Bern.config 0.5 1.0e-3 Bern.Newton) xs = take 100 (cycle [True, False])- st = foldl' (Bern.update cfg) (Bern.initial cfg) xs+ st = L.foldl' (Bern.update cfg) (Bern.initial cfg) xs assertBool "samples advanced" (Bern.samples st == 100) , testCase "Adaptive bettor runs without error (bernoulli)" $ do let cfg = ok (Bern.config 0.5 1.0e-3 Bern.Adaptive) xs = take 100 (cycle [True, False])- st = foldl' (Bern.update cfg) (Bern.initial cfg) xs+ st = L.foldl' (Bern.update cfg) (Bern.initial cfg) xs assertBool "samples advanced" (Bern.samples st == 100) ] @@ -408,16 +409,16 @@ let cfg = ok (Bounded.config 0.5 0.0 1.0 0.5 (Bounded.Fixed 1.0)) xs1 = replicate 5 1.0 xs2 = replicate 40 0.0- st1 = foldl' (Bounded.update cfg) (Bounded.initial cfg) xs1- st2 = foldl' (Bounded.update cfg) st1 xs2+ st1 = L.foldl' (Bounded.update cfg) (Bounded.initial cfg) xs1+ st2 = L.foldl' (Bounded.update cfg) st1 xs2 Bounded.decide cfg st1 @?= Bounded.Reject Bounded.decide cfg st2 @?= Bounded.Reject , testCase "bernoulli: cross then drown stays rejected" $ do let cfg = ok (Bern.config 0.05 0.5 (Bern.Fixed 1.0)) xs1 = replicate 5 True xs2 = replicate 200 False- st1 = foldl' (Bern.update cfg) (Bern.initial cfg) xs1- st2 = foldl' (Bern.update cfg) st1 xs2+ st1 = L.foldl' (Bern.update cfg) (Bern.initial cfg) xs1+ st2 = L.foldl' (Bern.update cfg) st1 xs2 Bern.decide cfg st1 @?= Bern.Reject Bern.decide cfg st2 @?= Bern.Reject ]@@ -516,7 +517,7 @@ let cfg = ok (BernTS.config 0.5 1.0e-6 BernTS.Newton) -- alternating True/False keeps the empirical rate at 0.5. xs = take 5000 (cycle [True, False])- st = foldl' (BernTS.update cfg) (BernTS.initial cfg) xs+ st = L.foldl' (BernTS.update cfg) (BernTS.initial cfg) xs BernTS.decide cfg st @?= BernTS.Continue , testCase "detects upward shift (p = 0.7 vs p_0 = 0.5)" $ do let cfg = ok (BernTS.config 0.5 1.0e-3 BernTS.Newton)@@ -545,8 +546,8 @@ -- then two hundred 0s drop the current wealth, but the -- latch must hold. xs2 = replicate 200 False- st1 = foldl' (BernTS.update cfg) (BernTS.initial cfg) xs1- st2 = foldl' (BernTS.update cfg) st1 xs2+ st1 = L.foldl' (BernTS.update cfg) (BernTS.initial cfg) xs1+ st2 = L.foldl' (BernTS.update cfg) st1 xs2 BernTS.decide cfg st1 @?= BernTS.Reject BernTS.decide cfg st2 @?= BernTS.Reject , testCase "config: NaN p0 rejected" $ do@@ -596,7 +597,7 @@ QC.forAll arb_bettor $ \b -> QC.forAll (QC.listOf unit_double) $ \xs -> let cfg = ok (Bounded.config 0.5 0.0 1.0 1.0e-3 b)- st = foldl' (Bounded.update cfg) (Bounded.initial cfg) xs+ st = L.foldl' (Bounded.update cfg) (Bounded.initial cfg) xs in finite (Bounded.log_wealth st) && finite (Bounded.log_wealth_sup st) @@ -604,21 +605,21 @@ QC.forAll arb_bettor $ \b -> QC.forAll QC.arbitrary $ \xs -> let cfg = ok (Bern.config 0.05 1.0e-3 b)- st = foldl' (Bern.update cfg) (Bern.initial cfg) (xs :: [Bool])+ st = L.foldl' (Bern.update cfg) (Bern.initial cfg) (xs :: [Bool]) in finite (Bern.log_wealth st) && finite (Bern.log_wealth_sup st) , QC.testProperty "Bounded: Fixed with arbitrary lambda is safe" $ QC.forAll (QC.choose (-1000, 1000)) $ \lam -> QC.forAll (QC.listOf unit_double) $ \xs -> let cfg = ok (Bounded.config 0.5 0.0 1.0 1.0e-3 (C.Fixed lam))- st = foldl' (Bounded.update cfg) (Bounded.initial cfg) xs+ st = L.foldl' (Bounded.update cfg) (Bounded.initial cfg) xs in finite (Bounded.log_wealth st) , QC.testProperty "Bernoulli: Fixed with arbitrary lambda is safe" $ QC.forAll (QC.choose (-1000, 1000)) $ \lam -> QC.forAll QC.arbitrary $ \xs -> let cfg = ok (Bern.config 0.05 1.0e-3 (C.Fixed lam))- st = foldl' (Bern.update cfg) (Bern.initial cfg) (xs :: [Bool])+ st = L.foldl' (Bern.update cfg) (Bern.initial cfg) (xs :: [Bool]) in finite (Bern.log_wealth st) , QC.testProperty "Bounded: log_wealth_sup is monotone nondecreasing" $@@ -671,14 +672,16 @@ QC.forAll arb_bettor $ \b -> QC.forAll QC.arbitrary $ \xs -> let cfg = ok (BernTS.config 0.5 1.0e-3 b)- st = foldl' (BernTS.update cfg) (BernTS.initial cfg) (xs :: [Bool])+ st = L.foldl' (BernTS.update cfg) (BernTS.initial cfg)+ (xs :: [Bool]) in finite (BernTS.log_wealth st) && finite (BernTS.log_wealth_sup st) , QC.testProperty "BernTS: Fixed with arbitrary lambda is safe" $ QC.forAll (QC.choose (-1000, 1000)) $ \lam -> QC.forAll QC.arbitrary $ \xs -> let cfg = ok (BernTS.config 0.5 1.0e-3 (C.Fixed lam))- st = foldl' (BernTS.update cfg) (BernTS.initial cfg) (xs :: [Bool])+ st = L.foldl' (BernTS.update cfg) (BernTS.initial cfg)+ (xs :: [Bool]) in finite (BernTS.log_wealth st) , QC.testProperty "BernTS: log_wealth_sup is monotone nondecreasing" $@@ -730,14 +733,14 @@ QC.forAll arb_bettor $ \b -> QC.forAll (QC.listOf unit_double) $ \xs -> let cfg = ok (Bounded.config 0.5 0.0 1.0 1.0e-3 b)- st = foldl' (Bounded.update cfg) (Bounded.initial cfg) xs+ st = L.foldl' (Bounded.update cfg) (Bounded.initial cfg) xs in Bounded.log_evalue st == Bounded.log_wealth st - C.log2_dbl , QC.testProperty "Bernoulli: log_evalue coincides with log_wealth" $ QC.forAll arb_bettor $ \b -> QC.forAll QC.arbitrary $ \xs -> let cfg = ok (Bern.config 0.05 1.0e-3 b)- st = foldl' (Bern.update cfg) (Bern.initial cfg) (xs :: [Bool])+ st = L.foldl' (Bern.update cfg) (Bern.initial cfg) (xs :: [Bool]) in Bern.log_evalue st == Bern.log_wealth st , QC.testProperty "Bounded: decide agrees with p_value at alpha" $@@ -903,10 +906,10 @@ sts = drop 1 (scanl (Bounded.update bcfg) (Bounded.initial bcfg) xs) les = map Bounded.log_evalue sts- mix = foldl'+ mix = L.foldl' (\acc l -> Mix.update xcfg acc (replicate k l)) (Mix.initial xcfg) les- cfin = foldl' (Bounded.update bcfg) (Bounded.initial bcfg) xs+ cfin = L.foldl' (Bounded.update bcfg) (Bounded.initial bcfg) xs in approx_eq (Mix.log_evalue xcfg mix) (Bounded.log_evalue cfin) && approx_eq (Mix.log_evalue_sup xcfg mix)@@ -1019,7 +1022,7 @@ , testCase "consistency: Bernoulli(0.3) interval shrinks onto mean" $ do let cfg = ok (CS.config 0.0 1.0 1.0e-3 200) xs = cs_stream 0.3 5000 (mk_gen 424242)- st = foldl' (CS.update cfg) (CS.initial cfg) xs+ st = L.foldl' (CS.update cfg) (CS.initial cfg) xs case CS.interval cfg st of Nothing -> assertFailure "interval empty" Just (l, u) -> do@@ -1033,7 +1036,7 @@ let cfg = ok (CS.config (-5.0) 5.0 0.05 100) xs = [ if b == 1.0 then 4.0 else (-4.0) | b <- cs_stream 0.7 3000 (mk_gen 232323) ]- st = foldl' (CS.update cfg) (CS.initial cfg) xs+ st = L.foldl' (CS.update cfg) (CS.initial cfg) xs case CS.interval cfg st of Nothing -> assertFailure "interval empty" Just (l, u) -> do@@ -1059,7 +1062,7 @@ , QC.testProperty "interval endpoints well-formed on any stream" $ QC.forAll (QC.listOf unit_double) $ \xs -> let cfg = ok (CS.config 0.0 1.0 0.05 25)- st = foldl' (CS.update cfg) (CS.initial cfg) xs+ st = L.foldl' (CS.update cfg) (CS.initial cfg) xs in case CS.interval cfg st of Nothing -> True Just (l, u) ->