1%%% Copyright 2010-2013 Manolis Papadakis <manopapad@gmail.com>,
2%%%                     Eirini Arvaniti <eirinibob@gmail.com>
3%%%                 and Kostis Sagonas <kostis@cs.ntua.gr>
4%%%
5%%% This file is part of PropEr.
6%%%
7%%% PropEr is free software: you can redistribute it and/or modify
8%%% it under the terms of the GNU General Public License as published by
9%%% the Free Software Foundation, either version 3 of the License, or
10%%% (at your option) any later version.
11%%%
12%%% PropEr is distributed in the hope that it will be useful,
13%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
14%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15%%% GNU General Public License for more details.
16%%%
17%%% You should have received a copy of the GNU General Public License
18%%% along with PropEr.  If not, see <http://www.gnu.org/licenses/>.
19
20%%% @copyright 2010-2013 Manolis Papadakis, Eirini Arvaniti and Kostis Sagonas
21%%% @version {@version}
22%%% @author Manolis Papadakis
23
24%%% @doc Type manipulation functions and predefined types.
25%%%
26%%% == Basic types ==
27%%% This module defines all the basic types of the PropEr type system as
28%%% functions. See the <a href="#index">function index</a> for an overview.
29%%%
30%%% Types can be combined in tuples or lists to produce other types. Exact
31%%% values (such as exact numbers, atoms, binaries and strings) can be combined
32%%% with types inside such structures, like in this example of the type of a
33%%% tagged tuple: ``{'result', integer()}''.
34%%%
35%%% When including the PropEr header file, all
36%%% <a href="#index">API functions</a> of this module are automatically
37%%% imported, unless `PROPER_NO_IMPORTS' is defined.
38%%%
39%%% == Customized types ==
40%%% The following operators can be applied to basic types in order to produce
41%%% new ones:
42%%%
43%%% <dl>
44%%% <dt>`?LET(<Xs>, <Xs_type>, <In>)'</dt>
45%%% <dd>To produce an instance of this type, all appearances of the variables
46%%%   in `<Xs>' are replaced inside `<In>' by their corresponding values in a
47%%%   randomly generated instance of `<Xs_type>'. It's OK for the `<In>' part to
48%%%   evaluate to a type - in that case, an instance of the inner type is
49%%%   generated recursively.</dd>
50%%% <dt>`?SUCHTHAT(<X>, <Type>, <Condition>)'</dt>
51%%% <dd>This produces a specialization of `<Type>', which only includes those
52%%%   members of `<Type>' that satisfy the constraint `<Condition>' - that is,
53%%%   those members for which the function `fun(<X>) -> <Condition> end' returns
54%%%   `true'. If the constraint is very strict - that is, only a small
55%%%   percentage of instances of `<Type>' pass the test - it will take a lot of
56%%%   tries for the instance generation subsystem to randomly produce a valid
57%%%   instance. This will result in slower testing, and testing may even be
58%%%   stopped short, in case the `constraint_tries' limit is reached (see the
59%%%   "Options" section in the documentation of the {@link proper} module). If
60%%%   this is the case, it would be more appropriate to generate valid instances
61%%%   of the specialized type using the `?LET' macro. Also make sure that even
62%%%   small instances can satisfy the constraint, since PropEr will only try
63%%%   small instances at the start of testing. If this is not possible, you can
64%%%   instruct PropEr to start at a larger size, by supplying a suitable value
65%%%   for the `start_size' option (see the "Options" section in the
66%%%   documentation of the {@link proper} module).</dd>
67%%% <dt>`?SUCHTHATMAYBE(<X>, <Type>, <Condition>)'</dt>
68%%% <dd>Equivalent to the `?SUCHTHAT' macro, but the constraint `<Condition>'
69%%%   is considered non-strict: if the `constraint_tries' limit is reached, the
70%%%   generator will just return an instance of `<Type>' instead of failing,
71%%%   even if that instance doesn't satisfy the constraint.</dd>
72%%% <dt>`?SHRINK(<Generator>, <List_of_alt_gens>)'</dt>
73%%% <dd>This creates a type whose instances are generated by evaluating the
74%%%   statement block `<Generator>' (this may evaluate to a type, which will
75%%%   then be generated recursively). If an instance of such a type is to be
76%%%   shrunk, the generators in `<List_of_alt_gens>' are first run to produce
77%%%   hopefully simpler instances of the type. Thus, the generators in the
78%%%   second argument should be simpler than the default. The simplest ones
79%%%   should be at the front of the list, since those are the generators
80%%%   preferred by the shrinking subsystem. Like the main `<Generator>', the
81%%%   alternatives may also evaluate to a type, which is generated recursively.
82%%%   </dd>
83%%% <dt>`?LETSHRINK(<List_of_variables>, <List_of_types>, <Generator>)'</dt>
84%%% <dd>This is created by combining a `?LET' and a `?SHRINK' macro. Instances
85%%%   are generated by applying a randomly generated list of values inside
86%%%   `<Generator>' (just like a `?LET', with the added constraint that the
87%%%   variables and types must be provided in a list - alternatively,
88%%%   `<List_of_types>' may be a list or vector type). When shrinking instances
89%%%   of such a type, the sub-instances that were combined to produce it are
90%%%   first tried in place of the failing instance.</dd>
91%%% <dt>`?LAZY(<Generator>)'</dt>
92%%% <dd>This construct returns a type whose only purpose is to delay the
93%%%   evaluation of `<Generator>' (`<Generator>' can return a type, which will
94%%%   be generated recursively). Using this, you can simulate the lazy
95%%%   generation of instances:
96%%%   ``` stream() -> ?LAZY(frequency([ {1,[]}, {3,[0|stream()]} ])). '''
97%%%   The above type produces lists of zeroes with an average length of 3. Note
98%%%   that, had we not enclosed the generator with a `?LAZY' macro, the
99%%%   evaluation would continue indefinitely, due to the eager evaluation of
100%%%   the Erlang language.</dd>
101%%% <dt>`non_empty(<List_or_binary_type>)'</dt>
102%%% <dd>See the documentation for {@link non_empty/1}.</dd>
103%%% <dt>`noshrink(<Type>)'</dt>
104%%% <dd>See the documentation for {@link noshrink/1}.</dd>
105%%% <dt>`default(<Default_value>, <Type>)'</dt>
106%%% <dd>See the documentation for {@link default/2}.</dd>
107%%% <dt>`with_parameter(<Parameter>, <Value>, <Type>)'</dt>
108%%% <dd>See the documentation for {@link with_parameter/3}.</dd>
109%%% <dt>`with_parameters(<Param_value_pairs>, <Type>)'</dt>
110%%% <dd>See the documentation for {@link with_parameters/2}.</dd>
111%%% </dl>
112%%%
113%%% == Size manipulation ==
114%%% The following operators are related to the `size' parameter, which controls
115%%% the maximum size of produced instances. The actual size of a produced
116%%% instance is chosen randomly, but can never exceed the value of the `size'
117%%% parameter at the moment of generation. A more accurate definition is the
118%%% following: the maximum instance of `size S' can never be smaller than the
119%%% maximum instance of `size S-1'. The actual size of an instance is measured
120%%% differently for each type: the actual size of a list is its length, while
121%%% the actual size of a tree may be the number of its internal nodes. Some
122%%% types, e.g. unions, have no notion of size, thus their generation is not
123%%% influenced by the value of `size'. The `size' parameter starts at 1 and
124%%% grows automatically during testing.
125%%%
126%%% <dl>
127%%% <dt>`?SIZED(<S>, <Generator>)'</dt>
128%%% <dd>Creates a new type, whose instances are produced by replacing all
129%%%   appearances of the `<S>' parameter inside the statement block
130%%%   `<Generator>' with the value of the `size' parameter. It's OK for the
131%%%   `<Generator>' to return a type - in that case, an instance of the inner
132%%%   type is generated recursively.</dd>
133%%% <dt>`resize(<New_size>, <Type>)'</dt>
134%%% <dd>See the documentation for {@link resize/2}.</dd>
135%%% </dl>
136
137-module(proper_types).
138-export([is_inst/2, is_inst/3]).
139
140-export([integer/2, float/2, atom/0, binary/0, binary/1, bitstring/0,
141	 bitstring/1, list/1, vector/2, union/1, weighted_union/1, tuple/1,
142	 loose_tuple/1, exactly/1, fixed_list/1, function/2, any/0,
143	 shrink_list/1, safe_union/1, safe_weighted_union/1]).
144-export([integer/0, non_neg_integer/0, pos_integer/0, neg_integer/0, range/2,
145	 float/0, non_neg_float/0, number/0, boolean/0, byte/0, char/0,
146	 list/0, tuple/0, string/0, wunion/1, term/0, timeout/0, arity/0]).
147-export([int/0, nat/0, largeint/0, real/0, bool/0, choose/2, elements/1,
148	 oneof/1, frequency/1, return/1, default/2, orderedlist/1, function0/1,
149	 function1/1, function2/1, function3/1, function4/1,
150	 weighted_default/2]).
151-export([resize/2, non_empty/1, noshrink/1]).
152
153-export([cook_outer/1, is_type/1, equal_types/2, is_raw_type/1, to_binary/1,
154	 from_binary/1, get_prop/2, find_prop/2, safe_is_instance/2,
155	 is_instance/2, unwrap/1, weakly/1, strongly/1, satisfies_all/2,
156	 new_type/2, subtype/2]).
157-export([lazy/1, sized/1, bind/3, shrinkwith/2, add_constraint/3,
158	 native_type/2, distlist/3, with_parameter/3, with_parameters/2,
159	 parameter/1, parameter/2]).
160-export([le/2]).
161
162-export_type([type/0, raw_type/0, extint/0, extnum/0]).
163
164-include("proper_internal.hrl").
165
166
167%%------------------------------------------------------------------------------
168%% Comparison with erl_types
169%%------------------------------------------------------------------------------
170
171%% Missing types
172%% -------------------
173%% will do:
174%%	records, maybe_improper_list(T,S), nonempty_improper_list(T,S)
175%%	maybe_improper_list(), maybe_improper_list(T), iolist, iodata
176%% don't need:
177%%	nonempty_{list,string,maybe_improper_list}
178%% won't do:
179%%	pid, port, ref, identifier, none, no_return, module, mfa, node
180%%	array, dict, digraph, set, gb_tree, gb_set, queue, tid
181
182%% Missing type information
183%% ------------------------
184%% bin types:
185%%	other unit sizes? what about size info?
186%% functions:
187%%	generally some fun, unspecified number of arguments but specified
188%%	return type
189%% any:
190%%	doesn't cover functions and improper lists
191
192
193%%------------------------------------------------------------------------------
194%% Type declaration macros
195%%------------------------------------------------------------------------------
196
197-define(BASIC(PropList), new_type(PropList,basic)).
198-define(WRAPPER(PropList), new_type(PropList,wrapper)).
199-define(CONSTRUCTED(PropList), new_type(PropList,constructed)).
200-define(CONTAINER(PropList), new_type(PropList,container)).
201-define(SUBTYPE(Type,PropList), subtype(PropList,Type)).
202
203
204%%------------------------------------------------------------------------------
205%% Types
206%%------------------------------------------------------------------------------
207
208-type type_kind() :: 'basic' | 'wrapper' | 'constructed' | 'container' | atom().
209-type instance_test() :: fun((proper_gen:imm_instance()) -> boolean())
210                       | {'typed',
211                          fun((proper_types:type(),
212                               proper_gen:imm_instance()) -> boolean())}.
213-type index() :: pos_integer().
214%% @alias
215-type value() :: term().
216%% @private_type
217%% @alias
218-type extint()  :: integer() | 'inf'.
219%% @private_type
220%% @alias
221-type extnum()  :: number()  | 'inf'.
222-type constraint_fun() :: fun((proper_gen:instance()) -> boolean()).
223
224-opaque type() :: {'$type', [type_prop()]}.
225%% A type of the PropEr type system
226%% @type raw_type(). You can consider this as an equivalent of {@type type()}.
227-type raw_type() :: type() | [raw_type()] | loose_tuple(raw_type()) | term().
228-type type_prop_name() :: 'kind' | 'generator' | 'reverse_gen' | 'parts_type'
229			| 'combine' | 'alt_gens' | 'shrink_to_parts'
230			| 'size_transform' | 'is_instance' | 'shrinkers'
231			| 'noshrink' | 'internal_type' | 'internal_types'
232			| 'get_length' | 'split' | 'join' | 'get_indices'
233			| 'remove' | 'retrieve' | 'update' | 'constraints'
234			| 'parameters' | 'env' | 'subenv'.
235
236-type type_prop_value() :: term().
237-type type_prop() ::
238      {'kind', type_kind()}
239    | {'generator', proper_gen:generator()}
240    | {'reverse_gen', proper_gen:reverse_gen()}
241    | {'parts_type', type()}
242    | {'combine', proper_gen:combine_fun()}
243    | {'alt_gens', proper_gen:alt_gens()}
244    | {'shrink_to_parts', boolean()}
245    | {'size_transform', fun((size()) -> size())}
246    | {'is_instance', instance_test()}
247    | {'shrinkers', [proper_shrink:shrinker()]}
248    | {'noshrink', boolean()}
249    | {'internal_type', raw_type()}
250    | {'internal_types', tuple() | maybe_improper_list(type(),type() | [])}
251      %% The items returned by 'remove' must be of this type.
252    | {'get_length', fun((proper_gen:imm_instance()) -> length())}
253      %% If this is a container type, this should return the number of elements
254      %% it contains.
255    | {'split', fun((proper_gen:imm_instance()) -> [proper_gen:imm_instance()])
256	      | fun((length(),proper_gen:imm_instance()) ->
257		    {proper_gen:imm_instance(),proper_gen:imm_instance()})}
258      %% If present, the appropriate form depends on whether get_length is
259      %% defined: if get_length is undefined, this must be in the one-argument
260      %% form (e.g. a tree should be split into its subtrees), else it must be
261      %% in the two-argument form (e.g. a list should be split in two at the
262      %% index provided).
263    | {'join', fun((proper_gen:imm_instance(),proper_gen:imm_instance()) ->
264		   proper_gen:imm_instance())}
265    | {'get_indices', fun((proper_types:type(),
266                           proper_gen:imm_instance()) -> [index()])}
267      %% If this is a container type, this should return a list of indices we
268      %% can use to remove or insert elements from the given instance.
269    | {'remove', fun((index(),proper_gen:imm_instance()) ->
270		     proper_gen:imm_instance())}
271    | {'retrieve', fun((index(), proper_gen:imm_instance() | tuple()
272			       | maybe_improper_list(type(),type() | [])) ->
273		       value() | type())}
274    | {'update', fun((index(),value(),proper_gen:imm_instance()) ->
275		     proper_gen:imm_instance())}
276    | {'constraints', [{constraint_fun(), boolean()}]}
277      %% A list of constraints on instances of this type: each constraint is a
278      %% tuple of a fun that must return 'true' for each valid instance and a
279      %% boolean field that specifies whether the condition is strict.
280    | {'parameters', [{atom(),value()}]}
281    | {'env', term()}
282    | {'subenv', term()}.
283
284
285%%------------------------------------------------------------------------------
286%% Type manipulation functions
287%%------------------------------------------------------------------------------
288
289%% TODO: We shouldn't need the fully qualified type name in the range of these
290%%       functions.
291
292%% @private
293%% TODO: just cook/1 ?
294-spec cook_outer(raw_type()) -> proper_types:type().
295cook_outer(Type = {'$type',_Props}) ->
296    Type;
297cook_outer(RawType) ->
298    if
299	is_tuple(RawType) -> tuple(tuple_to_list(RawType));
300	%% CAUTION: this must handle improper lists
301	is_list(RawType)  -> fixed_list(RawType);
302	%% default case (covers integers, floats, atoms, binaries, ...):
303	true              -> exactly(RawType)
304    end.
305
306%% @private
307-spec is_type(term()) -> boolean().
308is_type({'$type',_Props}) ->
309    true;
310is_type(_) ->
311    false.
312
313%% @private
314-spec equal_types(proper_types:type(), proper_types:type()) -> boolean().
315equal_types(SameType, SameType) ->
316    true;
317equal_types(_, _) ->
318    false.
319
320%% @private
321-spec is_raw_type(term()) -> boolean().
322is_raw_type({'$type',_TypeProps}) ->
323    true;
324is_raw_type(X) ->
325    if
326	is_tuple(X) -> is_raw_type_list(tuple_to_list(X));
327	is_list(X)  -> is_raw_type_list(X);
328	true        -> false
329    end.
330
331-spec is_raw_type_list(maybe_improper_list()) -> boolean().
332%% CAUTION: this must handle improper lists
333is_raw_type_list(List) ->
334    proper_arith:safe_any(fun is_raw_type/1, List).
335
336%% @private
337-spec to_binary(proper_types:type()) -> binary().
338to_binary(Type) ->
339    term_to_binary(Type).
340
341%% @private
342%% TODO: restore: -spec from_binary(binary()) -> proper_types:type().
343from_binary(Binary) ->
344    binary_to_term(Binary).
345
346-spec type_from_list([type_prop()]) -> proper_types:type().
347type_from_list(KeyValueList) ->
348    {'$type',KeyValueList}.
349
350-spec add_prop(type_prop_name(), type_prop_value(), proper_types:type()) ->
351	  proper_types:type().
352add_prop(PropName, Value, {'$type',Props}) ->
353    {'$type',lists:keystore(PropName, 1, Props, {PropName, Value})}.
354
355-spec add_props([type_prop()], proper_types:type()) -> proper_types:type().
356add_props(PropList, {'$type',OldProps}) ->
357    {'$type', lists:foldl(fun({N,_}=NV,Acc) ->
358                    lists:keystore(N, 1, Acc, NV)
359                end, OldProps, PropList)}.
360
361-spec append_to_prop(type_prop_name(), type_prop_value(),
362		     proper_types:type()) -> proper_types:type().
363append_to_prop(PropName, Value, {'$type',Props}) ->
364    Val = case lists:keyfind(PropName, 1, Props) of
365        {PropName, V} ->
366            V;
367        _ ->
368            []
369    end,
370    {'$type', lists:keystore(PropName, 1, Props,
371                             {PropName, lists:reverse([Value|Val])})}.
372
373-spec append_list_to_prop(type_prop_name(), [type_prop_value()],
374			  proper_types:type()) -> proper_types:type().
375append_list_to_prop(PropName, List, {'$type',Props}) ->
376    {PropName, Val} = lists:keyfind(PropName, 1, Props),
377    {'$type', lists:keystore(PropName, 1, Props, {PropName, Val++List})}.
378
379%% @private
380-spec get_prop(type_prop_name(), proper_types:type()) -> type_prop_value().
381get_prop(PropName, {'$type',Props}) ->
382    {_PropName, Val} = lists:keyfind(PropName, 1, Props),
383    Val.
384
385%% @private
386-spec find_prop(type_prop_name(), proper_types:type()) ->
387	  {'ok',type_prop_value()} | 'error'.
388find_prop(PropName, {'$type',Props}) ->
389    case lists:keyfind(PropName, 1, Props) of
390        {PropName, Value} ->
391            {ok, Value};
392        _ ->
393            error
394    end.
395
396%% @private
397-spec new_type([type_prop()], type_kind()) -> proper_types:type().
398new_type(PropList, Kind) ->
399    Type = type_from_list(PropList),
400    add_prop(kind, Kind, Type).
401
402%% @private
403-spec subtype([type_prop()], proper_types:type()) -> proper_types:type().
404%% TODO: should the 'is_instance' function etc. be reset for subtypes?
405subtype(PropList, Type) ->
406    add_props(PropList, Type).
407
408%% @private
409-spec is_inst(proper_gen:instance(), raw_type()) ->
410	  boolean() | {'error',{'typeserver',term()}}.
411is_inst(Instance, RawType) ->
412    is_inst(Instance, RawType, 10).
413
414%% @private
415-spec is_inst(proper_gen:instance(), raw_type(), size()) ->
416	  boolean() | {'error',{'typeserver',term()}}.
417is_inst(Instance, RawType, Size) ->
418    proper:global_state_init_size(Size),
419    Result = safe_is_instance(Instance, RawType),
420    proper:global_state_erase(),
421    Result.
422
423%% @private
424-spec safe_is_instance(proper_gen:imm_instance(), raw_type()) ->
425	  boolean() | {'error',{'typeserver',term()}}.
426safe_is_instance(ImmInstance, RawType) ->
427    try is_instance(ImmInstance, RawType) catch
428	throw:{'$typeserver',SubReason} -> {error, {typeserver,SubReason}}
429    end.
430
431%% @private
432-spec is_instance(proper_gen:imm_instance(), raw_type()) -> boolean().
433%% TODO: If the second argument is not a type, let it pass (don't even check for
434%%	 term equality?) - if it's a raw type, don't cook it, instead recurse
435%%	 into it.
436is_instance(ImmInstance, RawType) ->
437    CleanInstance = proper_gen:clean_instance(ImmInstance),
438    Type = cook_outer(RawType),
439    (case get_prop(kind, Type) of
440	 wrapper     -> wrapper_test(ImmInstance, Type);
441	 constructed -> constructed_test(ImmInstance, Type);
442	 _           -> false
443     end
444     orelse
445     case find_prop(is_instance, Type) of
446	 {ok,{typed, IsInstance}} -> IsInstance(Type, ImmInstance);
447	 {ok,IsInstance} -> IsInstance(ImmInstance);
448	 error           -> false
449     end)
450    andalso weakly(satisfies_all(CleanInstance, Type)).
451
452-spec wrapper_test(proper_gen:imm_instance(), proper_types:type()) -> boolean().
453wrapper_test(ImmInstance, Type) ->
454    %% TODO: check if it's actually a raw type that's returned?
455    lists:any(fun(T) -> is_instance(ImmInstance, T) end, unwrap(Type)).
456
457%% @private
458%% TODO: restore:-spec unwrap(proper_types:type()) -> [proper_types:type(),...].
459%% TODO: check if it's actually a raw type that's returned?
460unwrap(Type) ->
461    RawInnerTypes = proper_gen:alt_gens(Type) ++ [proper_gen:normal_gen(Type)],
462    [cook_outer(T) || T <- RawInnerTypes].
463
464-spec constructed_test(proper_gen:imm_instance(), proper_types:type()) ->
465	  boolean().
466constructed_test({'$used',ImmParts,ImmInstance}, Type) ->
467    PartsType = get_prop(parts_type, Type),
468    Combine = get_prop(combine, Type),
469    is_instance(ImmParts, PartsType) andalso
470    begin
471	%% TODO: check if it's actually a raw type that's returned?
472	%% TODO: move construction code to proper_gen
473	%% TODO: non-type => should we check for strict term equality?
474	RawInnerType = Combine(proper_gen:clean_instance(ImmParts)),
475	is_instance(ImmInstance, RawInnerType)
476    end;
477constructed_test({'$to_part',ImmInstance}, Type) ->
478    PartsType = get_prop(parts_type, Type),
479    get_prop(shrink_to_parts, Type) =:= true andalso
480    %% TODO: we reject non-container types
481    get_prop(kind, PartsType) =:= container andalso
482    case {find_prop(internal_type,PartsType),
483	  find_prop(internal_types,PartsType)} of
484	{{ok,EachPartType},error} ->
485	    %% The parts are in a list or a vector.
486	    is_instance(ImmInstance, EachPartType);
487	{error,{ok,PartTypesList}} ->
488	    %% The parts are in a fixed list.
489	    %% TODO: It should always be a proper list.
490	    lists:any(fun(T) -> is_instance(ImmInstance,T) end, PartTypesList)
491    end;
492constructed_test(_CleanInstance, _Type) ->
493    %% TODO: can we do anything better?
494    false.
495
496%% @private
497-spec weakly({boolean(),boolean()}) -> boolean().
498weakly({B1,_B2}) -> B1.
499
500%% @private
501-spec strongly({boolean(),boolean()}) -> boolean().
502strongly({_B1,B2}) -> B2.
503
504-spec satisfies(proper_gen:instance(), {constraint_fun(),boolean()})
505	  -> {boolean(),boolean()}.
506satisfies(Instance, {Test,false}) ->
507    {true,Test(Instance)};
508satisfies(Instance, {Test,true}) ->
509    Result = Test(Instance),
510    {Result,Result}.
511
512%% @private
513-spec satisfies_all(proper_gen:instance(), proper_types:type()) ->
514	  {boolean(),boolean()}.
515satisfies_all(Instance, Type) ->
516    case find_prop(constraints, Type) of
517	{ok, Constraints} ->
518	    L = [satisfies(Instance, C) || C <- Constraints],
519	    {L1,L2} = lists:unzip(L),
520	    {lists:all(fun(B) -> B end, L1), lists:all(fun(B) -> B end, L2)};
521	error ->
522	    {true,true}
523    end.
524
525
526%%------------------------------------------------------------------------------
527%% Type definition functions
528%%------------------------------------------------------------------------------
529
530%% @private
531-spec lazy(proper_gen:nosize_generator()) -> proper_types:type().
532lazy(Gen) ->
533    ?WRAPPER([
534	{generator, Gen}
535    ]).
536
537%% @private
538-spec sized(proper_gen:sized_generator()) -> proper_types:type().
539sized(Gen) ->
540    ?WRAPPER([
541        {generator, Gen}
542    ]).
543
544%% @private
545-spec bind(raw_type(), proper_gen:combine_fun(), boolean()) ->
546	  proper_types:type().
547bind(RawPartsType, Combine, ShrinkToParts) ->
548    PartsType = cook_outer(RawPartsType),
549    ?CONSTRUCTED([
550	{parts_type, PartsType},
551	{combine, Combine},
552	{shrink_to_parts, ShrinkToParts}
553    ]).
554
555%% @private
556-spec shrinkwith(proper_gen:nosize_generator(), proper_gen:alt_gens()) ->
557	  proper_types:type().
558shrinkwith(Gen, DelaydAltGens) ->
559    ?WRAPPER([
560	{generator, Gen},
561	{alt_gens, DelaydAltGens}
562    ]).
563
564%% @private
565-spec add_constraint(raw_type(), constraint_fun(), boolean()) ->
566	  proper_types:type().
567add_constraint(RawType, Condition, IsStrict) ->
568    Type = cook_outer(RawType),
569    append_to_prop(constraints, {Condition,IsStrict}, Type).
570
571%% @private
572-spec native_type(mod_name(), string()) -> proper_types:type().
573native_type(Mod, TypeStr) ->
574    ?WRAPPER([
575	{generator, fun() ->  proper_gen:native_type_gen(Mod,TypeStr) end}
576    ]).
577
578
579%%------------------------------------------------------------------------------
580%% Basic types
581%%------------------------------------------------------------------------------
582
583%% @doc All integers between `Low' and `High', bounds included.
584%% `Low' and `High' must be Erlang expressions that evaluate to integers, with
585%% `Low =< High'. Additionally, `Low' and `High' may have the value `inf', in
586%% which case they represent minus infinity and plus infinity respectively.
587%% Instances shrink towards 0 if `Low =< 0 =< High', or towards the bound with
588%% the smallest absolute value otherwise.
589-spec integer(extint(), extint()) -> proper_types:type().
590integer(Low, High) ->
591    ?BASIC([
592	{env, {Low, High}},
593	{generator, {typed, fun integer_gen/2}},
594	{is_instance, {typed, fun integer_is_instance/2}},
595	{shrinkers, [fun number_shrinker/3]}
596    ]).
597
598integer_gen(Type, Size) ->
599    {Low, High} = get_prop(env, Type),
600    proper_gen:integer_gen(Size, Low, High).
601
602integer_is_instance(Type, X) ->
603    {Low, High} = get_prop(env, Type),
604    is_integer(X) andalso le(Low, X) andalso le(X, High).
605
606number_shrinker(X, Type, S) ->
607    {Low, High} = get_prop(env, Type),
608    proper_shrink:number_shrinker(X, Low, High, S).
609
610%% @doc All floats between `Low' and `High', bounds included.
611%% `Low' and `High' must be Erlang expressions that evaluate to floats, with
612%% `Low =< High'. Additionally, `Low' and `High' may have the value `inf', in
613%% which case they represent minus infinity and plus infinity respectively.
614%% Instances shrink towards 0.0 if `Low =< 0.0 =< High', or towards the bound
615%% with the smallest absolute value otherwise.
616-spec float(extnum(), extnum()) -> proper_types:type().
617float(Low, High) ->
618    ?BASIC([
619	{env, {Low, High}},
620	{generator, {typed, fun float_gen/2}},
621	{is_instance, {typed, fun float_is_instance/2}},
622	{shrinkers, [fun number_shrinker/3]}
623    ]).
624
625float_gen(Type, Size) ->
626    {Low, High} = get_prop(env, Type),
627    proper_gen:float_gen(Size, Low, High).
628
629float_is_instance(Type, X) ->
630    {Low, High} = get_prop(env, Type),
631    is_float(X) andalso le(Low, X) andalso le(X, High).
632
633%% @private
634-spec le(extnum(), extnum()) -> boolean().
635le(inf, _B) -> true;
636le(_A, inf) -> true;
637le(A, B)    -> A =< B.
638
639%% @doc All atoms. All atoms used internally by PropEr start with a '`$'', so
640%% such atoms will never be produced as instances of this type. You should also
641%% refrain from using such atoms in your code, to avoid a potential clash.
642%% Instances shrink towards the empty atom, ''.
643-spec atom() -> proper_types:type().
644atom() ->
645    ?WRAPPER([
646	{generator, fun proper_gen:atom_gen/1},
647	{reverse_gen, fun proper_gen:atom_rev/1},
648	{size_transform, fun(Size) -> erlang:min(Size,255) end},
649	{is_instance, fun atom_is_instance/1}
650    ]).
651
652atom_is_instance(X) ->
653    is_atom(X)
654    %% We return false for atoms starting with '$', since these are
655    %% atoms used internally and never produced by the atom generator.
656    andalso (X =:= '' orelse hd(atom_to_list(X)) =/= $$).
657
658%% @doc All binaries. Instances shrink towards the empty binary, `<<>>'.
659-spec binary() -> proper_types:type().
660binary() ->
661    ?WRAPPER([
662	{generator, fun proper_gen:binary_gen/1},
663	{reverse_gen, fun proper_gen:binary_rev/1},
664	{is_instance, fun erlang:is_binary/1}
665    ]).
666
667%% @doc All binaries with a byte size of `Len'.
668%% `Len' must be an Erlang expression that evaluates to a non-negative integer.
669%% Instances shrink towards binaries of zeroes.
670-spec binary(length()) -> proper_types:type().
671binary(Len) ->
672    ?WRAPPER([
673	{env, Len},
674	{generator, {typed, fun binary_len_gen/1}},
675	{reverse_gen, fun proper_gen:binary_rev/1},
676	{is_instance, {typed, fun binary_len_is_instance/2}}
677    ]).
678
679binary_len_gen(Type) ->
680    Len = get_prop(env, Type),
681    proper_gen:binary_len_gen(Len).
682
683binary_len_is_instance(Type, X) ->
684    Len = get_prop(env, Type),
685    is_binary(X) andalso byte_size(X) =:= Len.
686
687%% @doc All bitstrings. Instances shrink towards the empty bitstring, `<<>>'.
688-spec bitstring() -> proper_types:type().
689bitstring() ->
690    ?WRAPPER([
691	{generator, fun proper_gen:bitstring_gen/1},
692	{reverse_gen, fun proper_gen:bitstring_rev/1},
693	{is_instance, fun erlang:is_bitstring/1}
694    ]).
695
696%% @doc All bitstrings with a bit size of `Len'.
697%% `Len' must be an Erlang expression that evaluates to a non-negative integer.
698%% Instances shrink towards bitstrings of zeroes
699-spec bitstring(length()) -> proper_types:type().
700bitstring(Len) ->
701    ?WRAPPER([
702	{env, Len},
703	{generator, {typed, fun bitstring_len_gen/1}},
704	{reverse_gen, fun proper_gen:bitstring_rev/1},
705	{is_instance, {typed, fun bitstring_len_is_instance/2}}
706    ]).
707
708bitstring_len_gen(Type) ->
709    Len = get_prop(env, Type),
710    proper_gen:bitstring_len_gen(Len).
711
712bitstring_len_is_instance(Type, X) ->
713    Len = get_prop(env, Type),
714    is_bitstring(X) andalso bit_size(X) =:= Len.
715
716%% @doc All lists containing elements of type `ElemType'.
717%% Instances shrink towards the empty list, `[]'.
718-spec list(ElemType::raw_type()) -> proper_types:type().
719% TODO: subtyping would be useful here (list, vector, fixed_list)
720list(RawElemType) ->
721    ElemType = cook_outer(RawElemType),
722    ?CONTAINER([
723	{generator, {typed, fun list_gen/2}},
724	{is_instance, {typed, fun list_is_instance/2}},
725	{internal_type, ElemType},
726	{get_length, fun erlang:length/1},
727	{split, fun lists:split/2},
728	{join, fun lists:append/2},
729	{get_indices, fun list_get_indices/2},
730	{remove, fun proper_arith:list_remove/2},
731	{retrieve, fun lists:nth/2},
732	{update, fun proper_arith:list_update/3}
733    ]).
734
735list_gen(Type, Size) ->
736    ElemType = get_prop(internal_type, Type),
737    proper_gen:list_gen(Size, ElemType).
738
739list_is_instance(Type, X) ->
740    ElemType = get_prop(internal_type, Type),
741    list_test(X, ElemType).
742
743%% @doc A type that generates exactly the list `List'. Instances shrink towards
744%% shorter sublists of the original list.
745-spec shrink_list([term()]) -> proper_types:type().
746shrink_list(List) ->
747    ?CONTAINER([
748	{env, List},
749	{generator, {typed, fun shrink_list_gen/1}},
750	{is_instance, {typed, fun shrink_list_is_instance/2}},
751	{get_length, fun erlang:length/1},
752	{split, fun lists:split/2},
753	{join, fun lists:append/2},
754	{get_indices, fun list_get_indices/2},
755	{remove, fun proper_arith:list_remove/2}
756    ]).
757
758shrink_list_gen(Type) ->
759    get_prop(env, Type).
760
761shrink_list_is_instance(Type, X) ->
762    List = get_prop(env, Type),
763    is_sublist(X, List).
764
765-spec is_sublist([term()], [term()]) -> boolean().
766is_sublist([], _) -> true;
767is_sublist(_, []) -> false;
768is_sublist([H|T1], [H|T2]) -> is_sublist(T1, T2);
769is_sublist(Slice, [_|T2]) -> is_sublist(Slice, T2).
770
771-spec list_test(proper_gen:imm_instance(), proper_types:type()) -> boolean().
772list_test(X, ElemType) ->
773    is_list(X) andalso lists:all(fun(E) -> is_instance(E, ElemType) end, X).
774
775%% @private
776-spec list_get_indices(proper_gen:generator(), list()) -> [position()].
777list_get_indices(_, List) ->
778    lists:seq(1, length(List)).
779
780%% @private
781%% This assumes that:
782%% - instances of size S are always valid instances of size >S
783%% - any recursive calls inside Gen are lazy
784-spec distlist(size(), proper_gen:sized_generator(), boolean()) ->
785	  proper_types:type().
786distlist(Size, Gen, NonEmpty) ->
787    ParentType = case NonEmpty of
788		     true  -> non_empty(list(Gen(Size)));
789		     false -> list(Gen(Size))
790		 end,
791    ?SUBTYPE(ParentType, [
792	{subenv, {Size, Gen, NonEmpty}},
793	{generator, {typed, fun distlist_gen/1}}
794    ]).
795
796distlist_gen(Type) ->
797    {Size, Gen, NonEmpty} = get_prop(subenv, Type),
798    proper_gen:distlist_gen(Size, Gen, NonEmpty).
799
800%% @doc All lists of length `Len' containing elements of type `ElemType'.
801%% `Len' must be an Erlang expression that evaluates to a non-negative integer.
802-spec vector(length(), ElemType::raw_type()) -> proper_types:type().
803vector(Len, RawElemType) ->
804    ElemType = cook_outer(RawElemType),
805    ?CONTAINER([
806	{env, Len},
807	{generator, {typed, fun vector_gen/1}},
808	{is_instance, {typed, fun vector_is_instance/2}},
809	{internal_type, ElemType},
810	{get_indices, fun vector_get_indices/2},
811	{retrieve, fun lists:nth/2},
812	{update, fun proper_arith:list_update/3}
813    ]).
814
815vector_gen(Type) ->
816    Len = get_prop(env, Type),
817    ElemType = get_prop(internal_type, Type),
818    proper_gen:vector_gen(Len, ElemType).
819
820vector_is_instance(Type, X) ->
821    Len = get_prop(env, Type),
822    ElemType = get_prop(internal_type, Type),
823    is_list(X)
824    andalso length(X) =:= Len
825    andalso lists:all(fun(E) -> is_instance(E, ElemType) end, X).
826
827vector_get_indices(Type, _X) ->
828    lists:seq(1, get_prop(env, Type)).
829
830%% @doc The union of all types in `ListOfTypes'. `ListOfTypes' can't be empty.
831%% The random instance generator is equally likely to choose any one of the
832%% types in `ListOfTypes'. The shrinking subsystem will always try to shrink an
833%% instance of a type union to an instance of the first type in `ListOfTypes',
834%% thus you should write the simplest case first.
835-spec union(ListOfTypes::[raw_type(),...]) -> proper_types:type().
836union(RawChoices) ->
837    Choices = [cook_outer(C) || C <- RawChoices],
838    ?BASIC([
839	{env, Choices},
840	{generator, {typed, fun union_gen/1}},
841	{is_instance, {typed, fun union_is_instance/2}},
842	{shrinkers, [fun union_shrinker_1/3, fun union_shrinker_2/3]}
843    ]).
844
845union_gen(Type) ->
846    Choices = get_prop(env,Type),
847    proper_gen:union_gen(Choices).
848
849union_is_instance(Type, X) ->
850    Choices = get_prop(env, Type),
851    lists:any(fun(C) -> is_instance(X, C) end, Choices).
852
853union_shrinker_1(X, Type, S) ->
854    Choices = get_prop(env, Type),
855    proper_shrink:union_first_choice_shrinker(X, Choices, S).
856
857union_shrinker_2(X, Type, S) ->
858    Choices = get_prop(env, Type),
859    proper_shrink:union_recursive_shrinker(X, Choices, S).
860
861%% @doc A specialization of {@link union/1}, where each type in `ListOfTypes' is
862%% assigned a frequency. Frequencies must be Erlang expressions that evaluate to
863%% positive integers. Types with larger frequencies are more likely to be chosen
864%% by the random instance generator. The shrinking subsystem will ignore the
865%% frequencies and try to shrink towards the first type in the list.
866-spec weighted_union(ListOfTypes::[{frequency(),raw_type()},...]) ->
867          proper_types:type().
868weighted_union(RawFreqChoices) ->
869    CookFreqType = fun({Freq,RawType}) -> {Freq,cook_outer(RawType)} end,
870    FreqChoices = lists:map(CookFreqType, RawFreqChoices),
871    Choices = [T || {_F,T} <- FreqChoices],
872    ?SUBTYPE(union(Choices), [
873	{subenv, FreqChoices},
874	{generator, {typed, fun weighted_union_gen/1}}
875    ]).
876
877weighted_union_gen(Gen) ->
878    FreqChoices = get_prop(subenv, Gen),
879    proper_gen:weighted_union_gen(FreqChoices).
880
881%% @private
882-spec safe_union([raw_type(),...]) -> proper_types:type().
883safe_union(RawChoices) ->
884    Choices = [cook_outer(C) || C <- RawChoices],
885    subtype(
886      [{subenv, Choices},
887       {generator, {typed, fun safe_union_gen/1}}],
888      union(Choices)).
889
890safe_union_gen(Type) ->
891    Choices = get_prop(subenv, Type),
892    proper_gen:safe_union_gen(Choices).
893
894%% @private
895-spec safe_weighted_union([{frequency(),raw_type()},...]) ->
896         proper_types:type().
897safe_weighted_union(RawFreqChoices) ->
898    CookFreqType = fun({Freq,RawType}) ->
899			   {Freq,cook_outer(RawType)} end,
900    FreqChoices = lists:map(CookFreqType, RawFreqChoices),
901    Choices = [T || {_F,T} <- FreqChoices],
902    subtype([{subenv, FreqChoices},
903             {generator, {typed, fun safe_weighted_union_gen/1}}],
904            union(Choices)).
905
906safe_weighted_union_gen(Type) ->
907    FreqChoices = get_prop(subenv, Type),
908    proper_gen:safe_weighted_union_gen(FreqChoices).
909
910%% @doc All tuples whose i-th element is an instance of the type at index i of
911%% `ListOfTypes'. Also written simply as a tuple of types.
912-spec tuple(ListOfTypes::[raw_type()]) -> proper_types:type().
913tuple(RawFields) ->
914    Fields = [cook_outer(F) || F <- RawFields],
915    ?CONTAINER([
916	{env, Fields},
917	{generator, {typed, fun tuple_gen/1}},
918	{is_instance, {typed, fun tuple_is_instance/2}},
919	{internal_types, list_to_tuple(Fields)},
920	{get_indices, fun tuple_get_indices/2},
921	{retrieve, fun erlang:element/2},
922	{update, fun tuple_update/3}
923    ]).
924
925tuple_gen(Type) ->
926    Fields = get_prop(env, Type),
927    proper_gen:tuple_gen(Fields).
928
929tuple_is_instance(Type, X) ->
930    Fields = get_prop(env, Type),
931    is_tuple(X) andalso fixed_list_test(tuple_to_list(X), Fields).
932
933tuple_get_indices(Type, _X) ->
934    lists:seq(1, length(get_prop(env, Type))).
935
936-spec tuple_update(index(), value(), tuple()) -> tuple().
937tuple_update(Index, NewElem, Tuple) ->
938    setelement(Index, Tuple, NewElem).
939
940%% @doc Tuples whose elements are all of type `ElemType'.
941%% Instances shrink towards the 0-size tuple, `{}'.
942-spec loose_tuple(ElemType::raw_type()) -> proper_types:type().
943loose_tuple(RawElemType) ->
944    ElemType = cook_outer(RawElemType),
945    ?WRAPPER([
946	{env, ElemType},
947	{generator, {typed, fun loose_tuple_gen/2}},
948	{reverse_gen, {typed, fun loose_tuple_rev/2}},
949	{is_instance, {typed, fun loose_tuple_is_instance/2}}
950    ]).
951
952loose_tuple_gen(Type, Size) ->
953    ElemType = get_prop(env, Type),
954    proper_gen:loose_tuple_gen(Size, ElemType).
955
956loose_tuple_rev(Type, X) ->
957    ElemType = get_prop(env, Type),
958    proper_gen:loose_tuple_rev(X, ElemType).
959
960loose_tuple_is_instance(Type, X) ->
961    ElemType = get_prop(env, Type),
962    is_tuple(X) andalso list_test(tuple_to_list(X), ElemType).
963
964%% @doc Singleton type consisting only of `E'. `E' must be an evaluated term.
965%% Also written simply as `E'.
966-spec exactly(term()) -> proper_types:type().
967exactly(E) ->
968    ?BASIC([
969	{env, E},
970	{generator, {typed, fun exactly_gen/1}},
971	{is_instance, {typed, fun exactly_is_instance/2}}
972    ]).
973
974exactly_gen(Type) ->
975    E = get_prop(env, Type),
976    proper_gen:exactly_gen(E).
977
978exactly_is_instance(Type, X) ->
979    E = get_prop(env, Type),
980    X =:= E.
981
982%% @doc All lists whose i-th element is an instance of the type at index i of
983%% `ListOfTypes'. Also written simply as a list of types.
984-spec fixed_list(ListOfTypes::maybe_improper_list(raw_type(),raw_type()|[])) ->
985	  proper_types:type().
986fixed_list(MaybeImproperRawFields) ->
987    %% CAUTION: must handle improper lists
988    {Fields, Internal, Len, Retrieve, Update} =
989	case proper_arith:cut_improper_tail(MaybeImproperRawFields) of
990	    % TODO: have cut_improper_tail return the length and use it in test?
991	    {ProperRawHead, ImproperRawTail} ->
992		HeadLen = length(ProperRawHead),
993		CookedHead = [cook_outer(F) || F <- ProperRawHead],
994		CookedTail = cook_outer(ImproperRawTail),
995		{{CookedHead,CookedTail},
996		 CookedHead ++ CookedTail,
997		 HeadLen + 1,
998		 fun(I,L) -> improper_list_retrieve(I, L, HeadLen) end,
999		 fun(I,V,L) -> improper_list_update(I, V, L, HeadLen) end};
1000	    ProperRawFields ->
1001		LocalFields = [cook_outer(F) || F <- ProperRawFields],
1002		{LocalFields,
1003		 LocalFields,
1004		 length(ProperRawFields),
1005		 fun lists:nth/2,
1006		 fun proper_arith:list_update/3}
1007	end,
1008    ?CONTAINER([
1009	{env, {Fields, Len}},
1010	{generator, {typed, fun fixed_list_gen/1}},
1011	{is_instance, {typed, fun fixed_list_is_instance/2}},
1012	{internal_types, Internal},
1013	{get_indices, fun fixed_list_get_indices/2},
1014	{retrieve, Retrieve},
1015	{update, Update}
1016    ]).
1017
1018fixed_list_gen(Type) ->
1019    {Fields, _} = get_prop(env, Type),
1020    proper_gen:fixed_list_gen(Fields).
1021
1022fixed_list_is_instance(Type, X) ->
1023    {Fields, _} = get_prop(env, Type),
1024    fixed_list_test(X, Fields).
1025
1026fixed_list_get_indices(Type, _X) ->
1027    {_, Len} = get_prop(env, Type),
1028    lists:seq(1, Len).
1029
1030-spec fixed_list_test(proper_gen:imm_instance(),
1031		      [proper_types:type()] | {[proper_types:type()],
1032					       proper_types:type()}) ->
1033	  boolean().
1034fixed_list_test(X, {ProperHead,ImproperTail}) ->
1035    is_list(X) andalso
1036    begin
1037	ProperHeadLen = length(ProperHead),
1038	proper_arith:head_length(X) >= ProperHeadLen andalso
1039	begin
1040	    {XHead,XTail} = lists:split(ProperHeadLen, X),
1041	    fixed_list_test(XHead, ProperHead)
1042	    andalso is_instance(XTail, ImproperTail)
1043	end
1044    end;
1045fixed_list_test(X, ProperFields) ->
1046    is_list(X)
1047    andalso length(X) =:= length(ProperFields)
1048    andalso lists:all(fun({E,T}) -> is_instance(E, T) end,
1049		      lists:zip(X, ProperFields)).
1050
1051%% TODO: Move these 2 functions to proper_arith?
1052-spec improper_list_retrieve(index(), nonempty_improper_list(value(),value()),
1053			     pos_integer()) -> value().
1054improper_list_retrieve(Index, List, HeadLen) ->
1055    case Index =< HeadLen of
1056	true  -> lists:nth(Index, List);
1057	false -> lists:nthtail(HeadLen, List)
1058    end.
1059
1060-spec improper_list_update(index(), value(),
1061			   nonempty_improper_list(value(),value()),
1062			   pos_integer()) ->
1063	  nonempty_improper_list(value(),value()).
1064improper_list_update(Index, Value, List, HeadLen) ->
1065    case Index =< HeadLen of
1066	%% TODO: This happens to work, but is not implied by list_update's spec.
1067	true  -> proper_arith:list_update(Index, Value, List);
1068	false -> lists:sublist(List, HeadLen) ++ Value
1069    end.
1070
1071%% @doc All pure functions that map instances of `ArgTypes' to instances of
1072%% `RetType'. The syntax `function(Arity, RetType)' is also acceptable.
1073-spec function(ArgTypes::[raw_type()] | arity(), RetType::raw_type()) ->
1074          proper_types:type().
1075function(Arity, RawRetType) when is_integer(Arity), Arity >= 0, Arity =< 255 ->
1076    RetType = cook_outer(RawRetType),
1077    ?BASIC([
1078	{env, {Arity, RetType}},
1079	{generator, {typed, fun function_gen/1}},
1080	{is_instance, {typed, fun function_is_instance/2}}
1081    ]);
1082function(RawArgTypes, RawRetType) ->
1083    function(length(RawArgTypes), RawRetType).
1084
1085function_gen(Type) ->
1086    {Arity, RetType} = get_prop(env, Type),
1087    proper_gen:function_gen(Arity, RetType).
1088
1089function_is_instance(Type, X) ->
1090    {Arity, RetType} = get_prop(env, Type),
1091    is_function(X, Arity)
1092    %% TODO: what if it's not a function we produced?
1093    andalso equal_types(RetType, proper_gen:get_ret_type(X)).
1094
1095%% @doc All Erlang terms (that PropEr can produce). For reasons of efficiency,
1096%% functions are never produced as instances of this type.<br />
1097%% CAUTION: Instances of this type are expensive to produce, shrink and instance-
1098%% check, both in terms of processing time and consumed memory. Only use this
1099%% type if you are certain that you need it.
1100-spec any() -> proper_types:type().
1101any() ->
1102    AllTypes = [integer(),float(),atom(),bitstring(),?LAZY(loose_tuple(any())),
1103		?LAZY(list(any()))],
1104    ?SUBTYPE(union(AllTypes), [
1105	{generator, fun proper_gen:any_gen/1}
1106    ]).
1107
1108
1109%%------------------------------------------------------------------------------
1110%% Type aliases
1111%%------------------------------------------------------------------------------
1112
1113%% @equiv integer(inf, inf)
1114-spec integer() -> proper_types:type().
1115integer() -> integer(inf, inf).
1116
1117%% @equiv integer(0, inf)
1118-spec non_neg_integer() -> proper_types:type().
1119non_neg_integer() -> integer(0, inf).
1120
1121%% @equiv integer(1, inf)
1122-spec pos_integer() -> proper_types:type().
1123pos_integer() -> integer(1, inf).
1124
1125%% @equiv integer(inf, -1)
1126-spec neg_integer() -> proper_types:type().
1127neg_integer() -> integer(inf, -1).
1128
1129%% @equiv integer(Low, High)
1130-spec range(extint(), extint()) -> proper_types:type().
1131range(Low, High) -> integer(Low, High).
1132
1133%% @equiv float(inf, inf)
1134-spec float() -> proper_types:type().
1135float() -> float(inf, inf).
1136
1137%% @equiv float(0.0, inf)
1138-spec non_neg_float() -> proper_types:type().
1139non_neg_float() -> float(0.0, inf).
1140
1141%% @equiv union([integer(), float()])
1142-spec number() -> proper_types:type().
1143number() -> union([integer(), float()]).
1144
1145%% @doc The atoms `true' and `false'. Instances shrink towards `false'.
1146-spec boolean() -> proper_types:type().
1147boolean() -> union(['false', 'true']).
1148
1149%% @equiv integer(0, 255)
1150-spec byte() -> proper_types:type().
1151byte() -> integer(0, 255).
1152
1153%% @equiv integer(0, 16#10ffff)
1154-spec char() -> proper_types:type().
1155char() -> integer(0, 16#10ffff).
1156
1157%% @equiv list(any())
1158-spec list() -> proper_types:type().
1159list() -> list(any()).
1160
1161%% @equiv loose_tuple(any())
1162-spec tuple() -> proper_types:type().
1163tuple() -> loose_tuple(any()).
1164
1165%% @equiv list(char())
1166-spec string() -> proper_types:type().
1167string() -> list(char()).
1168
1169%% @equiv weighted_union(FreqChoices)
1170-spec wunion([{frequency(),raw_type()},...]) -> proper_types:type().
1171wunion(FreqChoices) -> weighted_union(FreqChoices).
1172
1173%% @equiv any()
1174-spec term() -> proper_types:type().
1175term() -> any().
1176
1177%% @equiv union([non_neg_integer() | infinity])
1178-spec timeout() -> proper_types:type().
1179timeout() -> union([non_neg_integer(), 'infinity']).
1180
1181%% @equiv integer(0, 255)
1182-spec arity() -> proper_types:type().
1183arity() -> integer(0, 255).
1184
1185
1186%%------------------------------------------------------------------------------
1187%% QuickCheck compatibility types
1188%%------------------------------------------------------------------------------
1189
1190%% @doc Small integers (bound by the current value of the `size' parameter).
1191%% Instances shrink towards `0'.
1192-spec int() -> proper_types:type().
1193int() -> ?SIZED(Size, integer(-Size,Size)).
1194
1195%% @doc Small non-negative integers (bound by the current value of the `size'
1196%% parameter). Instances shrink towards `0'.
1197-spec nat() -> proper_types:type().
1198nat() -> ?SIZED(Size, integer(0,Size)).
1199
1200%% @equiv integer()
1201-spec largeint() -> proper_types:type().
1202largeint() -> integer().
1203
1204%% @equiv float()
1205-spec real() -> proper_types:type().
1206real() -> float().
1207
1208%% @equiv boolean()
1209-spec bool() -> proper_types:type().
1210bool() -> boolean().
1211
1212%% @equiv integer(Low, High)
1213-spec choose(extint(), extint()) -> proper_types:type().
1214choose(Low, High) -> integer(Low, High).
1215
1216%% @equiv union(Choices)
1217-spec elements([raw_type(),...]) -> proper_types:type().
1218elements(Choices) -> union(Choices).
1219
1220%% @equiv union(Choices)
1221-spec oneof([raw_type(),...]) -> proper_types:type().
1222oneof(Choices) -> union(Choices).
1223
1224%% @equiv weighted_union(Choices)
1225-spec frequency([{frequency(),raw_type()},...]) -> proper_types:type().
1226frequency(FreqChoices) -> weighted_union(FreqChoices).
1227
1228%% @equiv exactly(E)
1229-spec return(term()) -> proper_types:type().
1230return(E) -> exactly(E).
1231
1232%% @doc Adds a default value, `Default', to `Type'.
1233%% The default serves as a primary shrinking target for instances, while it
1234%% is also chosen by the random instance generation subsystem half the time.
1235-spec default(raw_type(), raw_type()) -> proper_types:type().
1236default(Default, Type) ->
1237    union([Default, Type]).
1238
1239%% @doc All sorted lists containing elements of type `ElemType'.
1240%% Instances shrink towards the empty list, `[]'.
1241-spec orderedlist(ElemType::raw_type()) -> proper_types:type().
1242orderedlist(RawElemType) ->
1243    ?LET(L, list(RawElemType), lists:sort(L)).
1244
1245%% @equiv function(0, RetType)
1246-spec function0(raw_type()) -> proper_types:type().
1247function0(RetType) ->
1248    function(0, RetType).
1249
1250%% @equiv function(1, RetType)
1251-spec function1(raw_type()) -> proper_types:type().
1252function1(RetType) ->
1253    function(1, RetType).
1254
1255%% @equiv function(2, RetType)
1256-spec function2(raw_type()) -> proper_types:type().
1257function2(RetType) ->
1258    function(2, RetType).
1259
1260%% @equiv function(3, RetType)
1261-spec function3(raw_type()) -> proper_types:type().
1262function3(RetType) ->
1263    function(3, RetType).
1264
1265%% @equiv function(4, RetType)
1266-spec function4(raw_type()) -> proper_types:type().
1267function4(RetType) ->
1268    function(4, RetType).
1269
1270%% @doc A specialization of {@link default/2}, where `Default' and `Type' are
1271%% assigned weights to be considered by the random instance generator. The
1272%% shrinking subsystem will ignore the weights and try to shrink using the
1273%% default value.
1274-spec weighted_default({frequency(),raw_type()}, {frequency(),raw_type()}) ->
1275	  proper_types:type().
1276weighted_default(Default, Type) ->
1277    weighted_union([Default, Type]).
1278
1279
1280%%------------------------------------------------------------------------------
1281%% Additional type specification functions
1282%%------------------------------------------------------------------------------
1283
1284%% @doc Overrides the `size' parameter used when generating instances of
1285%% `Type' with `NewSize'. Has no effect on size-less types, such as unions.
1286%% Also, this will not affect the generation of any internal types contained in
1287%% `Type', such as the elements of a list - those will still be generated
1288%% using the test-wide value of `size'. One use of this function is to modify
1289%% types to produce instances that grow faster or slower, like so:
1290%% ```?SIZED(Size, resize(Size * 2, list(integer()))'''
1291%% The above specifies a list type that grows twice as fast as normal lists.
1292-spec resize(size(), Type::raw_type()) -> proper_types:type().
1293resize(NewSize, RawType) ->
1294    Type = cook_outer(RawType),
1295    case find_prop(size_transform, Type) of
1296	{ok,Transform} ->
1297	    add_prop(size_transform, fun(_S) -> Transform(NewSize) end, Type);
1298	error ->
1299	    add_prop(size_transform, fun(_S) -> NewSize end, Type)
1300    end.
1301
1302%% @doc This is a predefined constraint that can be applied to random-length
1303%% list and binary types to ensure that the produced values are never empty.
1304%%
1305%% e.g. {@link list/0}, {@link string/0}, {@link binary/0})
1306-spec non_empty(ListType::raw_type()) -> proper_types:type().
1307non_empty(RawListType) ->
1308    ?SUCHTHAT(L, RawListType, L =/= [] andalso L =/= <<>>).
1309
1310%% @doc Creates a new type which is equivalent to `Type', but whose instances
1311%% are never shrunk by the shrinking subsystem.
1312-spec noshrink(Type::raw_type()) -> proper_types:type().
1313noshrink(RawType) ->
1314    add_prop(noshrink, true, cook_outer(RawType)).
1315
1316%% @doc Associates the atom key `Parameter' with the value `Value' while
1317%% generating instances of `Type'.
1318-spec with_parameter(atom(), value(), Type::raw_type()) -> proper_types:type().
1319with_parameter(Parameter, Value, RawType) ->
1320    with_parameters([{Parameter,Value}], RawType).
1321
1322%% @doc Similar to {@link with_parameter/3}, but accepts a list of
1323%% `{Parameter, Value}' pairs.
1324-spec with_parameters([{atom(),value()}], Type::raw_type()) ->
1325          proper_types:type().
1326with_parameters(PVlist, RawType) ->
1327    Type = cook_outer(RawType),
1328    case find_prop(parameters, Type) of
1329	{ok,Params} when is_list(Params) ->
1330	    append_list_to_prop(parameters, PVlist, Type);
1331	error ->
1332	    add_prop(parameters, PVlist, Type)
1333    end.
1334
1335%% @doc Returns the value associated with `Parameter', or `Default' in case
1336%% `Parameter' is not associated with any value.
1337-spec parameter(atom(), value()) -> value().
1338parameter(Parameter, Default) ->
1339    Parameters =
1340	case erlang:get('$parameters') of
1341	    undefined -> [];
1342	    List -> List
1343	end,
1344    proplists:get_value(Parameter, Parameters, Default).
1345
1346%% @equiv parameter(Parameter, undefined)
1347-spec parameter(atom()) -> value().
1348parameter(Parameter) ->
1349    parameter(Parameter, undefined).
1350