Skip to content

Commit

Permalink
Use binary shrinking for integral.
Browse files Browse the repository at this point in the history
The current shrink strategy produces potentially quite a lot of
duplication. By using a binary search, we should be able to
significantly speed up shrinking.

With this change we can see

```
Gen.printTreeWith  30 (Seed 5 3)  $ Gen.int (Range.constant 0 22)
 7
 ├╼ 0
 ├╼ 4
 │  ├╼ 2
 │  │  └╼ 1
 │  └╼ 3
 └╼ 6
    └╼ 5
```

While before we had
```
Gen.printTreeWith  30 (Seed 5 3)  $ Gen.int (Range.constant 0 22)
 7
 ├╼ 0
 ├╼ 4
 │  ├╼ 0
 │  ├╼ 2
 │  │  ├╼ 0
 │  │  └╼ 1
 │  │     └╼ 0
 │  └╼ 3
 │     ├╼ 0
 │     └╼ 2
 │        ├╼ 0
 │        └╼ 1
 │           └╼ 0
 └╼ 6
    ├╼ 0
    ├╼ 3
    │  ├╼ 0
    │  └╼ 2
    │     ├╼ 0
    │     └╼ 1
    │        └╼ 0
    └╼ 5
       ├╼ 0
       ├╼ 3
       │  ├╼ 0
       │  └╼ 2
       │     ├╼ 0
       │     └╼ 1
       │        └╼ 0
       └╼ 4
          ├╼ 0
          ├╼ 2
          │  ├╼ 0
          │  └╼ 1
          │     └╼ 0
          └╼ 3
             ├╼ 0
             └╼ 2
                ├╼ 0
                └╼ 1
                   └╼ 0
```

The first level of the tree is exactly the same, but then the size of the
tree reduces significantly as all duplication is removed.

This is currently just for `integral`, but as integral is used for
`element`, this should improve things pretty broadly.
  • Loading branch information
HuwCampbell committed Feb 2, 2021
1 parent 4dd8ccd commit ecd1c65
Showing 1 changed file with 27 additions and 2 deletions.
29 changes: 27 additions & 2 deletions hedgehog/src/Hedgehog/Internal/Gen.hs
Original file line number Diff line number Diff line change
Expand Up @@ -799,9 +799,34 @@ golden x =
-- > 2058
-- > 2060
--
integral :: (MonadGen m, Integral a) => Range a -> m a
integral :: forall m a. (MonadGen m, Integral a) => Range a -> m a
integral range =
shrink (Shrink.towards $ Range.origin range) (integral_ range)
let
appendOrigin :: Tree.TreeT (MaybeT (GenBase m)) a -> Tree.TreeT (MaybeT (GenBase m)) a
appendOrigin tree =
Tree.TreeT $ do
Tree.NodeT x xs <- Tree.runTreeT tree
pure $
Tree.NodeT x (xs <> [pure (Range.origin range)])

binarySearchTree :: a -> Tree.TreeT (MaybeT (GenBase m)) a -> Tree.TreeT (MaybeT (GenBase m)) a
binarySearchTree bottom tree =
Tree.TreeT $ do
Tree.NodeT x xs <- Tree.runTreeT tree
let
level =
Shrink.towards bottom x
zipped =
zipWith (\b a -> binarySearchTree b (pure a)) (level) (drop 1 level)

pure $
Tree.NodeT x (xs <> zipped)

withGenT' :: (GenT (GenBase m) a -> GenT (GenBase m) b) -> m a -> m b
withGenT' = withGenT

in
withGenT' (mapGenT (binarySearchTree (Range.origin range) . appendOrigin)) (integral_ range)

-- | Generates a random integral number in the [inclusive,inclusive] range.
--
Expand Down

0 comments on commit ecd1c65

Please sign in to comment.