Files :
use51.m
Back to main
QuickCheck Tutorial 5
Generators - Basic
The invariant function is of the form
:- func Invariant_Function_X(T, T1, T2 ...) = property
:- mode Invariant_Function_X(in, in, in ...) = out.
Quickcheck generates random values for each input argument at run time.
The following types have default generators:
- int
- char
- float
- string
- some functions (more detail in Tutorial 8)
- any type defined as a discriminated union, provided
that all types in the body of the definition have
default/custom generators
- any type defined as being equivalent to a type with a
default/custom generator
There is no code written to handle equivalent types. But it works
as if the compiler replaced all the equivalent types with their real type
before compiling.
The default generator for int is rand_int/2, which has distribution:
50%
| even distribution in the range [-100, 100]
|
50%
| even distribution in the range (-2^31, 2^31)
(This will probably change to [int__min_int, int__max_int] in
a future version.)
|
:- func rand_int(rnd, rnd) = int.
:- mode rand_int(in, out) = out is det.
rand_int(BS0, BS) = Int :-
Temp = rand_allint(BS0, BS1) rem 2,
(if Temp = 0
then
irange(-100, 100, Int, BS1, BS)
else
Int = rand_allint(BS1, BS)
).
:- func rand_allint(rnd, rnd) = int.
:- mode rand_allint(in, out) = out is det.
rand_allint(BS0, BS) = Int :-
next(1, Sign, BS0, BS1),
next(31, TempInt, BS1, BS),
( Sign > 0 ->
Int = TempInt
;
Int = -TempInt
).
|
Default for char is rand_char/2, with even spread over
char__to_int(Char, X) where X is (-1000, 1000).
:- func rand_char(rnd, rnd) = char.
:- mode rand_char(in, out) = out is det.
rand_char(RS0, RS) = Char :-
Int = rand_allint(RS0, RS1) rem 1000,
(if char__to_int(Char0, Int)
then
Char = Char0,
RS = RS1
else
Char = rand_char(RS1, RS)
).
|
Default for float is rand_float/2, which has roughly even
distribution over all discrete values for a 32-bit float.
If machine is less then 32bits, overfloat occurs. It should still
cover all possible values, but may alter distribution.
If machine is more than 32bits, rand_float/2 will miss some values
but retains even distribution.
:- func rand_float(rnd, rnd) = float.
:- mode rand_float(in, out) = out is det.
rand_float(BS0, BS) = Flt :-
next(31, Mant0, BS0, BS1),
next(1, Sign, BS1, BS2),
( Sign > 0 ->
Mant = Mant0
;
Mant = -Mant0
),
next(7, Exp, BS2, BS3),
next(1, ExpSign, BS3, BS),
Flt0 = float(Mant) * pow(2.0, Exp),
( ExpSign > 0, Flt0 \= 0.0 ->
Flt = 1.0/Flt0
;
Flt = Flt0
).
|
Default for string is rand_string/2, each element is generated by
rand_char/2.
0.9^0 * 0.1
| chance being string length == 0
|
0.9^1 * 0.1
| chance being string length == 1
|
0.9^2 * 0.1
| chance being string length == 2
|
...etc...
|
So the mean string length is Sum(0.1* 0.9^N * N) where N ← {0, infinity},
which converges to 9.
:- func rand_string(rnd, rnd) = string.
:- mode rand_string(in, out) = out is det.
rand_string(RS0, RS) = X :-
gen(Charlist, [], [ {type_of(['A']), [{10,[]},{90,[]}]} ],
[], RS0, RS),
string__from_char_list(Charlist,X).
|
Note that the default generator for stirng actually uses the default generator
for discriminated union (indirectly).
use51.m gives an illustration of generating int, char, float, string.
:- module use51.
:- interface.
:- type marks == int.
:- use_module io.
:- pred main(io__state, io__state).
:- mode main(di, uo) is det.
%---------------------------------------------------------------------------%
:- implementation.
:- import_module int, char, float, string, list.
:- import_module qcheck.
%---------------------------------------------------------------------------%
main -->
qcheck(qcheck__f(junk), "just to show the inputs", 5, [], []).
:- func junk(marks, char, float, string) = property.
junk(A, B, C, D) =
{A,B,C,D} `>>>` [yes].
|
There are a few thing to note about use51.m :
qcheck(qcheck__f(junk), "just to show the inputs", 5, [], []).
The 3rd argument is an int, which specifies how many times to run. In this
example 5 tests are run , but the default is 100. The other auguments will
be described later.
junk(A, B, C, D) =
{A,B,C,D} `>>>` [yes].
the invariant function doesn't do any testing, it always succeeds. But
`>>>` collects all the inputs.
A Sample output
Test Description : just to show the inputs
Number of test cases that succeeded : 5
Number of trivial tests : 0
Number of tests cases which failed the pre-condition : 0
Distributions of selected argument(s) :
1 {-50, '4', -3.55475907854864e+25, "\241r\371~~\316\002LJ~\204\246"}
1 {27, '\342', -311727734390784., "\377g.\001"}
1 {1389908257, '8', 2.63071847153664e+15, "\342"}
1 {-90973704, '<', -2.10559053720692e-22, ""}
1 {-896549770, 's', 7.72155851221736e+30, "[\230m\304\2561\254Q"}
The char and string output doesn't look pretty, since most are not printable,
eg: \342, \254