0 | module Hedgehog.Internal.Gen
  1 |
  2 | import Data.Bounded
  3 | import Data.Colist
  4 | import Data.Cotree
  5 | import Data.Fin
  6 | import Data.List
  7 | import Data.List.Quantifiers
  8 | import Data.List1
  9 | import Data.Nat
 10 | import Data.SOP
 11 | import Data.String
 12 | import Data.Tree
 13 | import Data.Vect
 14 | import Data.Vect.Quantifiers
 15 |
 16 | import Control.Monad.Maybe
 17 |
 18 | import Hedgehog.Internal.Range
 19 | import Hedgehog.Internal.Shrink
 20 | import Hedgehog.Internal.Util
 21 |
 22 | import System.Random.Pure.StdGen
 23 |
 24 | %hide Prelude.Range
 25 |
 26 | %default total
 27 |
 28 | --------------------------------------------------------------------------------
 29 | --          Random Value Generator with Integrated Shrinking
 30 | --------------------------------------------------------------------------------
 31 |
 32 | ||| Generates random values of type `a`
 33 | public export
 34 | record Gen (a : Type) where
 35 |   constructor MkGen
 36 |   unGen   : Size -> StdGen -> Cotree a
 37 |
 38 | public export %inline
 39 | runGen : Size -> StdGen -> Gen a -> Cotree a
 40 | runGen si se g = unGen g si se
 41 |
 42 | public export
 43 | mapGen :  (f : Cotree a -> Cotree b) -> Gen a -> Gen b
 44 | mapGen f (MkGen run) = MkGen $ \si,se => f (run si se)
 45 |
 46 | ||| Lift a predefined shrink tree in to a generator, ignoring the seed and the
 47 | ||| size.
 48 | public export
 49 | fromTree : Cotree a -> Gen a
 50 | fromTree ct = MkGen $ \_,_ => ct
 51 |
 52 | ||| Observe a generator's shrink tree.
 53 | public export
 54 | toTree : Gen a -> Gen (Cotree a)
 55 | toTree (MkGen unGen) = MkGen $ \si,se => pure (unGen si se)
 56 |
 57 | --------------------------------------------------------------------------------
 58 | --          Interface Implementations
 59 | --------------------------------------------------------------------------------
 60 |
 61 | public export
 62 | Functor Gen where
 63 |   map f (MkGen g) = MkGen $ \si,se => map f (g si se)
 64 |
 65 | public export
 66 | Applicative Gen where
 67 |   pure a = MkGen $ \_,_ => pure a
 68 |
 69 |   MkGen ff <*> MkGen fa =
 70 |     MkGen $ \si,se =>
 71 |       let (se1,se2) := split se
 72 |        in interleave (ff si se1) (fa si se2)
 73 |
 74 | public export
 75 | Monad Gen where
 76 |   MkGen run >>= f =
 77 |     MkGen $ \si,se =>
 78 |       let (se1,se2) := split se
 79 |           ta        := run si se1
 80 |        in bind ta (runGen si se2 . f)
 81 |
 82 | --------------------------------------------------------------------------------
 83 | --          Combinators
 84 | --------------------------------------------------------------------------------
 85 |
 86 | public export
 87 | generate : (Size -> StdGen -> a) -> Gen a
 88 | generate  f = MkGen $ \si,se => pure (f si se)
 89 |
 90 | --------------------------------------------------------------------------------
 91 | --          Shrinking
 92 | --------------------------------------------------------------------------------
 93 |
 94 | public export
 95 | shrink : (a -> Colist a) -> Gen a -> Gen a
 96 | shrink f = mapGen (expand f)
 97 |
 98 | public export %inline
 99 | prune : Gen a -> Gen a
