macro( row=linalg['row'], addrow=linalg['addrow'], dotprod=linalg['dotprod'] );

##################################################################
# spectral(m, a, T) -- Spectral Test for Congruential generators #
#                                                                #
# m = the modulus, a = the multiplier, T = number of dimensions  #
#                                                                #
# Output : t = 1..t, nu, nu^2, d = 1/nu, mu_t ;                  #
# For explanations see Knuth(vol 2) page 98-105                  #
#                                                                #
# Programmer: Rohit Goyal                                        #
# Language:   Maple Version 5, Release 2                         #
# Last Modified: 07/20/93                                        #
##################################################################

spectral := proc(m, a, T)
local h,hp,p,pp,r,s,t,q,u,v,U,V,Utemp,i,j,Vtemp,k,flag57,
	Vsave,X,Y,Z,flag911,Ytemp;
#step 1
h := a; hp := m; p := 1; pp := 0; r := a; s := 1+a^2;
t := 2;

#step 2
q := floor(hp/h); u := hp -q*h; v := pp-q*p;
while u^2+v^2 < s do
       s := u^2+v^2; hp := h; h := u; pp := p; p:=v; 
       q := floor(hp/h); u := hp -q*h; v := pp-q*p;
od;

#step 3
u := u-h; v := v - p;
if u^2 + v^2 < s then s:=u^2+v^2; hp := u; pp := v; fi;
printf(`t = %d, nu = %f, nu^2 = %f, d = %f, mu = %f, r = %f \n`,
t, evalf(sqrt(s)), s, evalf(1/(sqrt(s))), evalf(Pi*s/m),
 evalf((1/sqrt(s))*((2*m)^(1/2)-1)) );
U := array(1..t, 1..t,[[-h,p],[-hp, pp]]);
if pp > 0 then V := array([[-pp,-hp],[p,h]]);
else V := array([[pp,hp],[-p,-h]]); fi;

#step 4
do 
if t = T then RETURN(s); fi;
t := t+1; r := (a*r) mod m;
Utemp := array(
      [seq([seq(U[i,j], j=1..t-1),0], i=1..t-1),[-r, seq(0,i=1..t-2),1]]);
U := map(x->x,Utemp);
q := array([seq(round(V[i,1]*r/m), i=1..t-1)]);
Vtemp :=array( 
[seq([seq(V[i,j], j=1..t-1),V[i,1]*r-q[i]*m],i=1..t-1), [seq(0,i=1..t-1),m]]);
V := map(x->x,Vtemp);
for i from 1 to t-1 do
        Utemp:= addrow(U,i,t,q[i]);
 U := map(x->x,Utemp);
od;

s := min(s, dotprod(row(U,t), row(U,t)));
k := t; j := 1;

flag57 := true;
while flag57 =true do
  #step 5
  q:=array(1..t, [seq(round(dotprod(row(V,i),row(V,j))
                  / dotprod(row(V,j), row(V,j))), i=1..t)]);
  Vsave := map(x->x, V);
  for i from 1 to t do
##########################################################################
# Note the ambiguity in the interpretation in the following if...then    #
# Knuth wants to compare the dotproducts of V for all i = 1..t and then  #
# for those i that satisfy the if then condition, change V as done below.#
##########################################################################
   if i <> j and (2*abs(dotprod(row(Vsave,i),row(Vsave,j))) 
                       > dotprod(row(Vsave,j),row(Vsave,j))) then
         Vtemp:= addrow(V,j,i,-q[i]); V := map(x->x,Vtemp);
                Utemp := addrow(U,i,j,q[i]); U := map(x->x,Utemp);
                k := j;
         fi;
  od;

  #step 6
  if k = j then s := min(s, dotprod(row(U,j),row(U,j)));fi;

  #step 7
  if j = t then j := 1; else j := j+1; fi;
  if k = j then flag57 := false; fi;
od;

#step 8
X := array([seq(0, i=1..t)]);
Y := array([seq(0, i=1..t)]);
k := t;
Z := array([seq(floor(sqrt(floor(dotprod(row(V,i),row(V,i))*s/(m^2))))
           ,i= 1..t)]);

#step 9
flag911:=true;
while flag911 = true do 
  if X[k] <> Z[k] then 
        X[k]:= X[k]+1;
        Ytemp:=array([seq(Y[i]+U[k,i], i=1..t)]);
 Y := map(x->x,Ytemp);

        #step 10
        k := k+1;
        while k <= t do
         X[k] := -1*Z[k];
                Ytemp := array([seq(Y[i]- 2 * Z[k] *U[k,i],i=1..t)]);
             Y := map(x->x, Ytemp);
                k:=k+1;
        od;
        s:= min(s, dotprod(Y,Y));
  fi;

  #step 11
  k:=k-1;
  if k < 1 then flag911:=false; fi;
od;
printf(`t = %d, nu = %f, nu^2 = %f , d = %f, mu = %f, r = %f\n`,
t, evalf(sqrt(s)), s, evalf(1/sqrt(s)),evalf((Pi^(t/2)*s^(t/2))/((t/2)!*m)),
 evalf((1/sqrt(s))*((t!*m)^(1/t)-1)) );
