  program BestTest; { = a 100% systematic exhaustive tester of algorithms }
(* BestTest is a fully operational task-adaptable 100% systematic and
               exhaustive combinatorial tester of (data-driven) algorithms.
       Combinatorial testing is obtained via backtracking .
  CopyRight (C) 1988 + 2007 , Jan Hajek , NL , version 5.08 of 2007-12-14
           "The more correct the prog, the subtler the bugs"  [ JH ]
     "A minute of reflection is worth a megaton of hardware"
 Feel free to re-implement BestTest in your favorite programming language
             and to adapt it to your tasks, subject to the following rule :
NO part of this document may be published, implemented, programmed, copied
or communicated by any means without an explicit & full reference to this
author + the full title (here on the 1st non-empty line) + the website
   www.humintel.com/hajek  for the freshest version + the CopyRight note
in texts and in ALL references to this. An implicit, incomplete, indirect,
disconnected or unlinked reference (in your text and/or on www) does
NOT suffice. ALL based on 1st-hand experience. ALL rights reserved.

+ 2 modes of generation of data vectors  V[..] are available :
   1. Straight depth-first (find depth-1st ) ie LIFO generator of
       data-/parameters-/events-vector V[lo..h =hi] ie a stack.
   2. Iterative deepening (Id) of data V[lo..h<=hi] is the recommended mode.
   Both can be run in a submode as :
    + a model checking ie algorithm checking ie verification test , or as :
    + a demo which tests nothing, and only shows how all the subvectors of
       V[..] are generated . Running demos for 1. & 2. shows that 2. ie Id
        generates ALL SHORT subvectors first (like a breath-first would),
        hence Id also uses Vmax much sooner than straight depth-1st mode.
        These are 2 very good reasons to prefer iterative deepening over
         the straight depth-1st.
      Q: why?  A: because such extreme cases (shortest vectors with Vmax)
                  are most likely to reveal bugs soon. Iterative deepening
   !     allows us to use hi-lo+1 and/or Vmax-Vmin+1 so LARGE that a
         combinatorial testing CANNOT be completed in any realistic time,
   !     nevertheless, unlike depth-1st mode, iterative deepening starts
         by exhausting ALL extremely short vectors including  Vmax  value.
   !     Moreover, in my BestTest I left it to the user to confirm each
         next deepening step, so that (s)he can decide to stop eg when
         no bugs have been found and BestTest would run too long.

+ 3 methods of checking are available ( repeatedly find  relative  ) :
   1. An "absolute check" ie a direct check of the result in W or X only .
   2. A  "relative check" ie by comparing the result in  W  against in  X
        from another (often simpler) algorithm believed to be correct.
   3. Moreover we are free to insert assertions at any place and check them
      at runtime (find Assert , fassert, also serve as executable comments).

BestTest (compiles in Borland Pascal BP7, Turbo Pascal TP, in FreePascal FP
  with  Options\Compiler\ set to TP compatible) CATCHES ALL BUGS because :
+ It generates all test vectors (of test data) of all sizes ( within the
  limits lo..h ) containing all variations with repetitions of values
  ( from Vmin to Vmax ).  For a data-vector with  k  components aka atoms,
  eg V[1..k] , each atom allowed to have any of  Vmax-Vmin+1 = n  values,
  the number of variations with repetition is n^k , eg 3^2 = 9 full-size
  vectors.  Since we want to generate and test also all subvectors of
  all sizes from 1 to k , we test the following 3^1 + 3^2 = 3 + 9 = 12
  data-vectors: 1; 2; 3;  1,1; 1,2; 1,3; 2,1; 2,2; 2,3; 3,1; 3,2; 3,3; ,
  if our chosen constants were  Vmin=1, Vmax=3, and  h-lo+1 = k = 2 .
  If k = 3 then our series would continue with 1,1,1; and end with 3,3,3.
  The just shown list of (sub)vectors is in breath-first order. But here
   we  actually generate (sub)vectors    in  depth-first order. Unlike
  breadth-1st, depth-1st needs almost no memory and is easy to program
  as a stack. Here the vector  V[lo..r..h]  is here treated as a stack
    V[lo..r..]  which grows upon  r:=r+1  and shrinks upon  r:=r-1. Deeper
  insight is that  V[lo..r..h] can be viewed as 2 complementary stacks :
    V[lo..r-1] and V[r+1..h] with V[r] moving in between. When one stack
  grows at r , the other shrinks at r . The 1st stack contains a partial
  solution, while the 2nd one is a pool of free atoms to be consumed ie
  waiting to get a next value. Even deeper insight is that the 2nd stack
    V[r+1..h]  needs not to be a stack, because the values of its atoms
  do not matter yet/anymore, and so it is a SET of yet unassigned/undone
  values (which might be, but need not to be, reset to some nil, fill, or
  be left as are, since they simply do not matter yet/anymore ). The
  consequence of this deeper view is that the SET OF INDEXES [r+1..h] is
  always allowed (without any special precaution or fix) to be PERMUTED ie
  REORDERED to our advantage. A reordering changes nothing but the order
  in which V's atoms are (re)assigned values. Caution: we must keep stored
  the unchanging positional/structural index of each atom in V, because
  structural positions of atoms in V are task dependent and not exchangable
  in general. Then the challenge is to invent a smart task-(in)dependent
  HEURISTIC which will exploit such a REARRANGEMENT to find asap a solution
  (eg a bug, when testing). In fact it is not necessary to reorder
     V[r+1..h] ; it just suffices a heuristic choice of the  V[best] (best
  means finding a solution asap) within  V[r+1..h], and swap V[best]  with
     V[r+1] .  This is called NONCHRONOLOGICAL BACKTRACKING aka
  DEPENDENCY-DIRECTED BACKTRACKING utilizing task's constraints . Caution :
     V[r+1..h]  contains irrelevant values, ie they are UNINFOrmative.
  Hence a simple swap would have no effect. It is the structural/positional
  index of each atom in V[lo..h] , what does matter. Hence for such swaps
  the positional index of each atom must be kept preserved, either in the
   V of "records" with 2 items ( Value proper & its inDex), or in an array
   D[lo..h] , initially filled by: FOR dx:=lo TO h DO D[dx] := dx . In my
  backtracker proper we would have to replace all V[r] with  V[D[r]] , etc.
  Such a double indexing aka indirect access aka indirection allows us to
  swap elements of D only, while the possibly large atoms of V do not move.
  Again : we must not lose structural positions of atoms in  V[lo..h] ,
  but may be FREE to process them in any (smart) order ( look-ahead search
  in V[r+1..h] to choose the best V[best] , then V[best]:=next value ).
  Of course, we always must satisfy all task-dependent constraints, if any.
  I do not do it here, I leave it as an exercise to non-junior programmers,
  in order to keep it KISS = Keep it simple, students ! :-)
  So far on FORWARD moves ie on our freedom to pick from V[r+1..h] the
    V[best] as the next to be processed ie assigned a next value. In 1988
  I  have invented DYNAMIC BACTRACKING which picks yet another best V[back]
  from  V[lo..r-1]  while moving BACKWARD . Here I will not go deeper into
  its nontrivial algorithmics requiring deeper thoughts and precautions,
  published in my report on  A New Dynamic Backtracking Algorithm for
  Constraints Satisfaction Problems , January 1990 , 17 pages. In that
  report I also explain an extremely powerful and task-INdependent ( ! )
  Least-Free-Variable-First ( LFVF ) heuristic ( Warnsdorff's rule of 1823
  to solve one specific task called the Knight's Tour Problem ). I will not
  go into it here, however superpowerful it is for constraints satisfaction
  problems ( CSP ), because I do not believe it can be really exploited for
  program testing. My final general constructive remark on these matters :
  Identification & exploitation of CONSTRAINTS & FREEDOMS is one of the
  key tricks of trade in algorithm design and in software engineering. A
  common freedom is a COMMUTATIVITY of operations ie preordering or even
  in-flight ie dynamic reordering of processing. Another case of freedom
  is a RANDOM choice of a split in all interval bisections (see (a+b) div 2
  here below), or partitioning of an array (eg in MergeSort or QuickSort ).

+ Any task-dependent constraints on data may be imposed by means of the
     OK := Boolean expression (is true iff V's constraints are satisfied).

+ Exhaustive runtime testing of extremely short data vectors ( with bounds
    lo, h, BOTH either extremely low or BOTH extremely high ; "extremely"
    wrt to a preferably VERY SMALL range of the VAR lo , hi : ShortInt , or
    a subrange TYPE index = lo..hi ; ) provides for STRESS-TESTing.

+ Runtime testing of an implementation is more realistic than hand-proving,
  as it covers all idiosyncrasies of your hardware + OS + compiler +
  programming language, like eg parameter-passing mechanisms plus all kinds
  of overflow if you switch their checking on with  {$R+,Q+,S+,I+} as I do.
  Eg, a forgotten VAR in Swap( VAR y , z : atom ) will cause a detected bug.

Hints :
- Please repeatedly find : user , must , TestTaskNr:=

- Programs and algorithms (procedures, functions) become incorrect when
  programmer's INDUCTIVE THINKING BREAKS DOWN, which typically happens at
  some discontinuities, or extreme situations, including initialization
  and especially at the ends or at the bounds of eg data structures.
  Therefore it is BEST TO TEST :
   - arrays, strings, files with NONE, 1, 2, few atoms (elements, records);
   - with LARGE values of VERY SMALL-ranged integers (indexes) likely to
     overflow or to cause out of bound indexing.

- In PASCAL arrays are pointer-less and must have constant bounds. Hence
  a possible out of bound indexing of V's subvectors may not be caught when
  testing an incorrect algorithm which sometimes indexes out of subvector's
  upper bound  r < h . Therefore to surely get all thinkable bugs out of
  your algorithm , I recommend to recompile & run BestTest with several
  different low values of lo  and especially of  hi (to force  r = hi ).

- Tests with 8 or 16 bit integer indexes or with very narrow subrange
  types (like eg: type index = 126..127; ) are more likely to reveal subtle
  bugs than are the tests with LongInt. Eg, a poor interval bisection by
  (a+b) div 2  overflows when  (a+b) > MaxInt , which happens whenever
  ((a < b) and (a > (MaxInt div 2))).  Safer is  (a + ((b-a) div 2)).
  Although both bisections are not always the same, they are functionally
  equivalent whenever any RANDOM partition of an interval is allowed from
  the correctness point of view.   Aha!
  Quiz : what about  b - ((b-a) div 2) ?  Dear user , test it here in
         a smart & safe binary search of your own making, or from
         a smart book by some smart professor.
Notes :
+ I kept the code straight so that every non-junior programmer can easily
  adapt it for his own tasks and tests. Examples and exercises are inside.
+ Find  TestTaskNr:= , where a user can choose how to do different tests
                       on other/different tasks.
+ Repeatedly find the word  backtrack  to get to the "smart part" (near
  the end of  Tester ) which generates all (un)constrained combinatorial
  variations with repetitions for all possible (sub)vectors within the
  ranges specified (by  Vmin , Vmax , and by  lo , hi ) by the user.

+ By data-driven algorithms I mean those more common than event-driven
  algorithms like eg protocols for which I have pioneered verifications
  aka validations aka model checking by means of a highly dynamic (because
  driven by a priority queue) yet 100% systematically executed BEST-FIRST
  search reachability analysis in my APPROVER (see the end of the list of
  my epapers at  www.humintel.com/hajek ).  Nevertheless, nothing prevents
  us to REMAP  V[..] into task/prog's INPUT PARAMETERS .  But then V[..]
  might also be used for exhaustive model checking of some EVENT-driven
  algorithms treated either as-if-simultaneous, or as-if-queued in the
  vector V[..] ; atoms in V would have only to be remapped into task
  dependent representations of events.

+ Though not simplistic, BestTest is simple enough and therefore adaptable
  ie modifiable, because of no pointers, no recursion, few parameters
  (user's and in procedures). BestTest is also extremely efficient :
  negligible use of memory (uses RAM only), blitzy fast.

+ I checked this source code by means of my rigorous automated
  static analysis from my post-compile, pre-run static analyzer ANA9.
*)
{$N-}{ no-op in FP ; in BP/TP $N- better pinpoints RTErrors, float = REAL }
{$R+,Q+,S+,I+}   uses CRT { needed for readkey } ;
const pr = True { = writes to file : } ;   OutFileName = 'BestTest.out';
    { MinInt = -32768;  MaxWord = 65535; }  MaxLongInt = 2147483647;
var out : text{file} ;               ds : char;
   cFullsize,{ <= }cAllTests, IL, TestTaskNr, mode, submode : LONGint;

type shortI = ShortInt ;  { tiny ranges catch more bugs sooner        }
      index = ShortI   ;  { or much narrower & sharper subrange  :    }
{eg}{ index = 126..127 ; }{ should equal (or at least contain) lo..hi }

procedure writelnS( S: string );
begin writeln( S );   if pr then writeln( out, S )
end;

procedure writelnSNrS( S0: string;  Nr: LONGint;  S: string );
begin writeln( S0, Nr, S );  if pr then writeln( out, S0, Nr, S )
end;

procedure ASSert( condition: boolean; txt: string ) ;
begin  if not condition then
       begin writeln(    '>>> False ASSert ',txt);  if pr then
             writeln(out,'>>> False ASSert ',txt);  ds:=readkey
       end
end;
(*
function fassert( condition: boolean;   txt: string ) : boolean;
begin if condition then fassert:=true else
      begin writeln(    '>>> False fassert: ',txt);  if pr then
            writeln(out,'>>> False fassert: ',txt);
         fassert:= False;  ds:=readkey
      end
end;

procedure BreakS( txt: string );
begin writeln('Breakpoint in/at: ',txt);   ds:=readkey
end;
*)
function  HpowerL( x,{power} y : REAL ): REAL ;
begin assert( y < 38,'y = h-lo+1 >= 38 is a too big REAL nr');
      assert( y*LN(x) < LN( MaxLONGint ),
        'HpowerL < MaxLONGint; Ctrl+Break; do lower h-lo or Vmax-Vmin');
         HpowerL := exp( y * LN(x) )
end;

procedure Tester { ( VAR V,W,X: kind;  lo,hi: index;  Vmin,Vmax: atom ) } ;
 const                       { tiny bounds also do avoid too long runs  }
  Vmin =   1; {<=} Vmax = 3; { tiny diff Vmax-Vmin avoids HpowerI overflow }
    lo = 125; {<=} hi = 127; { tiny diff   hi-lo   avoids HpowerI overflow }
  { -128 <= ShortInt <= 127 in BP7 / TP / FP }{ find  subrange   }
 type { The shorter the type, the more bugs it is likely to catch sooner }
    atom  = ShortInt;  { or BYTE, coz tiny ranges catch more bugs sooner }
               kind =  array[lo..hi] of atom;
 var V, W, X : kind;            Value : atom;
 label DONE ;
 const fillV = 88 ; { = a fill of V, W, X visualizes ends of subvectors }
 var jx , { plain index }
    h, h1st, { <= hi }
     r : index ; {  r is running index of atoms within X, W, V[lo..r..h]  }
   OK : Boolean; { OK is value of a constraint, if any imposed on vector V }
 procedure ShowV( r: index;  st : string ); { for mode = Demo }
   var kx : index ;
 begin write(    st,' V[lo..r] = ');  if pr then
       write(out,st,' V[lo..r] = ');
    for kx := lo to r {     r  hides fillV , h would show fillV } do
    begin write( V[kx]:3,' ');  if pr then write (out, V[kx]:3,' ');
    end;  writeln;  if pr then writeln(out);
    if (cAlltests mod 15) = 0 then
    begin writeln('have a look, note the depth-1st (sub)order, ' +
                  'press a key (or Ctrl+break) : ');  readkey
    end
 end;

 procedure TestedTasks( st: string );  { user supplied }
  const noX = false;  YesX = true;
  var ix, jj, p1, p2 : index;
  procedure ShowBug( r: index;  showX : boolean );  var kx : index;
  begin { To keep this code simple, p1 and p2 may be uninitialized
                    for tasks where p1 and p2 doNt matter }
    writeln(    'Bug=??? at case=',cAllTests,' : lo=',lo,' <= r =',r,
            ' <= h =',h,' ;  p1=',p1,'  p2=',p2,' :');  if pr then
    writeln(out,'Bug=??? at case=',cAlltests,' : lo=',lo,' <= r =',r,
            ' <= h =',h,' ;  p1=',p1,'  p2=',p2,' :');
    write (     st,'     V[lo..r] = ');   if pr then
    write (out, st,'     V[lo..r] = ');
    for kx := lo to r { r hides fillV , h would show fillV } do
     { ^^^^^^ lo..h MUST FIT within subrange of kx = index }
    begin write ( V[kx],' ');  if pr then write (out, V[kx],' ')
    end;
    writeln;  if pr then writeln(out);

    write (     st,' ??? W[lo..r] = ');   if pr then
    write (out, st,' ??? W[lo..r] = ');
    for kx := lo to r { r hides fillV , h would show fillV } do
    begin write ( W[kx],' ');  if pr then write (out, W[kx],' ')
    end;
    writeln;  if pr then writeln(out);

    if showX then
    begin write (     st,' ??? X[lo..r] = ');  if pr then
          write (out, st,' ??? X[lo..r] = ');
      for kx := lo to r { r hides fillV , h would show fillV } do
      begin write ( X[kx],' ');  if pr then write (out, X[kx],' ')
      end;
      writeln;  if pr then writeln(out)
    end;

    writeln;  if pr then writeln(out);     readkey
  end{ ShowBug };

  procedure swap( VAR{= must } y, z : atom );  var temp : atom;
  begin  temp:=y;  y:=z;  z:=temp
  end;

  procedure InSortUpJH( VAR Ary: kind ;  from, upto: index );
   var j, k: index;  T: atom; { T is a temporary cache }
  begin { Ary[from] serves as a sentry ie sentinel for the 1st repeat-loop }
   for k := from +1 to upto do
    if Ary[k-1] > Ary[k] then   { this compare is out of inner repeat-loop }
    begin j := k;  T := Ary[k]; { MUST SAVE record not to lose in shuffle  }
     if T < Ary[from] { 1st loop top trimmed by Hajek: no records compared }
       then  repeat Ary[j]:=Ary[j-1]; j:=j-1 until j = from {or for-downto }
       else  repeat Ary[j]:=Ary[j-1]; j:=j-1 until T >= Ary[j-1];{ 2nd loop}
     Ary[j] := T { store the SAVED record at not necessarily a final place }
    end{if for}
  end;

  procedure BinInSortUp  ( VAR Ary: kind ;  from, upto: index );
     var j, k: index;  T: atom; { T is temporary cache }
  begin assert( false,'user has to write own BinSortUp');
  { dear user , you are invited to put here your Insertion sort
    which employs a binary search over the ordered part of the array Ary.
    Then test it to see if you can program a simple algorithm }
  end;

  function CheckIfUpSorted( VAR Ary: kind ;  from, upto: index ) : boolean;
  begin assert(false,'user has to write own check of upsortedness of Ary');
           CheckIfUpSorted := FALSE  { to be changed by the user }
  end;

 Begin     { TestedTasks : }
    W:=V;  { we must keep array  V  intact for the backtracker
                by doing the TestedTasks computations in W         }
    X:=V;  { must for relative checking of the result in W  against 2nd
             algorithm which stores its result in X , or vice versa, both
             results are compared. V  must never be changed by tested tasks }
   case TestTaskNr of      { check your checks too }
 0: begin { empty slot shows counters only , checks nothing }
    end;
 1: begin        InSortUpJH( W, lo, r );
     if not(CheckIfUpSorted( W, lo, r )) then ShowBug( r, noX);
    end;
 2: begin     BinInSortUp  ( W, lo, r );
     if not(CheckIfUpSorted( W, lo, r )) then ShowBug( r, noX);
    end;
 3: begin { Reference task1 stores result in W : }
                 InSortUpJH( W, lo, r );   { serves as a reference task }
              BinInSortUp  ( X, lo, r );
       for  jj := lo to h     {  to  h  checks beyond  r }
        do if X[jj] <> W[jj] then  ShowBug( r, YesX) ;
    end;
 4: if lo < r then { lo < r  is due to the definition of  task2  :   }
    begin { Test   task2 when distinct indexes p1 and p2 are given : }
          { Tested task2 : swap the smallest X[p1] into X[lo]
                        and the 2nd smallest X[p2] into X[lo+1] , so
            that thus rearranged array will contain the same atoms : }
{ Giving p1 and p2 : }
{ find p1:} p1:=lo;    for ix:=lo to r do if X[ix] < X[p1]  then p1:=ix ;
{ find p2 as the 2nd lowest index other than p1 : }
         if p1=lo then p2:=lo+1 else p2:=lo;
         for ix:=lo to r do if (ix <> p1)and(X[ix] < X[p2]) then p2:=ix ;
      InSortUpJH( W, lo, r ); { serves as a reference for checking because
        upsorted  W  solves  task2  too }
    { task2 proper : can you program a correct swapper ?  test IT  }
      begin  { This swapper is by a poor programmer, not me :-)    }
        swap( X[lo] , X[p1] );   swap( X[lo+1] , X[p2] );
      end;
  { checks:} if (X[lo] <> W[lo])or(X[lo+1] <> W[lo+1]) then ShowBug( r, YesX)
    end;
 5: begin  assert(false,'bad case TestTaskNr , free slot is empty')
    end;
 6: begin  assert(false,'bad case TestTaskNr , free slot is empty')
    end;
 7: begin  assert(false,'bad case TestTaskNr , free slot is empty')
    end;
  Else{case} assert(false,'wrong case TestTasknr');
   end{case}
 End{ TestedTasks } ;

BEgin writeln('Exhaustively combinatorial testing is up & running...');
  write ('wish submode: Demo = 0 ;  Test = 1 :  ');  read( submode );
  if submode <> 1 then
  begin writeln('submode = Demo (no test, just shows generated ' +
                'Values; fillV=',fillV,' must never show)');
     submode:=0 { demo enforced if no Test = 1  }
  end;
  write('wish to run : Straight depth-1st = 1 ;  Iterative deepening = 2 : ');
  read( mode );  h1st := hi { = depth-1st = default } ;
  if mode=1 then writeln('runs Straight depth-1st data-generator...') else
  if mode=2 then
  begin h1st := lo ; writeln('runs Iterative deepening...');
  end else begin mode:=1; assert(false,'bad mode cmded, we do Straight :')
           end  ;
(*
  cAllTests:=0; { combinatorial nr of unconstrained partial & full vectors }
  for IL :=1 to hi-lo+1
     do    cAllTests := cAllTests + round( HpowerL(Vmax-Vmin+1, IL) );
  writeln( cAlltests,' = Total nr of tests to do;' +
         ' if ok then PRESS A KEY else Ctrl+Break : ');      readkey;
  writelnSNrS('',cAllTests,' = Total of unconstrained (sub)vectors to be' +
              ' generated & tested :');
  assert( 0 < cAllTests,'0 < cAllTests');
*)
  for jx:=lo to hi{!} do V[jx]:=fillV;  W:=V; X:=V; { for readable output }
  assert( lo <= h1st,'lo <= h1st');     assert( h1st <= hi,'h1st <= hi');
  FOR   h :=    h1st to hi do { for iterative deepening iff h1st <  hi }
  Begin  { Combinatorial generator-tester proper : }
   if   h1st < hi then
   begin write ('Next iterative deepening ',lo,'...h=',h );
     if h < hi then write(' < ') else
     if h = hi then write(' = ') else assert(false,'h <= hi');
     writeln( hi,'=hi ; a key or Ctrl+break : ');     readkey;
     for jx:=lo to hi{!} do V[jx]:=fillV;  W:=V; X:=V; { for readable output }
   end;
   writeln(' vector V[ lo=',lo,'..',h,'=h ]; Vmin=',Vmin,', Vmax=',Vmax,
           ' limit values in each V[.]');
   assert( lo <= h,'lo <= h   in Tester');
   assert( h-lo+1 >  0,    'h-lo+1 > 0');
   assert( h-lo+1 < 38,'p = h-lo+1 is too BIG a power in (Vmax-Vmin+1)^p');
   assert( Vmin <= Vmax,'Vmin <= Vmax in Tester');
   cFullsize := round( HpowerL(Vmax-Vmin+1, h-lo+1) );
   assert( 0 < cFullsize,'0 < cFullsize');   writelnSNrS('', cFullsize,
     ' = Unconstrained full-size variations w/ repetition to be tested');
   assert( cFullsize < 1000000000,'cFullsize > 1 Giga , may overflow'  );
   assert( cFullsize <    1000000,'cFullsize > 1 Mega may run too long');
   assert( Vmax-Vmin+1 <= 7,'Vmax-Vmin+1 > 7 may be superfluously high' +
                            ' & run too long');
   assert( h-lo+1 <= 7,'h-lo+1 > 7 may be superfluously high & run too long');
   cAllTests:=0;   cFullsize:=0; { counters of actually tested cases }
   r := lo;    V[r]:=Vmin { = 1st value of the 1st element } ;
       { after V[r]:= , V[r] can be remapped into eg Y[r]  }
   Repeat { in V[..] all values' variations w/ repetition are generated : }
        OK := True; { OK = True iff vector V satisfies CONSTRaints, if any
             specified; use OK to impose task-specific CONSTRaints, if any }
     if OK and ( r < h {!} ) then
     begin cAllTests := cAllTests + 1;  { stats only }
       if submode = 0
       then ShowV( r,' Demo: part')   { shows PARTial vectors ie  r < h  }
       else TestedTasks(' Part:');    { tests PARTial vectors ie  r < h  }
      {        } { CONSTRaints, if any, should be recorded in V[r] here. }
         r:=r+1; { insert = extend partial vector V[lo..r] = push to stack }
       V[r]:= Vmin;  { next atom V[r] got its 1st value  }
     end else    { CONSTRaint not ok , or  r = h }
     begin
       if OK { = true if CONSTRaint is ok } then
       begin cFullsize:=cFullsize+1;  cAllTests:=cAllTests+1; {stats only}
         if submode = 0
         then ShowV( r,' Demo: full')  { shows full-size vectors ie r=h }
         else TestedTasks(' Full:');   { tests full-size vectors ie r=h }
       end ;
       WHILE   V[r] = Vmax  DO { backtracking loop proper }
       begin
         if lo < r then
         begin V[r]:=fillV; { not needed but if fillV  shows up it is KO  }
           r:=r-1; { pop = pull = delete = shortens subvector  V[lo..r..] }
          { undo }{  here CONSTRaints imposed on  V[r]  should be undone  }
         end else
         if lo = r then GOTO DONE { backtrack exhausted all possibilities }
                   else assert( false,'r < lo must never occur !') ;
       end{od};
       V[r] := V[r] + 1;  { V[r] got the next value }
     end
   Until false;  DONE :
  End{for} ;
  writelnSNrS('',cFullsize,' = Actually  generated  Full-size'  +
              ' variations w/ repetition');
  writelnSNrS('',cAllTests,' = Actual count of all partial and' +
              ' full vectors tested ;    done.');  readkey
ENd{ Tester };

BEGIN  { main : } assign(out, OutFileName);  rewrite(out);  writelnS('');
  writelnS('Fresh run :  BestsJ2 source code is CopyRight (C) 1988+2007,' +
           ' Jan Hajek, NL');
  repeat
    write('Ctrl+break or wish a TestTaskNr = ');  read( TestTaskNr );
    assert(  0 <= TestTaskNr,  '0 <= TestTaskNr');
    assert( TestTaskNr < 999,'TestTaskNr < 999' );
    TESTER { ( V, W, X,  lo, h,  Vmin, Vmax ) }  ;
    writeln('-.-');  if pr then writeln(out,'-.-')
  until false { = 4ever until killed by Ctrl+break } ;
  Close(out)
END.


