1:-module(test_helpers, [alphabetic_sequence/2
    2		       ,random_iterm/11
    3                       ,partly_random_iterm/11
    4		       ]).    5
    6:-use_module(prolog/src/indexed_terms).
 alphabetic_sequence(+Width, -Sequence) is det
Create an ordered sequence of letters of the alphabet, starting at a.

Use this to generate iterms to test with rather than hand-crafting them.

To be done
- This could allow an alphabetic sequence of any length, or even skipping elements, mapping interval/4 values to letters.
   20alphabetic_sequence(K,T):-
   21	A = v(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,x,y,z)
   22	,slice(A,1,K,T).
 random_iterm(?F, -A, -T, -I, -J, -K, -N, -S, -M, -Mn) is det
Generate a random iterm for testing.

T is bound to a numeric iterm with functor F and arity A, from 1 to 100. Arguments of this iterm form a sequence starting at 1 and ending at A, proceeding in strides of N.

I,J,K and N are bound to valid window/6 parameters for that term.

S, M and Mn are bound to the span, window size and maximum stride for a window with those parameters over T, respectively.

Use this to avoid creating iterms by hand (and risk getting spurious results with magic values).

To be done
- reduce boilerplate in this and partly_random_iterm/11
- this needs to backtrack from invalid parameter combinations.
   48random_iterm(F,A,T,I,J,K,N,S,M,Mn,asc):-
   49	!
   50	,random_between(1,100,A)
   51	% Default functor is v. Doesn't really affect anything...
   52	,(   var(F)
   53	 ->  F = v
   54	 ;   true
   55	)
   56	% Off-by-one correction - we want the sequence
   57	% to start at 1 and still have arity A.
   58	,(   A > 1
   59	->  sequence(F,1,A,1,T)
   60	;   sequence(F,1,A,0,T)
   61	)
   62	,assertion(compound_name_arity(T,_,A))
   63	,random_between(1,A,I)
   64	,random_between(I,A,J)
   65	,window_span(I,J,S)
   66	,random_between(0,S,K)
   67	,(   I == J
   68	 ->  N = 0
   69	 ;   random_between(1,S,N)
   70	 )
   71	,window_size(I,J,K,N,M)
   72	,maximum_stride(I,J,K,Mn).
   73
   74random_iterm(F,A,T,I,J,K,N,S,M,Mn,desc):-
   75	random_between(1,100,A)
   76	,(   var(F)
   77	 ->  F = v
   78	 ;   true
   79	)
   80	,A_ is A + 1
   81	,sequence(F,1,A_,1,T)
   82	,random_between(1,A,I)
   83	% Will allow I == J! So not strictly descending.
   84	,random_between(1,I,J)
   85	,window_span(I,J,S)
   86	,random_between(0,S,K)
   87	,(   I == J
   88	 ->  N = 0
   89	 ;   random_between(1,S,N)
   90	 )
   91	,window_size(I,J,K,N,M)
   92	,maximum_stride(I,J,K,Mn).
 partly_random_iterm(?F, ?A, +T, ?I, ?J, ?K, ?N, -S, -M, -Mn) is det
As random_iterm/10 but any number of items may be bound.

Use this to randomise only some of an iterm's parameters. Window size, span and max stride are still calculated according to the given, or generated, parameters (you can't specify them and get the parameters for a window with that size, span and max stride).