od;

end:


`help/text/spectral` := TEXT (
`FUNCTION: spectral - Performs the Spectral Test for Congruential Random`,
`                     Number Generators (see Knuth Vol 2, p. 98-105)`,
``,
`CALLING SEQUENCE:`,
`spectral( m, a, T )`,
``,
`PARAMETERS:`,
`m - Modulus of the random number generator`,
`a - Multiplier of the generator`,
`T - Maximum dimension for the Spectral Test`,
``,
`SYNOPSIS:`,
`- Let u_1, u_2,... be a sequence of random numbers produced by a linear`,
`  congruential generator. Then the points (u_1,u_2,...,u_t),`,
`  (u_2,u_3,...,u_t+1),... must lie on a finite number of equally spaced`,
`  parallel hyperplanes in t-space.`,
``,
`- The ratio r_t = d_t/d_tmin can be used to measure the relative quality of`,
`  multiplier.`,
`  Where: d_t = the interplanar distance, and`,
`  d_tmin = best possible interplanar distance`,
`  Values of r_t < 4 are considered to be quite good.`,
``,
`- In a related approach, mu_t is used as a measure of goodness such that`,
`  The generator passes the spectral test for mu_t > 0.1`,
`  and passes with flying colors if mu_t >= 1 fot 2 <= t <= 6.`,
``,
`- The procedure prints the following values for each integral t`,
`  between 1 and T:`,
`  t = Number of Dimensions`,
`  nu = Number of hyperplanes`,
`  nu^2`,
`  d = 1/nu = Interplanar distance`,
`  mu`,
`  r = d/d_min`,
``, 
`EXAMPLES:`,
``,
`> spectral(999999999989, 427419669081, 6);`,
``,
`t = 2, nu = 807293.242600, nu^2 = 651722379493.000000, d = 0.000001,`,
`mu = 2.047446, r = 1.751795 `,
``,
`t = 3, nu = 8268.191640, nu^2 = 68362993.000000 , d = 0.000121, mu =2.367665,`,
`r = 2.197603`,
``,
`t = 4, nu = 771.920980, nu^2 = 595862.000000 , d = 0.001295, mu = 1.752109,`,
`r = 2.866050`,
``,
`t = 5, nu = 225.986725, nu^2 = 51070.000000 , d = 0.004425, mu = 3.102509,`,
`r = 2.891273`,
``,
`t = 6, nu = 81.455509, nu^2 = 6635.000000 , d = 0.012277, mu = 1.509458, `,
`r = 3.663098`,
``,
`                                      6635`
):



`help/text/MaxPeriod` := TEXT (
`FUNCTION: MaxPeriod - determines if a linear congruential generator`,
`                      produces maximum period`,
``,
`CALLING SEQUENCE:`,
`MaxPeriod( p, a )`,
``,
`PARAMETERS:`,
`p - Modulus of the generator--- p must be a prime`,
`a - Multiplier of the generator`,
``,
`SYNOPSIS:`,
`- MaxPeriod returns true if linear congruential generator with prime`,
`  modulus p, multiplier a, and increment 0, produces maximum period`,
``,
`EXAMPLES:`,
`> MaxPeriod(999999999989, 427419669081);`,
``,
`                                       true`,
``
):



######################################################################
# Maxperiod(p,a)  --  returns true if linear congruential generator  #
# with prime modulus p, multiplier a, and increment = 0 produces     #
# maximal period.                                                    #
#                                                                    #
# For detailed explanation see Knuth (vol2) page 15-20               #
#                                                                    #
# Programmer: Zaven Karian                                           #
# Language:  Maple Version 5 Release 2                               #
######################################################################
MaxPeriod:=proc(p:posint,a:posint)
    local f, n, q, e, m3, m6, m9,
    v, num, d9, d6, d3, d0, MP, i:
if not(isprime(p)) then ERROR(`First argument must be a prime.`) fi;
if p=2 and type(a, odd) then RETURN(`true`) elif p=2 and type(a,even)
    then RETURN(`false`) fi;
f:=readlib(ifactors)(p-1) [2];
n:=nops(f);
q:=[seq(i[1], i=f)];
e:=[seq((p-1)/q[i], i=1..n)];
m3:=irem(a^1000, p):
m6:=irem(m3^1000,p):
m9:=irem(m6^1000,p):
MP:=true;

for i from 1 to n                                       do
    num:=e[i]:         d9:=trunc(num/10^9):
    num:=num-10^9*d9:  d6:=trunc(num/10^6):
    num:=num-10^6*d6:  d3:=trunc(num/10^3):
    d0:= num-10^3*d3:
    v:=irem(m9^d9,p)*irem(m6^d6,p)*irem(m3^d3,p)*irem(a^d0,p):
    if irem(v,p)=1 then MP:=false fi                    od;
MP
end:


macro( row=row, addrow=addrow, dotprod=dotprod );

#save `spectral.m`;
#quit