100 | prune = mapGen prune
101 |
102 | --------------------------------------------------------------------------------
103 | --          Size
104 | --------------------------------------------------------------------------------
105 |
106 | ||| Construct a generator that depends on the size parameter.
107 | public export
108 | sized : (Size -> Gen a) -> Gen a
109 | sized f = generate (\si,_ => si) >>= f
110 |
111 | ||| Adjust the size parameter by transforming it with the given function.
112 | public export
113 | scale : (Size -> Size) -> Gen a -> Gen a
114 | scale f (MkGen run) = MkGen $ \si,se => run (f si) se
115 |
116 | ||| Override the size parameter. Returns a generator which uses the given size
117 | ||| instead of the runtime-size parameter.
118 | public export %inline
119 | resize : Size -> Gen a -> Gen a
120 | resize size = scale (const size)
121 |
122 | ||| Scale a size using the golden ratio.
123 | |||
124 | |||   > golden x = x / φ
125 | |||   > golden x = x / 1.61803..
126 | public export
127 | golden : Size -> Size
128 | golden = resize $ \n => round (0.61803398875 * cast n)
129 |
130 | ||| Make a generator smaller by scaling its size parameter.
131 | public export %inline
132 | small : Gen a -> Gen a
133 | small = scale golden
134 |
135 | --------------------------------------------------------------------------------
136 | --          Integral
137 | --------------------------------------------------------------------------------
138 |
139 | ||| Generates a random integral number in the [inclusive,inclusive] range.
140 | |||
141 | ||| This generator does not shrink.
142 | public export
143 | integral_ : ToInteger a => Range a -> Gen a
144 | integral_ range = generate $ \si,se =>
145 |   let (x, y) := bounds si range
146 |    in fromInteger . snd $ randomR (toInteger x, toInteger y) se
147 |
148 | ||| Generates a random integral number in the given @[inclusive,inclusive]@ range.
149 | |||
150 | ||| When the generator tries to shrink, it will shrink towards the
151 | ||| 'Range.origin' of the specified 'Range'.
152 | |||
153 | ||| For example, the following generator will produce a number between @1970@
154 | ||| and @2100@, but will shrink towards @2000@:
155 | |||
156 | ||| ```
157 | ||| integral (Range.'Range.constantFrom' 2000 1970 2100) :: 'Gen' 'Int'
158 | ||| ```
159 | |||
160 | |||   Some sample outputs from this generator might look like:
161 | |||
162 | |||   > === Outcome ===
163 | |||   > 1973
164 | |||   > === Shrinks ===
165 | |||   > 2000
166 | |||   > 1987
167 | |||   > 1980
168 | |||   > 1976
169 | |||   > 1974
170 | |||
171 | |||   > === Outcome ===
172 | |||   > 2061
173 | |||   > === Shrinks ===
174 | |||   > 2000
175 | |||   > 2031
176 | |||   > 2046
177 | |||   > 2054
178 | |||   > 2058
179 | |||   > 2060
180 | |||
181 | public export
182 | integral : ToInteger a => Range a -> Gen a
183 | integral range = shrink (towards $ origin range) (integral_ range)
184 |
185 | ||| Generates a random machine integer in the given range.
186 | |||
187 | ||| This is a specialization of `integral`, offered for convenience.
188 | public export %inline
189 | int : Range Int -> Gen Int
190 | int = integral
191 |
192 | ||| Generates a random 8-bit integer in the given range.
193 | |||
194 | ||| This is a specialization of `integral`, offered for convenience.
195 | public export %inline
196 | int8 : Range Int8 -> Gen Int8
197 | int8 = integral
198 |
199 | ||| Generates a random 8-bit integer in the full available range.
200 | |||
201 | ||| This shrinks exponentially towards 0.
202 | public export %inline
203 | anyInt8 : Gen Int8
204 | anyInt8 = int8 (exponentialFrom 0 minBound maxBound)
205 |
206 | ||| Generates a random 16-bit integer in the given range.
207 | |||
208 | ||| This is a specialization of `integral`, offered for convenience.
209 | public export %inline
210 | int16 : Range Int16 -> Gen Int16
211 | int16 = integral
212 |
213 | ||| Generates a random 16-bit integer in the full available range.
214 | |||
215 | ||| This shrinks exponentially towards 0.
216 | public export %inline
217 | anyInt16 : Gen Int16
218 | anyInt16 = int16 (exponentialFrom 0 minBound maxBound)
219 |
220 | ||| Generates a random 32-bit integer in the given range.
221 | |||
222 | ||| This is a specialization of `integral`, offered for convenience.
223 | public export %inline
224 | int32 : Range Int32 -> Gen Int32
225 | int32 = integral
226 |
227 | ||| Generates a random 32-bit integer in the full available range.
228 | |||
229 | ||| This shrinks exponentially towards 0.
230 | public export %inline
231 | anyInt32 : Gen Int32
232 | anyInt32 = int32 (exponentialFrom 0 minBound maxBound)
233 |
234 | ||| Generates a random 64-bit integer in the given range.
235 | |||
236 | ||| This is a specialization of `integral`, offered for convenience.
237 | public export %inline
238 | int64 : Range Int64 -> Gen Int64
239 | int64 = integral
240 |
241 | ||| Generates a random 64-bit integer in the full available range.
242 | |||
243 | ||| This shrinks exponentially towards 0.
244 | public export %inline
245 | anyInt64 : Gen Int64
246 | anyInt64 = int64 (exponentialFrom 0 minBound maxBound)
247 |
248 | ||| Generates a random 8-bit integer in the given range.
249 | |||
250 | ||| This is a specialization of 'integral', offered for convenience.
251 | public export %inline
252 | bits8 : Range Bits8 -> Gen Bits8
253 | bits8 = integral
254 |
255 | ||| Generates a random 8-bit signed integer in the full available range.
256 | |||
257 | ||| This shrinks exponentially towards 0.
258 | public export %inline
259 | anyBits8 : Gen Bits8
260 | anyBits8 = bits8 (exponential 0 maxBound)
261 |
262 | ||| Generates a random 16-bit integer in the given range.
263 | |||
264 | ||| This is a specialization of 'integral', offered for convenience.
265 | public export %inline
266 | bits16 : Range Bits16 -> Gen Bits16
267 | bits16 = integral
268 |
269 | ||| Generates a random 16-bit signed integer in the full available range.
270 | |||
271 | ||| This shrinks exponentially towards 0.
272 | public export %inline
273 | anyBits16 : Gen Bits16
274 | anyBits16 = bits16 (exponential 0 maxBound)
275 |
276 | ||| Generates a random 32-bit integer in the given range.
277 | |||
278 | ||| This is a specialization of 'integral', offered for convenience.
279 | public export %inline
280 | bits32 : Range Bits32 -> Gen Bits32
281 | bits32 = integral
282 |
283 | ||| Generates a random 32-bit signed integer in the full available range.
284 | |||
285 | ||| This shrinks exponentially towards 0.
286 | public export %inline
287 | anyBits32 : Gen Bits32
288 | anyBits32 = bits32 (exponential 0 maxBound)
289 |
290 | ||| Generates a random 64-bit integer in the given range.
291 | |||
292 | ||| This is a specialization of 'integral', offered for convenience.
293 | public export %inline
294 | bits64 : Range Bits64 -> Gen Bits64
295 | bits64 = integral
296 |
297 | ||| Generates a random 64-bit signed integer in the full available range.
298 | |||
299 | ||| This shrinks exponentially towards 0.
300 | public export %inline
301 | anyBits64 : Gen Bits64
302 | anyBits64 = bits64 (exponential 0 maxBound)
303 |
304 | ||| Generates a random Integer in the given range.
305 | |||
306 | ||| This is a specialization of 'integral', offered for convenience.
307 | public export %inline
308 | integer : Range Integer -> Gen Integer
309 | integer = integral
310 |
311 | ||| Generates a random Nat in the given range.
312 | |||
313 | ||| This is a specialization of 'integral', offered for convenience.
314 | public export %inline
315 | nat : Range Nat -> Gen Nat
316 | nat = integral
317 |
318 | ||| Generates a random Size in the given range.
319 | |||
320 | ||| This is a specialization of 'integral', offered for convenience.
321 | public export %inline
322 | size : Range Size -> Gen Size
323 | size = integral
324 |
325 | ||| Generates a random (Fin n) in the given range.
326 | public export
327 | fin : {n : _} -> Range (Fin n) -> Gen (Fin n)
328 | fin range =
329 |   let rangeInt := map finToInteger range
330 |    in map toFin (integer rangeInt)
331 |
332 |   where
333 |     toFin : Integer -> Fin n
334 |     toFin k = fromMaybe range.origin (integerToFin k n)
335 |
336 | --------------------------------------------------------------------------------
337 | --          Floating Point
338 | --------------------------------------------------------------------------------
339 |
340 | ||| Generates a random fractional number in the [inclusive,exclusive) range.
341 | |||
342 | ||| This generator does not shrink.
343 | export
344 | double_ : Range Double -> Gen Double
345 | double_ range =
346 |   generate $ \si,se =>
347 |     let (x, y) := bounds si range
348 |      in snd $ randomR (x, y) se
349 |
350 | ||| Generates a random floating-point number in the given range.
351 | |||
352 | ||| This generator works the same as 'integral', but for floating point numbers.
353 | |||
354 | export %inline
355 | double : Range Double -> Gen Double
356 | double range = shrink (towardsDouble $ origin range) (double_ range)
357 |
358 | --------------------------------------------------------------------------------
359 | --          Choice
360 | --------------------------------------------------------------------------------
361 |
362 | ||| Trivial generator that always produces the same element.
363 | |||
364 | ||| This is another name for `pure`.
365 | public export %inline
366 | constant : a -> Gen a
367 | constant = pure
368 |
369 | ||| Randomly selects one of the elements in the vector.
370 | |||
371 | ||| This generator shrinks towards the first element in the vector.
372 | public export
373 | element : {k : _} -> Vect (S k) a -> Gen a
374 | element vs = map (`index` vs) (fin $ constant FZ last)
375 |
376 | ||| Randomly selects one of the elements in the vector.
377 | |||
378 | ||| This generator does not shrink.
379 | public export %inline
380 | element_ : {k : _} -> Vect (S k) a -> Gen a
381 | element_ = prune . element
382 |
383 | ||| Randomly selects one of the generators in the vector.
384 | |||
385 | ||| This generator shrinks towards the first generator in the vector.
386 | public export %inline
387 | choice : {k : _} -> Vect (S k) (Gen a) -> Gen a
388 | choice vs = element vs >>= id
389 |
390 | ||| Randomly selects one of the generators in the vector.
391 | |||
392 | ||| This generator does not shrink towards a particular
393 | ||| generator in the vector
394 | public export %inline
395 | choice_ : {k : _} -> Vect (S k) (Gen a) -> Gen a
396 | choice_ vs = element_ vs >>= id
397 |
398 | ||| Uses a weighted distribution to randomly select one of the generators in
399 | ||| the vector.
400 | |||
401 | ||| This generator shrinks towards the first generator in the vector.
402 | |||
403 | ||| Note that if the given frequencies sum up to 0, the first element
404 | ||| of the vector
405 | public export
406 | frequency : Vect (S k) (Nat, Gen a) -> Gen a
407 | frequency ps =
408 |   let acc   := scanl1 addFst $ map (mapFst toInteger) ps
409 |       gen   := integral_ . constant 0 . fst $ last acc
410 |       lower := \n => takeWhile (< n) (fromFoldable $ map fst acc)
411 |
412 |   in shrink lower gen >>= choose acc
413 |
414 |   where
415 |     addFst : (Integer,x) -> (Integer,x) -> (Integer,x)
416 |     addFst (x,_) (y,v) = (x + y,v)
417 |
418 |     choose : Vect n (Integer, Gen a) -> Integer -> Gen a
419 |     choose []             _ = snd $ head ps
420 |     choose ((i, v) :: ps) k = if i >= k then v else choose ps k
421 |
422 | ||| Generates a random boolean.
423 | |||
424 | ||| This generator shrinks to `False`.
425 | public export %inline
426 | bool : Gen Bool
427 | bool = element [False,True]
428 |
429 | ||| Generates constant values of type `Unit"
430 | export %inline
431 | unit : Gen ()
432 | unit = pure ()
433 |
434 | --------------------------------------------------------------------------------
435 | --          Character
436 | --------------------------------------------------------------------------------
437 |
438 | ||| Generates a character in the given `Range`.
439 | |||
440 | ||| Shrinks towards the origin of the range.
441 | export
442 | char : Range Char -> Gen Char
443 | char = map chr . int . map ord
444 |
445 | ||| Generates a character in the interval [lower,uppper].
446 | |||
447 | ||| Shrinks towards the lower value.
448 | export %inline
449 | charc : (lower : Char) -> (upper : Char) -> Gen Char
450 | charc l u = char $ constant l u
451 |
452 |
453 | ||| Generates an ASCII binit: `'0'..'1'`
454 | export %inline
455 | binit : Gen Char
456 | binit = charc '0' '1'
457 |
458 | ||| Generates an ASCII octit: `'0'..'7'`
459 | export %inline
460 | octit : Gen Char
461 | octit = charc '0' '7'
462 |
463 | ||| Generates an ASCII digit: `'0'..'9'`
464 | export %inline
465 | digit : Gen Char
466 | digit = charc '0' '9'
467 |
468 | ||| Generates an ASCII hexit: `'0'..'9', 'a'..'f', 'A'..'F'`
469 | export
470 | hexit : Gen Char
471 | hexit = frequency [(10, digit),(6, charc 'a' 'f'),(6, charc 'A' 'F')]
472 |
473 | ||| Generates an ASCII lowercase letter: `'a'..'z'`
474 | export %inline
475 | lower : Gen Char
476 | lower = charc 'a' 'z'
477 |
478 | ||| Generates an ASCII uppercase letter: `'A'..'Z'`
479 | export %inline
480 | upper : Gen Char
481 | upper = charc 'A' 'Z'
482 |
483 | ||| Generates an ASCII letter: `'a'..'z', 'A'..'Z'`
484 | export %inline
485 | alpha : Gen Char
486 | alpha = choice [lower,upper]
487 |
488 | ||| Generates an ASCII letter or digit: `'a'..'z', 'A'..'Z', '0'..'9'`
489 | export
490 | alphaNum : Gen Char
491 | alphaNum = frequency [(10,digit),(24,lower),(24,upper)]
492 |
493 | ||| Generates an ASCII character: `'\0'..'\127'`
494 | export %inline
495 | ascii : Gen Char
496 | ascii = charc '\0' '\127'
497 |
498 | ||| Generates an printable ASCII character: `'\32'..'\126'`
499 | ||| Note: This includes the space character but no
500 | |||       line breaks or tabs
501 | export %inline
502 | printableAscii : Gen Char
503 | printableAscii = charc '\32' '\126'
504 |
505 | ||| Generates a latin character: `'\0'..'\255'`
506 | export %inline
507 | latin : Gen Char
508 | latin = charc '\0' '\255'
509 |
510 | ||| Generates a printable latin character: `'\32'..'\126'` and `'\160'..'\255'`
511 | export
512 | printableLatin : Gen Char
513 | printableLatin = frequency [ (95, printableAscii), (96, charc '\160' '\255') ]
514 |
515 | ||| Generates a Unicode character, excluding noncharacters
516 | ||| and invalid standalone surrogates:
517 | ||| `'\0'..'\1114111'` (excluding '\55296'..'\57343', '\65534', '\65535')`
518 | export
519 | unicode : Gen Char
520 | unicode =
521 |   frequency
522 |     [ (55296, charc '\0' '\55295')
523 |     , (8190, charc '\57344' '\65533')
524 |     , (1048576, charc '\65536' '\1114111')
525 |     ]
526 |
527 | ||| Generates a printable Unicode character, excluding noncharacters
528 | ||| and invalid standalone surrogates:
529 | ||| `'\0'..'\1114111'` (excluding '\0' .. '\31', '\127' .. '\159',
530 | ||| '\55296'..'\57343', and '\65534', '\65535')`
531 | export
532 | printableUnicode : Gen Char
533 | printableUnicode =
534 |   frequency
535 |     [ (95, printableAscii)
536 |     , (55136, charc '\160' '\55295')
537 |     , (8190, charc '\57344' '\65533')
538 |     , (1048576, charc '\65536' '\1114111')
539 |     ]
540 |
541 | ||| Generates a Unicode character, including noncharacters
542 | ||| but excluding invalid standalone surrogates: `0000..10FFFF`
543 | ||| (excluding D800..DFFFF)
544 | export %inline
545 | unicodeAll : Gen Char
546 | unicodeAll =
547 |   frequency
548 |     [ (55296, charc '\0' '\55295')
549 |     , (1056768, charc '\57344' '\1114111')
550 |     ]
551 |
552 | --------------------------------------------------------------------------------
553 | --          Containers
554 | --------------------------------------------------------------------------------
555 |
556 | ||| Generates a 'Nothing' some of the time.
557 | export
558 | maybe : Gen a -> Gen (Maybe a)
559 | maybe gen =
560 |   sized $ \s =>
561 |     frequency
562 |       [ (2, constant Nothing)
563 |       , (S s.size, Just <$> gen)
564 |       ]
565 |
566 | ||| Generates either an 'a' or a 'b'.
567 | |||
568 | ||| As the size grows, this generator generates @Right@s more often than @Left@s.
569 | export
570 | either : Gen a -> Gen b -> Gen (Either a b)
571 | either genA genB =
572 |   sized $ \s =>
573 |     frequency
574 |       [ (2, Left <$> genA)
575 |       , (S s.size, Right <$> genB)
576 |       ]
577 |
578 | ||| Generates either an 'a' or a 'b', without bias.
579 | |||
580 | ||| This generator generates as many @Right@s as it does @Left@s.
581 | export %inline
582 | either_ : Gen a -> Gen b -> Gen (Either a b)
583 | either_ genA genB = choice [Left <$> genA, Right <$> genB]
584 |
585 | ||| Generates a list using a 'Range' to determine the length.
586 | export
587 | vect : (n : Nat) -> Gen a -> Gen (Vect n a)
588 | vect 0     _ = pure []
589 | vect (S k) g = [| g :: vect k g |]
590 |
591 | ||| Generates a list using a 'Range' to determine the length.
592 | export
593 | list : Range Nat -> Gen a -> Gen (List a)
594 | list range gen =
595 |   sized $ \si =>
596 |     let minLength := lowerBound si range
597 |      in mapGen (interleave minLength . value) $
598 |         integral_ range >>= (\n => map toList (vect n (toTree gen)))
599 |
600 | ||| Generates a non-empty list using a `Range` to determine the length.
601 | export
602 | list1 : Range Nat -> Gen a -> Gen (List1 a)
603 | list1 range gen = [| gen ::: list (map pred range) gen |]
604 |
605 | ||| Generates a string using 'Range' to determine the length.
606 | export
607 | string : Range Nat -> Gen Char -> Gen String
608 | string range = map fastPack . list range
609 |
610 | ||| Generates an heterogeneous list being provided a generator for each element
611 | export
612 | hlist : All Gen ts -> Gen (HList ts)
613 | hlist []      = [| [] |]
614 | hlist (x::xs) = [| x :: hlist xs |]
615 |
616 | ||| Generates an heterogeneous vect being provided a generator for each element
617 | export
618 | hvect : All Gen ts -> Gen (HVect ts)
619 | hvect []      = [| [] |]
620 | hvect (x::xs) = [| x :: hvect xs |]
621 |
622 | --------------------------------------------------------------------------------
623 | --          SOP
624 | --------------------------------------------------------------------------------
625 |
626 | collapseNPV : NP (K a) ks -> {auto 0 _ : NonEmpty ks} -> (k ** Vect (S k) a)
627 | collapseNPV {ks = _} [] impossible
628 | collapseNPV {ks = _} (v::[]) = (0 ** [v])
629 | collapseNPV {ks = t::t2::ts}(v::v2::vs) =
630 |   let (k ** vs2:= collapseNPV {ks = t2 :: ts} (v2 :: vs)
631 |    in (S k ** (v :: vs2))
632 |
633 | ||| Turns an n-ary product of generators into a generator
634 | ||| of n-ary products of the same type.
635 | export
636 | np : NP Gen ts -> Gen (NP I ts)
637 | np = sequenceNP
638 |
639 | ||| Turns an n-ary product of generators into a generator
640 | ||| of n-ary sums of the same type. This is a generalisation
641 | ||| of choice and shrinks towards the first sum type
642 | ||| in the list.
643 | export
644 | ns : NP Gen ts -> {auto 0 prf : NonEmpty ts} -> Gen (NS I ts)
645 | ns np =
646 |   let (_ ** vs:= collapseNPV (apInjsNP_ np)
647 |    in choice (map sequenceNS vs)
648 |
649 | ||| Turns an n-ary product of generators into a generator
650 | ||| of n-ary sums of the same type. This is a generalisation
651 | ||| of `choice_` and does not shrink towards a particular
652 | ||| sum type.
653 | export
654 | ns_ : NP Gen ts -> {auto 0 prf : NonEmpty ts} -> Gen (NS I ts)
655 | ns_ np =
656 |   let (_ ** vs:= collapseNPV (apInjsNP_ np)
657 |    in choice_ (map sequenceNS vs)
658 |
659 | export
660 | sop : POP Gen tss -> {auto 0 prf : NonEmpty tss} -> Gen (SOP I tss)
661 | sop p =
662 |   let (_ ** vs:= collapseNPV {a = SOP Gen tss} (apInjsPOP_ p)
663 |    in choice (map sequenceSOP vs)
664 |
665 | --------------------------------------------------------------------------------
666 | --          Sampling
667 | --------------------------------------------------------------------------------
668 |
669 | ||| Print the value produced by a generator, and the first level of shrinks,
670 | ||| for the given size and seed.
671 | |||
672 | ||| Use 'print' to generate a value from a random seed.
673 | export
674 | printWith : (HasIO io, Show a) => Size -> StdGen -> Gen a -> io ()
675 | printWith si se gen =
676 |   let (MkCotree v fo) := runGen si se gen
677 |       shrinks         := value <$> take 1000 fo
678 |    in do
679 |      putStrLn "=== Outcome ==="
680 |      putStrLn (show v)
681 |      putStrLn "=== Shrinks ==="
682 |      traverse_ printLn shrinks
683 |
684 | ||| Run a generator with a random seed and print the outcome, and the first
685 | ||| level of shrinks.
686 | |||
687 | ||| @
688 | ||| Gen.print (Gen.'enum' \'a\' \'f\')
689 | ||| @
690 | |||
691 | |||   > === Outcome ===
692 | |||   > 'd'
693 | |||   > === Shrinks ===
694 | |||   > 'a'
695 | |||   > 'b'
696 | |||   > 'c'
697 | export
698 | print : Show a => HasIO io => Gen a -> io ()
699 | print gen = initSeed >>= \se => printWith 100 se gen
700 |
701 | ||| Generate a sample from a generator.
702 | export
703 | sample : HasIO io => Gen a -> io a
704 | sample gen = (value . gen.unGen 100) <$> initSeed
705 |
706 | ||| Render the shrink tree produced by a generator, for the given size and
707 | ||| seed up to the given depth and width.
708 | export
709 | renderTree :
710 |      {auto _ : Show a}
711 |   -> (maxDepth : Nat)
712 |   -> (maxWidth : Nat)
713 |   -> Size
714 |   -> StdGen
715 |   -> Gen a
716 |   -> String
717 | renderTree md mw si se = drawTree
718 |                        . map show
719 |                        . toTree md mw
720 |                        . runGen si se
721 |
722 | ||| Print the shrink tree produced by a generator, for the given size and
723 | ||| seed.
724 | |||
725 | ||| Use 'printTree' to generate a value from a random seed.
726 | export
727 | printTreeWith :
728 |      {auto _ : Show a}
729 |   -> {auto _ : HasIO io}
730 |   -> (maxDepth : Nat)
731 |   -> (maxWidth : Nat)
732 |   -> Size
733 |   -> StdGen
734 |   -> Gen a
735 |   -> io ()
736 | printTreeWith md mw si se = putStrLn . renderTree md mw si se
737 |
738 | ||| Run a generator with a random seed and print the resulting shrink tree.
739 | |||
740 | ||| @
741 | ||| Gen.printTree (Gen.'enum' \'a\' \'f\')
742 | ||| @
743 | |||
744 | |||   > 'd'
745 | |||   >  ├╼'a'
746 | |||   >  ├╼'b'
747 | |||   >  │  └╼'a'
748 | |||   >  └╼'c'
749 | |||   >     ├╼'a'
750 | |||   >     └╼'b'
751 | |||   >        └╼'a'
752 | |||
753 | |||   /This may not terminate when the tree is very large./
754 | |||
755 | export
756 | printTree :
757 |      {auto _ : Show a}
758 |   -> {auto _ : HasIO io}
759 |   -> (maxDepth : Nat)
760 |   -> (maxWidth : Nat)
761 |   -> Gen a
762 |   -> io ()
763 | printTree md mw gen = initSeed >>= \se => printTreeWith md mw 100 se gen
764 |