Note: the only "input" that can't be ground in advance is T. That one is a blank iterm always. But its arity and functor name can be given.

  109partly_random_iterm(F,A,T,I,J,K,N,S,M,Mn,asc):-
  110	!
  111	,maybe_random_between(1,100,A)
  112	,(   var(F)
  113	 ->  F = v
  114	 ;   true
  115	)
  116	,(   A > 1
  117	->  sequence(F,1,A,1,T)
  118	;   sequence(F,1,A,0,T)
  119	)
  120	,maybe_random_between(1,A,I)
  121	,(   N == 0
  122	 ->  J = I
  123	 ;   maybe_random_between(I,A,J)
  124	 )
  125	,window_span(I,J,S)
  126	,maybe_random_between(0,S,K)
  127	,(   I == J
  128	 ->  N = 0
  129	 ;   maybe_random_between(1,S,N)
  130	 )
  131	,window_size(I,J,K,N,M)
  132	,maximum_stride(I,J,K,Mn).
  133
  134partly_random_iterm(F,A,T,I,J,K,N,S,M,Mn,desc):-
  135	maybe_random_between(1,100,A)
  136	,(   var(F)
  137	 ->  F = v
  138	 ;   true
  139	)
  140	,(   A > 1
  141	->  sequence(F,1,A,1,T)
  142	;   sequence(F,1,A,0,T)
  143	)
  144	,maybe_random_between(1,A,I)
  145	,(   N == 0
  146	 ->  J = I
  147	 ;   maybe_random_between(1,I,J)
  148	 )
  149	,window_span(I,J,S)
  150	,maybe_random_between(0,S,K)
  151	,(   I == J
  152	 ->  N = 0
  153	 ;   maybe_random_between(1,S,N)
  154	 )
  155	,window_size(I,J,K,N,M)
  156	,maximum_stride(I,J,K,Mn).
 maybe_random_between(+High, +Low, ?Parameter) is semidet
Generate, or accept, a Parameter in the given ranges.
  164maybe_random_between(H,L,P):-
  165	var(P)
  166	,!
  167	,random_between(H,L,P).
  168maybe_random_between(H,L,P):-
  169	between(H,L,P).
  170
  171
  172:-begin_tests(test_helpers).  173
  174% Test that the test-generating predicate works as expected.
  175% Next, we'll test that the predicate testing the test-generating
  176% predicate works as expected.
  177test(window_random_iterm_ascending, []):-
  178	forall(between(1,100,_)
  179	      ,(random_iterm(_,A,T,I,J,K,N,_,M,_,asc)
  180	       % Just to make it clear endpoints are in ascending order:
  181	       ,assertion(I =< J)
  182	       % Naughty-naughty trespassing on module boundary.
  183	       ,assertion(indexed_terms:window_parameters(A,I,J,K,N,asc))
  184	       ,assertion(window(T,I,J,K,N,Ws))
  185	       ,assertion(length(Ws, M))
  186	       )
  187	      ).
  188
  189test(window_random_iterm_descending, []):-
  190	forall(between(1,100,_)
  191	      ,(random_iterm(_,A,T,I,J,K,N,_,M,_,desc)
  192	       % TODO: make it so I == J
  193	       ,assertion(I >= J)
  194	       ,assertion(indexed_terms:window_parameters(A,I,J,K,N,desc))
  195	       ,assertion(window(T,I,J,K,N,Ws))
  196	       ,assertion(length(Ws, M))
  197	       )
  198	      ).
  199
  200test(window_partly_random_iterm_ascending, []):-
  201	forall(between(1,100,_)
  202	       ,(random_between(1,10,A)
  203		,random_between(1,A,I)
  204		,random_between(I,A,J)
  205		,assertion(I =< J)
  206		,window_span(I,J,S)
  207		,random_between(0,S,K)
  208		,(   I == J
  209		 ->  N = 0
  210		 ;   random_between(1,S,N)
  211		 )
  212		%,format('~n A: ~w I: ~w J: ~w K: ~w N ~w S: ~w',[A,I,J,S,K,N])
  213		,partly_random_iterm(v,A,T,I,J,K,N,S_,M,_,asc)
  214		,assertion(S == S_)
  215		,assertion(window(T,I,J,K,N,Ws))
  216		,assertion(length(Ws, M))
  217		)
  218	       ).
  219
  220test(window_partly_random_iterm_descending, []):-
  221	forall(between(1,100,_)
  222	       ,(random_between(1,10,A)
  223		,random_between(1,A,I)
  224		,random_between(1,I,J)
  225		,assertion(I >= J)
  226		,window_span(I,J,S)
  227		,random_between(0,S,K)
  228		,(   I == J
  229		 ->  N = 0
  230		 ;   random_between(1,S,N)
  231		 )
  232		%,format('~n A: ~w I: ~w J: ~w K: ~w N ~w S: ~w',[A,I,J,S,K,N])
  233		,partly_random_iterm(v,A,T,I,J,K,N,S_,M,_,desc)
  234		,assertion(S == S_)
  235		,assertion(window(T,I,J,K,N,Ws))
  236		,assertion(length(Ws, M))
  237		)
  238	       ).
  239
  240:-end_tests(test_helpers).