1#############################################################################
2##
3##  This file is part of GAP, a system for computational discrete algebra.
4##
5##  Copyright of GAP belongs to its developers, whose names are too numerous
6##  to list here. Please refer to the COPYRIGHT file for details.
7##
8##  SPDX-License-Identifier: GPL-2.0-or-later
9##
10
11#############################################################################
12##
13##  Parametrize the output; if `error' has the value `Error' then only the
14##  first error in each call is printed in the `Test' run,
15##  if the value is `Print' then all errors are printed.
16##
17error:= Print;;
18
19#############################################################################
20##
21##  How many times to repeat the same tests? Large values result in longer
22##  test runs, but with a higher probability of finding bugs
23##
24ARITH_LST_REPS := 5;
25
26
27#############################################################################
28##
29##  Define auxiliary functions.
30##
31RandomSquareArray := function( dim, D )
32  return List( [ 1 .. dim ], i -> List( [ 1 .. dim ], j -> Random( D ) ) );
33end;;
34NestingDepthATest := function( obj )
35  if not IsGeneralizedRowVector( obj ) then
36    return 0;
37  elif IsEmpty( obj ) then
38    return 1;
39  else
40    return 1 + NestingDepthATest( obj[ PositionBound( obj ) ] );
41  fi;
42end;;
43NestingDepthMTest := function( obj )
44  if not IsMultiplicativeGeneralizedRowVector( obj ) then
45    return 0;
46  elif IsEmpty( obj ) then
47    return 1;
48  else
49    return 1 + NestingDepthMTest( obj[ PositionBound( obj ) ] );
50  fi;
51end;;
52ImmutabilityLevel2 := function( list )
53  if not IsList( list ) then
54    if IsMutable( list ) then
55      Error( "<list> is not a list" );
56    else
57      return 0;
58    fi;
59  elif IsEmpty( list ) then
60    # The empty list is defined to have immutability level 0.
61    return 0;
62  elif IsMutable( list ) then
63    return ImmutabilityLevel2( list[ PositionBound( list ) ] );
64  else
65    return 1 + ImmutabilityLevel2( list[ PositionBound( list ) ] );
66  fi;
67end;;
68ImmutabilityLevel := function( list )
69  if IsMutable( list ) then
70    return ImmutabilityLevel2( list );
71  else
72    return infinity;
73  fi;
74end;;
75
76##  Note that the two-argument version of `List' is defined only for
77##  dense lists.
78ListWithPrescribedHoles := function( list, func )
79  local result, i;
80
81  result:= [];
82  for i in [ 1 .. Length( list ) ] do
83    if IsBound( list[i] ) then
84      result[i]:= func( list[i] );
85    fi;
86  od;
87  return result;
88end;;
89SumWithHoles := function( list )
90  local pos, result, i;
91
92  pos:= PositionBound( list );
93  result:= list[ pos ];
94  for i in [ pos+1 .. Length( list ) ] do
95    if IsBound( list[i] ) then
96      result:= result + list[i];
97    fi;
98  od;
99  return result;
100end;;
101ParallelOp := function( op, list1, list2, mode )
102  local result, i;
103
104  result:= [];
105  for i in [ 1 .. Maximum( Length( list1 ), Length( list2 ) ) ] do
106    if IsBound( list1[i] ) then
107      if IsBound( list2[i] ) then
108        result[i]:= op( list1[i], list2[i] );
109      elif mode = "one" then
110        result[i]:= ShallowCopy( list1[i] );
111      fi;
112    elif IsBound( list2[i] ) and mode = "one" then
113      result[i]:= ShallowCopy( list2[i] );
114    fi;
115  od;
116  return result;
117end;;
118ErrorMessage := function( opname, operands, info, is, should )
119  local str, i;
120
121  str:= Concatenation( opname, "( " );
122  for i in [ 1 .. Length( operands ) - 1 ] do
123    Append( str, operands[i] );
124    Append( str, ", " );
125  od;
126  error( str, operands[ Length( operands ) ], " ):  ", info, ",\n",
127         "should be ", should, " but is ", is, "\n" );
128end;;
129CheckMutabilityStatus := function( opname, list )
130  local attr, op, val, sm;
131
132  attr:= ValueGlobal( Concatenation( opname, "Attr" ) );
133  if ImmutabilityLevel( attr( list ) ) <> infinity then
134    error( opname, "Attr: mutability problem for ", list,
135           " (", ImmutabilityLevel( list ), ")\n" );
136  fi;
137  op:= ValueGlobal( Concatenation( opname, "Op" ) );
138  val:= op( list );
139  if val <> fail and IsCopyable( val ) and not IsMutable( val ) then
140    error( opname, "Op: mutability problem for ", list,
141           " (", ImmutabilityLevel( list ), ")\n" );
142  fi;
143  sm:= ValueGlobal( Concatenation( opname, "SM" ) );
144  val:= sm( list );
145  if     val <> fail
146     and IsCopyable( val )
147     and ImmutabilityLevel( sm( list ) ) <> ImmutabilityLevel( list ) then
148    error( opname, "SM: mutability problem for ", list,
149           " (", ImmutabilityLevel( list ), ")\n" );
150  fi;
151end;;
152
153##  Check whether a unary operation preserves the compression status.
154COMPRESSIONS := [ "Is8BitMatrixRep", "Is8BitVectorRep",
155                     "IsGF2VectorRep", "IsGF2MatrixRep" ];;
156CheckCompressionStatus := function( opname, list )
157  local value, namefilter, filter;
158
159  value:= ValueGlobal( opname )( list );
160  if value <> fail then
161    for namefilter in COMPRESSIONS do
162      filter:= ValueGlobal( namefilter );
163      if filter( list ) and not filter( value ) then
164        error( opname, " does not preserve `", namefilter, "'\n" );
165      fi;
166    od;
167  fi;
168end;;
169CompareTest := function( opname, operands, result, desired )
170  local i, j, val;
171
172  # Check that the same positions are bound,
173  # and that corresponding entries are equal.
174  if IsList( result ) and IsList( desired ) then
175    if Length( result ) <> Length( desired ) then
176      ErrorMessage( opname, operands, "lengths differ",
177                    Length( result ), Length( desired ) );
178    fi;
179    for i in [ 1 .. Length( result ) ] do
180      if IsBound( result[i] ) then
181        if not IsBound( desired[i] ) then
182          ErrorMessage( opname, operands,
183                        Concatenation( "bound at ", String( i ) ),
184                        result[i], "unbound" );
185        elif result[i] <> desired[i] then
186          ErrorMessage( opname, operands,
187                        Concatenation( "error at ", String( i ) ),
188                        result[i], desired[i] );
189        fi;
190      elif IsBound( desired[i] ) then
191          ErrorMessage( opname, operands,
192                        Concatenation( "unbound at ", String( i ) ),
193                        "unbound", desired[i] );
194      fi;
195    od;
196  elif IsList( result ) or IsList( desired ) then
197    ErrorMessage( opname, operands, "list vs. non-list", result, desired );
198  elif result <> desired then
199    ErrorMessage( opname, operands, "two non-lists", result, desired );
200  fi;
201
202  # Check the mutability status.
203  if     Length( operands ) = 2
204     and IsList( result ) and IsCopyable( result )
205     and ImmutabilityLevel( result )
206         <> Minimum( List( operands, ImmutabilityLevel ) )
207     and not (ImmutabilityLevel(result)=infinity and
208               NestingDepthM(result) =
209                      Minimum( List( operands, ImmutabilityLevel ) )) then
210    error( opname, ": mutability problem for ", operands[1], " (",
211           ImmutabilityLevel( operands[1] ), ") and ", operands[2], " (",
212           ImmutabilityLevel( operands[2] ), ")\n" );
213  fi;
214end;;
215
216#############################################################################
217##
218#F  ZeroTest( <list> )
219##
220##  The zero of a list $x$ in `IsGeneralizedRowVector' is defined as
221##  the list whose entry at position $i$ is the zero of $x[i]$
222##  if this entry is bound, and is unbound otherwise.
223##
224ZeroTest := function( list )
225  if IsGeneralizedRowVector( list ) then
226    CompareTest( "Zero", [ list ],
227                 Zero( list ),
228                 ListWithPrescribedHoles( list, Zero ) );
229    CheckMutabilityStatus( "Zero", list );
230    CheckCompressionStatus( "ZeroAttr", list );
231    CheckCompressionStatus( "ZeroSM", list );
232  fi;
233end;;
234
235#############################################################################
236##
237#F  AdditiveInverseTest( <list> )
238##
239##  The additive inverse of a list $x$ in `IsGeneralizedRowVector' is defined
240##  as the list whose entry at position $i$ is the additive inverse of $x[i]$
241##  if this entry is bound, and is unbound otherwise.
242##
243AdditiveInverseTest := function( list )
244  if IsGeneralizedRowVector( list ) then
245    CompareTest( "AdditiveInverse", [ list ],
246                 AdditiveInverse( list ),
247                 ListWithPrescribedHoles( list, AdditiveInverse ) );
248    CheckMutabilityStatus( "AdditiveInverse", list );
249    CheckCompressionStatus( "AdditiveInverseAttr", list );
250    CheckCompressionStatus( "AdditiveInverseSM", list );
251  fi;
252end;;
253
254#############################################################################
255##
256#F  AdditionTest( <left>, <right> )
257##
258##  If $x$ and $y$ are in `IsGeneralizedRowVector' and have the same
259##  additive nesting depth (see~"NestingDepthA"),
260##  % By definition, this depth is nonzero.
261##  the sum $x + y$ is defined *pointwise*, in the sense that the result is a
262##  list whose entry at position $i$ is $x[i] + y[i]$ if these entries are
263##  bound,
264##  is a shallow copy (see~"ShallowCopy") of $x[i]$ or $y[i]$ if the other
265##  argument is not bound at position $i$,
266##  and is unbound if both $x$ and $y$ are unbound at position $i$.
267##
268##  If $x$ is in `IsGeneralizedRowVector' and $y$ is either not a list or is
269##  in `IsGeneralizedRowVector' and has lower additive nesting depth,
270##  the sum $x + y$ is defined as a list whose entry at position $i$ is
271##  $x[i] + y$ if $x$ is bound at position $i$, and is unbound if not.
272##  The equivalent holds in the reversed case,
273##  where the order of the summands is kept,
274##  as addition is not always commutative.
275##
276##  For two {\GAP} objects $x$ and $y$ of which one is in
277##  `IsGeneralizedRowVector' and the other is either not a list or is
278##  also in `IsGeneralizedRowVector',
279##  $x - y$ is defined as $x + (-y)$.
280##
281AdditionTest := function( left, right )
282  local depth1, depth2, desired;
283
284  if IsGeneralizedRowVector( left ) and IsGeneralizedRowVector( right ) then
285    depth1:= NestingDepthATest( left );
286    depth2:= NestingDepthATest( right );
287    if depth1 = depth2 then
288      desired:= ParallelOp( \+, left, right, "one" );
289    elif depth1 < depth2 then
290      desired:= ListWithPrescribedHoles( right, x -> left + x );
291    else
292      desired:= ListWithPrescribedHoles( left, x -> x + right );
293    fi;
294  elif IsGeneralizedRowVector( left ) and not IsList( right ) then
295    desired:= ListWithPrescribedHoles( left, x -> x + right );
296  elif not IsList( left ) and IsGeneralizedRowVector( right ) then
297    desired:= ListWithPrescribedHoles( right, x -> left + x );
298  else
299    return;
300  fi;
301  CompareTest( "Addition", [ left, right ], left + right, desired );
302  if AdditiveInverse( right ) <> fail then
303    CompareTest( "Subtraction", [ left, right ], left - right,
304                 left + ( - right ) );
305  fi;
306end;;
307
308#############################################################################
309##
310#F  OneTest( <list> )
311##
312OneTest := function( list )
313  if IsOrdinaryMatrix( list ) and Length( list ) = Length( list[1] ) then
314    CheckMutabilityStatus( "One", list );
315    CheckCompressionStatus( "OneAttr", list );
316    CheckCompressionStatus( "OneSM", list );
317  fi;
318end;;
319
320#############################################################################
321##
322#F  InverseTest( <obj> )
323##
324InverseTest := function( list )
325  if IsOrdinaryMatrix( list ) and Length( list ) = Length( list[1] ) then
326    CheckMutabilityStatus( "Inverse", list );
327    CheckCompressionStatus( "InverseAttr", list );
328    CheckCompressionStatus( "InverseSM", list );
329  fi;
330end;;
331
332#############################################################################
333##
334#F  TransposedMatTest( <obj> )
335##
336TransposedMatTest := function( list )
337  if IsOrdinaryMatrix( list ) then
338    CheckCompressionStatus( "TransposedMatAttr", list );
339    CheckCompressionStatus( "TransposedMatOp", list );
340  fi;
341end;;
342
343#############################################################################
344##
345#F  MultiplicationTest( <left>, <right> )
346##
347##  There are three possible computations that might be triggered by a
348##  multiplication involving a list in
349##  `IsMultiplicativeGeneralizedRowVector'.
350##  Namely, $x * y$ might be
351##  \beginlist
352##  \item{(I)}
353##      the inner product $x[1] * y[1] + x[2] * y[2] + \cdots + x[n] * y[n]$,
354##      where summands are omitted for which the entry in $x$ or $y$ is
355##      unbound
356##      (if this leaves no summand then the multiplication is an error),
357##      or
358##  \item{(L)}
359##      the left scalar multiple, i.e., a list whose entry at position $i$ is
360##      $x * y[i]$ if $y$ is bound at position $i$, and is unbound if not, or
361##  \item{(R)}
362##      the right scalar multiple, i.e., a list whose entry at position $i$
363##      is $x[i] * y$ if $x$ is bound at position $i$, and is unbound if not.
364##  \endlist
365##
366##  Our aim is to generalize the basic arithmetic of simple row vectors and
367##  matrices, so we first summarize the situations that shall be covered.
368##
369##  \beginexample
370##      | scl   vec   mat
371##  ---------------------
372##  scl |       (L)   (L)
373##  vec | (R)   (I)   (I)
374##  mat | (R)   (R)   (R)
375##  \endexample
376##
377##  This means for example that the product of a scalar (scl)
378##  with a vector (vec) or a matrix (mat) is computed according to (L).
379##  Note that this is asymmetric.
380##
381##  Now we can state the general multiplication rules.
382##
383##  If exactly one argument is in `IsMultiplicativeGeneralizedRowVector'
384##  then we regard the other argument (which is then not a list) as a scalar,
385##  and specify result (L) or (R), depending on ordering.
386##
387##  In the remaining cases, both $x$ and $y$ are in
388##  `IsMultiplicativeGeneralizedRowVector', and we distinguish the
389##  possibilities by their multiplicative nesting depths.
390##  An argument with *odd* multiplicative nesting depth is regarded as a
391##  vector, and an argument with *even* multiplicative nesting depth is
392##  regarded as a scalar or a matrix.
393##
394##  So if both arguments have odd multiplicative nesting depth,
395##  we specify result (I).
396##
397##  If exactly one argument has odd nesting depth,
398##  the other is treated as a scalar if it has lower multiplicative nesting
399##  depth, and as a matrix otherwise.
400##  In the former case, we specify result (L) or (R), depending on ordering;
401##  in the latter case, we specify result (L) or (I), depending on ordering.
402##
403##  We are left with the case that each argument has even multiplicative
404##  nesting depth.
405##  % By definition, this depth is nonzero.
406##  If the two depths are equal, we treat the computation as a matrix product,
407##  and specify result (R).
408##  Otherwise, we treat the less deeply nested argument as a scalar and the
409##  other as a matrix, and specify result (L) or (R), depending on ordering.
410##
411##  For two {\GAP} objects $x$ and $y$ of which one is in
412##  `IsMultiplicativeGeneralizedRowVector' and the other is either not a list
413##  or is also in `IsMultiplicativeGeneralizedRowVector',
414##  $x / y$ is defined as $x * y^{-1}$.
415##
416MultiplicationTest := function( left, right )
417  local depth1, depth2, par, desired;
418
419  if IsMultiplicativeGeneralizedRowVector( left ) and
420     IsMultiplicativeGeneralizedRowVector( right ) then
421    depth1:= NestingDepthMTest( left );
422    depth2:= NestingDepthMTest( right );
423    if IsOddInt( depth1 ) then
424      if IsOddInt( depth2 ) or depth1 < depth2 then
425        # <vec> * <vec> or <vec> * <mat>
426        par:= ParallelOp( \*, left, right, "both" );
427        if IsEmpty( par ) then
428          error( "vector multiplication <left>*<right> with empty ",
429                 "support:\n", left, "\n", right, "\n" );
430        else
431          desired:= SumWithHoles( par );
432        fi;
433      else
434        # <vec> * <scl>
435        desired:= ListWithPrescribedHoles( left, x -> x * right );
436      fi;
437    elif IsOddInt( depth2 ) then
438      if depth1 < depth2 then
439        # <scl> * <vec>
440        desired:= ListWithPrescribedHoles( right, x -> left * x );
441      else
442        # <mat> * <vec>
443        desired:= ListWithPrescribedHoles( left, x -> x * right );
444      fi;
445    elif depth1 = depth2 then
446      # <mat> * <mat>
447      desired:= ListWithPrescribedHoles( left, x -> x * right );
448    elif depth1 < depth2 then
449      # <scl> * <mat>
450      desired:= ListWithPrescribedHoles( right, x -> left * x );
451    else
452      # <mat> * <scl>
453      desired:= ListWithPrescribedHoles( left, x -> x * right );
454    fi;
455  elif IsMultiplicativeGeneralizedRowVector( left ) and
456       not IsList( right ) then
457    desired:= ListWithPrescribedHoles( left, x -> x * right );
458  elif IsMultiplicativeGeneralizedRowVector( right ) and
459       not IsList( left ) then
460    desired:= ListWithPrescribedHoles( right, x -> left * x );
461  else
462    return;
463  fi;
464  CompareTest( "Multiplication", [ left, right ], left * right, desired );
465  if     IsMultiplicativeGeneralizedRowVector( right )
466     and IsOrdinaryMatrix( right )
467     and Length( right ) = Length( right[1] )
468     and NestingDepthM( right ) = 2
469     and Inverse( right ) <> fail then
470    CompareTest( "Division", [ left, right ], left / right,
471                 left * ( right^-1 ) );
472  fi;
473end;;
474
475#############################################################################
476##
477#F  RunTest( <func>, <arg1>, ... )
478##
479##  Call <func> for the remaining arguments, or for shallow copies of them
480##  or immutable copies.
481##
482RunTest := function( arg )
483  local combinations, i, entry;
484
485  combinations:= [ ];
486  for i in [ 2 .. Length( arg ) ] do
487    entry:= [ arg[i] ];
488    if IsCopyable( arg[i] ) then
489      Add( entry, ShallowCopy( arg[i] ) );
490    fi;
491    if IsMutable( arg[i] ) then
492      Add( entry, Immutable( arg[i] ) );
493    fi;
494    Add( combinations, entry );
495  od;
496  for entry in Cartesian( combinations ) do
497    CallFuncList( arg[1], entry );
498  od;
499end;;
500
501#############################################################################
502##
503#F  TestOfAdditiveListArithmetic( <R>, <dim> )
504##
505##  For a ring or list of ring elements <R> (such that `Random( <R> )'
506##  returns an element in <R> and such that not all elements in <R> are
507##  zero),
508##  `TestOfAdditiveListArithmetic' performs the following tests of additive
509##  arithmetic operations.
510##  \beginlist
511##  \item{1.}
512##      If the elements of <R> are in `IsGeneralizedRowVector' then
513##      it is checked whether `Zero', `AdditiveInverse', and `\+'
514##      obey the definitions.
515##  \item{2.}
516##      If the elements of <R> are in `IsGeneralizedRowVector' then
517##      it is checked whether the sum of elements in <R> and (non-dense)
518##      plain lists of integers obeys the definitions.
519##  \item{3.}
520##      Check `Zero' and `AdditiveInverse' for nested plain lists of elements
521##      in <R>, and `\+' for elements in <R> and nested plain lists of
522##      elements in <R>.
523##  \endlist
524##
525TestOfAdditiveListArithmetic := function( R, dim )
526  local r, i, intlist, j, vec1, vec2, mat1, mat2, row;
527
528  r:= Random( R );
529  if IsGeneralizedRowVector( r ) then
530
531    # tests of kind 1.
532    for i in [ 1 .. ARITH_LST_REPS ] do
533      RunTest( ZeroTest, Random( R ) );
534      RunTest( AdditiveInverseTest, Random( R ) );
535      RunTest( AdditionTest, Random( R ), Random( R ) );
536    od;
537
538    # tests of kind 2.
539    for i in [ 1 .. ARITH_LST_REPS ] do
540      RunTest( AdditionTest, Random( R ), [] );
541      RunTest( AdditionTest, [], Random( R ) );
542      r:= Random( R );
543      intlist:= List( [ 1 .. Length( r ) + Random( -1, 1 ) ],
544                      x -> Random( Integers ) );
545      for j in [ 1 .. Int( Length( r ) / 3 ) ] do
546        Unbind( intlist[ Random( 1, Length( intlist ) ) ] );
547      od;
548      RunTest( AdditionTest, r, intlist );
549      RunTest( AdditionTest, intlist, r );
550    od;
551
552  fi;
553
554  # tests of kind 3.
555  for i in [ 1 .. ARITH_LST_REPS ] do
556
557    vec1:= List( [ 1 .. dim ], x -> Random( R ) );
558    vec2:= List( [ 1 .. dim ], x -> Random( R ) );
559
560    RunTest( ZeroTest, vec1 );
561    RunTest( AdditiveInverseTest, vec1 );
562    RunTest( AdditionTest, vec1, Random( R ) );
563    RunTest( AdditionTest, Random( R ), vec2 );
564    RunTest( AdditionTest, vec1, vec2 );
565    RunTest( AdditionTest, vec1, [] );
566    RunTest( AdditionTest, [], vec2 );
567    Unbind( vec1[ dim ] );
568    RunTest( AdditionTest, vec1, vec2 );
569    Unbind( vec2[ Random( 1, dim ) ] );
570    RunTest( ZeroTest, vec2 );
571    RunTest( AdditiveInverseTest, vec1 );
572    RunTest( AdditiveInverseTest, vec2 );
573    RunTest( AdditionTest, vec1, vec2 );
574    Unbind( vec1[ Random( 1, dim ) ] );
575    RunTest( AdditionTest, vec1, vec2 );
576
577    mat1:= RandomSquareArray( dim, R );
578    mat2:= RandomSquareArray( dim, R );
579
580    RunTest( ZeroTest, mat1 );
581    RunTest( AdditiveInverseTest, mat1 );
582    RunTest( TransposedMatTest, mat1 );
583    RunTest( AdditionTest, mat1, Random( R ) );
584    RunTest( AdditionTest, Random( R ), mat2 );
585    RunTest( AdditionTest, vec1, mat2 );
586    RunTest( AdditionTest, mat1, vec2 );
587    RunTest( AdditionTest, mat1, mat2 );
588    RunTest( AdditionTest, mat1, [] );
589    RunTest( AdditionTest, [], mat2 );
590    Unbind( mat1[ dim ] );
591    row:= mat1[ Random( 1, dim-1 ) ];
592    if not IsLockedRepresentationVector( row ) then
593      Unbind( row[ Random( 1, dim ) ] );
594    fi;
595    RunTest( AdditionTest, mat1, mat2 );
596    Unbind( mat2[ Random( 1, dim ) ] );
597    RunTest( ZeroTest, mat2 );
598    RunTest( AdditiveInverseTest, mat1 );
599    RunTest( AdditiveInverseTest, mat2 );
600    RunTest( TransposedMatTest, mat2 );
601    RunTest( AdditionTest, mat1, mat2 );
602    Unbind( mat1[ Random( 1, dim ) ] );
603    RunTest( AdditionTest, mat1, mat2 );
604
605  od;
606end;;
607
608#############################################################################
609##
610#F  TestOfMultiplicativeListArithmetic( <R>, <dim> )
611##
612##  For a ring or list of ring elements <R> (such that `Random( <R> )'
613##  returns an element in <R> and such that not all elements in <R> are
614##  zero),
615##  `TestOfMultiplicativeListArithmetic' performs the following tests of
616##  multiplicative arithmetic operations.
617##  \beginlist
618##  \item{1.}
619##      If the elements of <R> are in `IsMultiplicativeGeneralizedRowVector'
620##      then it is checked whether `One', `Inverse', and `\*'
621##      obey the definitions.
622##  \item{2.}
623##      If the elements of <R> are in `IsMultiplicativeGeneralizedRowVector'
624##      then it is checked whether the product of elements in <R> and
625##      (non-dense) plain lists of integers obeys the definitions.
626##      (Note that contrary to the additive case, we need not chack the
627##      special case of a multiplication with an empty list.)
628##  \item{3.}
629##      Check `One' and `Inverse' for nested plain lists of elements
630##      in <R>, and `\*' for elements in <R> and nested plain lists of
631##      elements in <R>.
632##  \endlist
633##
634TestOfMultiplicativeListArithmetic := function( R, dim )
635  local r, i, intlist, j, vec1, vec2, mat1, mat2, row;
636
637  r:= Random( R );
638  if IsMultiplicativeGeneralizedRowVector( r ) then
639
640    # tests of kind 1.
641    for i in [ 1 .. ARITH_LST_REPS ] do
642      RunTest( OneTest, Random( R ) );
643      RunTest( InverseTest, Random( R ) );
644      RunTest( MultiplicationTest, Random( R ), Random( R ) );
645    od;
646
647    # tests of kind 2.
648    for i in [ 1 .. ARITH_LST_REPS ] do
649      r:= Random( R );
650      intlist:= List( [ 1 .. Length( r ) + Random( -1, 1 ) ],
651                      x -> Random( Integers ) );
652      for j in [ 1 .. Int( Length( r ) / 3 ) ] do
653        Unbind( intlist[ Random( 1, Length( intlist ) ) ] );
654      od;
655      RunTest( MultiplicationTest, r, intlist );
656      RunTest( MultiplicationTest, intlist, r );
657    od;
658
659  fi;
660
661  # tests of kind 3.
662  for i in [ 1 .. ARITH_LST_REPS ] do
663
664    vec1:= List( [ 1 .. dim ], x -> Random( R ) );
665    vec2:= List( [ 1 .. dim ], x -> Random( R ) );
666
667    RunTest( OneTest, vec1 );
668    RunTest( InverseTest, vec1 );
669    RunTest( MultiplicationTest, vec1, Random( R ) );
670    RunTest( MultiplicationTest, Random( R ), vec2 );
671    RunTest( MultiplicationTest, vec1, vec2 );
672    Unbind( vec1[ dim ] );
673    RunTest( MultiplicationTest, vec1, vec2 );
674    Unbind( vec2[ Random( 1, dim ) ] );
675    RunTest( OneTest, vec2 );
676    RunTest( InverseTest, vec1 );
677    RunTest( InverseTest, vec2 );
678    RunTest( MultiplicationTest, vec1, vec2 );
679    Unbind( vec1[ Random( 1, dim ) ] );
680    RunTest( MultiplicationTest, vec1, vec2 );
681
682    mat1:= RandomSquareArray( dim, R );
683    mat2:= RandomSquareArray( dim, R );
684
685    RunTest( OneTest, mat1 );
686    RunTest( InverseTest, mat1 );
687    RunTest( MultiplicationTest, mat1, Random( R ) );
688    RunTest( MultiplicationTest, Random( R ), mat2 );
689    RunTest( MultiplicationTest, vec1, mat2 );
690    RunTest( MultiplicationTest, mat1, vec2 );
691    RunTest( MultiplicationTest, mat1, mat2 );
692    Unbind( mat1[ dim ] );
693    row:= mat1[ Random( 1, dim-1 ) ];
694    if not IsLockedRepresentationVector( row ) then
695      Unbind( row[ Random( 1, dim ) ] );
696    fi;
697    RunTest( MultiplicationTest, vec1, mat2 );
698    RunTest( MultiplicationTest, mat1, vec2 );
699    RunTest( MultiplicationTest, mat1, mat2 );
700    Unbind( mat2[ Random( 1, dim ) ] );
701    RunTest( OneTest, mat2 );
702    RunTest( InverseTest, mat1 );
703    RunTest( InverseTest, mat2 );
704    RunTest( MultiplicationTest, mat1, mat2 );
705    Unbind( mat1[ Random( 1, dim ) ] );
706    RunTest( MultiplicationTest, mat1, mat2 );
707
708  od;
709end;;
710
711#############################################################################
712##
713#F  TestOfListArithmetic( <R>, <dimlist> )
714##
715TestOfListArithmetic := function( R, dimlist )
716  local n, len, bools, i;
717
718  len:= 100;
719  bools:= [ true, false ];
720
721  for n in dimlist do
722    TestOfAdditiveListArithmetic( R, n );
723    TestOfMultiplicativeListArithmetic( R, n );
724    R:= List( [ 1 .. len ], x -> Random( R ) );
725    if IsMutable( R[1] ) and not ForAll( R, IsZero ) then
726      for i in [ 1 .. len ] do
727        if Random( bools ) then
728          R[i]:= Immutable( R[i] );
729        fi;
730      od;
731      TestOfAdditiveListArithmetic( R, n );
732      TestOfMultiplicativeListArithmetic( R, n );
733    fi;
734  od;
735end;;
736