#############################################################################
##
#W  classic.gi                  GAP group library                Frank Celler
#W                                                            & Heiko Theißen
#W                                                            & Thomas Breuer
##
#Y  Copyright (C)  1997,  Lehrstuhl D für Mathematik,  RWTH Aachen,  Germany
#Y  (C) 1998 School Math and Comp. Sci., University of St Andrews, Scotland
##


#############################################################################
##
#M  SymplecticGroupCons( <IsMatrixGroup>, <d>, <q> )
##
InstallMethod( SymplecticGroupCons,
    "matrix group for dimension and finite field size",
    [ IsMatrixGroup and IsFinite,
      IsPosInt,
      IsPosInt ],
    function( filter, d, q )
    local   g,  f,  z,  o,  mat1,  mat2,  i,  size,  qi,  c;

    # the dimension must be even
    if d mod 2 = 1  then
        Error( "the dimension <d> must be even" );
    fi;
    f := GF(q);
    z := PrimitiveRoot( f );
    o := One( f );
#T introduce variable d_2 = d/2 ?

    # if the dimension is two it is a special linear group
    if d = 2 then
        g := SL( 2, q );
#T no form for SL(2,q) if constructed like this?
#T better add the form in the SL(2,q) call

    # construct the generators
    else

        # SP(4,2)
        if d = 4 and q = 2  then
            mat1 := [ [1,0,1,1], [1,0,0,1], [0,1,0,1], [1,1,1,1] ] * o;
            mat2 := [ [0,0,1,0], [1,0,0,0], [0,0,0,1], [0,1,0,0] ] * o;

        # SP(d,q)
        else
            mat1 := IdentityMat( d, f );
            mat2 := List( 0 * mat1, ShallowCopy );
            for i  in [ 2 .. d/2 ]      do mat2[i][i-1]:= o;  od;
            for i  in [ d/2+1 .. d-1 ]  do mat2[i][i+1]:= o;  od;

            if q mod 2 = 1  then
                mat1[  1][    1] := z;
                mat1[  d][    d] := z^-1;
                mat2[  1][    1] := o;
                mat2[  1][d/2+1] := o;
                mat2[d-1][  d/2] := o;
                mat2[  d][  d/2] := -o;

            elif q <> 2  then
                mat1[    1][    1] := z;
                mat1[  d/2][  d/2] := z;
                mat1[d/2+1][d/2+1] := z^-1;
                mat1[    d][    d] := z^-1;
                mat2[    1][d/2-1] := o;
                mat2[    1][  d/2] := o;
                mat2[    1][d/2+1] := o;
                mat2[d/2+1][  d/2] := o;
                mat2[    d][  d/2] := o;

            else
                mat1[    1][  d/2] := o;
                mat1[    1][    d] := o;
                mat1[d/2+1][    d] := o;
                mat2[    1][d/2+1] := o;
                mat2[    d][  d/2] := o;
            fi;
        fi;

        mat1:=ImmutableMatrix(f,mat1,true);
        mat2:=ImmutableMatrix(f,mat2,true);
        # avoid to call 'Group' because this would check invertibility ...
        g := GroupWithGenerators( [ mat1, mat2 ] );
        SetName( g, Concatenation("Sp(",String(d),",",String(q),")") );
        SetDimensionOfMatrixGroup( g, Length( mat1 ) );
        SetFieldOfMatrixGroup( g, f );

        # add the size
        size := 1;
        qi   := 1;
        for i in [ 1 .. d/2 ] do
            qi   := qi * q^2;
            size := size * (qi-1);
        od;
        SetSize( g, q^((d/2)^2) * size );
    fi;

    # construct the form
    c := List( 0 * One( g ), ShallowCopy );
    for i  in [ 1 .. d/2 ]  do
        c[i][d-i+1] := o;
        c[d/2+i][d/2-i+1] := -o;
    od;
    SetInvariantBilinearForm( g,
        rec( matrix:= ImmutableMatrix( f, c, true ) ) );
    SetIsFullSubgroupGLorSLRespectingBilinearForm(g,true);
    SetIsSubgroupSL(g,true);

    # and return
    return g;
    end );

InstallMethod( SymplecticGroupCons,
    "matrix group for dimension and finite field",
    [ IsMatrixGroup and IsFinite,
      IsPosInt,
      IsField and IsFinite ],
function(filt,n,f)
  return SymplecticGroupCons(filt,n,Size(f));
end);



#############################################################################
##
#M  GeneralUnitaryGroupCons( <IsMatrixGroup>, <n>, <q> )
##
InstallMethod( GeneralUnitaryGroupCons,
    "matrix group for dimension and finite field size",
    [ IsMatrixGroup and IsFinite,
      IsPosInt,
      IsPosInt ],
    function( filter, n, q )
     local g, i, e, f, z, o, mat1, mat2, gens, size, qi, eps, c;

     f:= GF( q^2 );
     z:= PrimitiveRoot( f );
     o:= One( f );

     # Construct the generators.

     if n > 1 then
       mat1:= IdentityMat( n, f );
       mat2:= List( 0 * mat1, ShallowCopy );
     fi;

     if   n = 1 then
       mat1:= [ [ z ^ (q-1) ] ];

     elif n = 2 then

       # We use the isomorphism of 'SU(2,q)' and 'SL(2,q)':
       # 'e' is mapped to '-e' under the Frobenius mapping.
       e:= Z(q^2) - Z(q^2)^q;
       if q = 2 then
         mat1[1][1]:= z;
         mat1[2][2]:= z;
         mat1[1][2]:= z;
         mat2[1][2]:= o;
         mat2[2][1]:= o;
       else
         mat1[1][1]:= z;
         mat1[2][2]:= z^-q;
         mat2[1][1]:= -o;
         mat2[1][2]:= e;
         mat2[2][1]:= -e^-1;
       fi;

     elif n mod 2 = 0 then
       if q mod 2 = 1 then e:= z^( (q+1)/2 ); else e:= o; fi;
       mat1[1][1]:= z;
       mat1[n][n]:= z^-q;
       for i in [ 2 .. n/2 ]     do mat2[ i ][ i-1 ]:= o; od;
       for i in [ n/2+1 .. n-1 ] do mat2[ i ][ i+1 ]:= o; od;
       mat2[ 1 ][ 1 ]:= o;
       mat2[1][n/2+1]:= e;
       mat2[n-1][n/2]:= e^-1;
       mat2[n][ n/2 ]:= -e^-1;
     else
       mat1[(n-1)/2][(n-1)/2]:= z;
       mat1[(n-1)/2+2][(n-1)/2+2]:= z^-q;
       for i in [ 1 .. (n-1)/2-1 ] do mat2[ i ][ i+1 ]:= o; od;
       for i in [ (n-1)/2+3 .. n ] do mat2[ i ][ i-1 ]:= o; od;
       mat2[(n-1)/2][  1  ]:=  -(1+z^q/z)^-1;
       mat2[(n-1)/2][(n-1)/2+1]:= -o;
       mat2[(n-1)/2][  n  ]:=  o;
       mat2[(n-1)/2+1][  1  ]:= -o;
       mat2[(n-1)/2+1][(n-1)/2+1]:= -o;
       mat2[(n-1)/2+2][  1  ]:=  o;
     fi;

     mat1:=ImmutableMatrix(f,mat1,true);
     if n = 1 then
       gens := [ mat1 ];
     else
       mat2:=ImmutableMatrix(f,mat2,true);
       gens := [ mat1, mat2 ];
     fi;

     # Avoid to call 'Group' because this would check invertibility ...
     g:= GroupWithGenerators( gens );
     SetName( g, Concatenation("GU(",String(n),",",String(q),")") );
     SetDimensionOfMatrixGroup( g, Length( mat1 ) );
     SetFieldOfMatrixGroup( g, f );

     # Add the size.
     size := q+1;
     qi   := q;
     eps  := 1;
     for i in [ 2 .. n ] do
       qi   := qi * q;
       eps  := -eps;
       size := size * (qi+eps);
     od;
     SetSize( g, q^(n*(n-1)/2) * size );

     # construct the form
     c := Reversed( One( g ) );
     SetInvariantSesquilinearForm( g,
         rec( matrix:= ImmutableMatrix( f, c, true ) ) );
     SetIsFullSubgroupGLorSLRespectingSesquilinearForm(g,true);

     # Return the group.
     return g;
    end );


#############################################################################
##
#M  SpecialUnitaryGroupCons( <IsMatrixGroup>, <n>, <q> )
##
InstallMethod( SpecialUnitaryGroupCons,
    "matrix group for dimension and finite field size",
    [ IsMatrixGroup and IsFinite,
      IsPosInt,
      IsPosInt ],
    function( filter, n, q )
     local g, i, e, f, z, o, mat1, mat2, gens, size, qi, eps, c;

     f:= GF( q^2 );
     z:= PrimitiveRoot( f );
     o:= One( f );

     # Construct the generators.
     if n = 3 and q = 2 then

       mat1:= [ [o,z,z], [0,o,z^2], [0,0,o] ] * o;
       mat2:= [ [z,o,o], [o,o, 0 ], [o,0,0] ] * o;

     else

       mat1:= IdentityMat( n, f );
       mat2:= List( 0 * mat1, ShallowCopy );

       if   n = 2 then

         # We use the isomorphism of 'SU(2,q)' and 'SL(2,q)':
         # 'e' is mapped to '-e' under the Frobenius mapping.
         e:= Z(q^2) - Z(q^2)^q;
         if q <= 3 then
           mat1[1][2]:= e;
           mat2[1][2]:= e;
           mat2[2][1]:= -e^-1;
         else
           mat1[1][1]:= z^(q+1);
           mat1[2][2]:= z^(-q-1);
           mat2[1][1]:= -o;
           mat2[1][2]:= e;
           mat2[2][1]:= -e^-1;
         fi;

       elif n mod 2 = 0 then

         mat1[1][1]:= z;
         mat1[n][n]:= z^-q;
         mat1[2][2]:= z^-1;
         mat1[ n-1 ][ n-1 ]:= z^q;

         if q mod 2 = 1 then e:= z^( (q+1)/2 ); else e:= o; fi;
         for i in [ 2 .. n/2 ]     do mat2[ i ][ i-1 ]:= o; od;
         for i in [ n/2+1 .. n-1 ] do mat2[ i ][ i+1 ]:= o; od;
         mat2[ 1 ][ 1 ]:= o;
         mat2[1][n/2+1]:= e;
         mat2[n-1][n/2]:= e^-1;
         mat2[n][ n/2 ]:= -e^-1;

       elif n <> 1 then

         mat1[  (n-1)/2  ][  (n-1)/2  ]:= z;
         mat1[ (n-1)/2+1 ][ (n-1)/2+1 ]:= z^q/z;
         mat1[ (n-1)/2+2 ][ (n-1)/2+2 ]:= z^-q;

         for i in [ 1 .. (n-1)/2-1 ] do mat2[ i ][ i+1 ]:= o; od;
         for i in [ (n-1)/2+3 .. n ] do mat2[ i ][ i-1 ]:= o; od;
         mat2[(n-1)/2][    1    ]:=  -(1+z^q/z)^-1;
         mat2[(n-1)/2][(n-1)/2+1]:= -o;
         mat2[(n-1)/2][    n    ]:=  o;
         mat2[(n-1)/2+1][   1   ]:= -o;
         mat2[(n-1)/2+1][(n-1)/2+1]:= -o;
         mat2[(n-1)/2+2][  1  ]:=  o;
       fi;

     fi;

     mat1:=ImmutableMatrix(f,mat1,true);
     if n = 1 then
       gens := [ mat1 ];
     else
       mat2:=ImmutableMatrix(f,mat2,true);
       gens := [ mat1, mat2 ];
     fi;

     # Avoid to call 'Group' because this would check invertibility ...
     g:= GroupWithGenerators( gens );
     SetName( g, Concatenation("SU(",String(n),",",String(q),")") );
     SetDimensionOfMatrixGroup( g, Length( mat1 ) );
     SetFieldOfMatrixGroup( g, f );

     # Add the size.
     size := 1;
     qi   := q;
     eps  := 1;
     for i in [ 2 .. n ] do
       qi   := qi * q;
       eps  := -eps;
       size := size * (qi+eps);
     od;
     SetSize( g, q^(n*(n-1)/2) * size );

     # construct the form
     c := Reversed( One( g ) );
     SetInvariantSesquilinearForm( g,
         rec( matrix:= ImmutableMatrix( f, c, true ) ) );
     SetIsFullSubgroupGLorSLRespectingSesquilinearForm(g,true);
     SetIsSubgroupSL(g,true);

     # Return the group.
     return g;
    end );


#############################################################################
##
#M  SetInvariantQuadraticFormFromMatrix( <g>, <mat> )
##
##  Set the invariant quadratic form of <g>  to the matrix <mat>, and also
##  set the bilinear form to the value required by the documentation, i.e.,
#   to <mat> + <mat>^T.
##
BindGlobal( "SetInvariantQuadraticFormFromMatrix", function( g, mat )
    SetInvariantQuadraticForm( g, rec( matrix:= mat ) );
    SetInvariantBilinearForm( g, rec( matrix:= mat+TransposedMat(mat) ) );
end );


#############################################################################
##
#F  Oplus45() . . . . . . . . . . . . . . . . . . . . . . . . . . . . O+_4(5)
##
BindGlobal( "Oplus45", function()
    local   f,  id,  tau2,  tau,  phi,  delta,  eichler,  g;

    # identity matrix over <f>
    f  := GF(5);
    id := Immutable( IdentityMat( 4, f ) );

    # construct TAU2: tau(x1-x2)
    tau2 := List( 0*id, ShallowCopy );
    tau2[1][1] := One( f );
    tau2[2][2] := One( f );
    tau2[3][4] := One( f );
    tau2[4][3] := One( f );

    # construct TAU: tau(x1+x2)
    tau := List( 0*id, ShallowCopy );
    tau[1][1] := One( f );
    tau[2][2] := One( f );
    tau[3][4] := -One( f );
    tau[4][3] := -One( f );

    # construct PHI: phi(2)
    phi := List( id, ShallowCopy );
    phi[1][1] := 2*One( f );
    phi[2][2] := 3*One( f );

    # construct DELTA: u <-> v
    delta := List( id, ShallowCopy );
    delta{[1,2]}{[1,2]} := [[0,1],[1,0]]*One( f );

    # construct eichler transformation
    eichler := [[1,0,0,0],[-1,1,-1,0],[2,0,1,0],[0,0,0,1]]*One( f );

    # construct the group without calling 'Group'
    g := [ phi*tau2, tau*eichler*delta ];
    g:=List(g,i->ImmutableMatrix(f,i));
    g := GroupWithGenerators( g );
    SetDimensionOfMatrixGroup( g, 4 );
    SetFieldOfMatrixGroup( g, f );

    # set the size
    SetSize( g, 28800 );

    # construct the forms
    SetInvariantQuadraticFormFromMatrix( g, ImmutableMatrix( f,
        [[0,1,0,0],[0,0,0,0],[0,0,1,0],[0,0,0,1]] * One( f ), true ) );

    # and return
    return g;
end );


#############################################################################
##
#F  Opm3( <s>, <d> )  . . . . . . . . . . . . . . . . . . . . . .  O+-_<d>(3)
##
##  <q> must be 3, <d> at least 6,  beta is 2
##
BindGlobal( "Opm3", function( s, d )
    local   f,  id,  theta,  i,  theta2,  phi,  eichler,  g,  delta;

    # identity matrix over <f>
    f  := GF(3);
    id := Immutable( IdentityMat( d, f ) );

    # construct DELTA: u <-> v, x -> x
    delta := List( id, ShallowCopy );
    delta{[1,2]}{[1,2]} := [[0,1],[1,0]]*One( f );

    # construct THETA: x2 -> ... -> xk -> x2
    theta := List( 0*id, ShallowCopy );
    theta[1][1] := One( f );
    theta[2][2] := One( f );
    theta[3][3] := One( f );
    for i  in [ 4 .. d-1 ]  do
        theta[i][i+1] := One( f );
    od;
    theta[d][4] := One( f );

    # construct THETA2: x2 -> x1 -> x3 -> x2
    theta2 := List( id, ShallowCopy );
    theta2{[3..5]}{[3..5]} := [[0,1,1],[-1,-1,1],[1,-1,1]]*One( f );

    # construct PHI: u -> au, v -> a^-1v, x -> x
    phi := List( id, ShallowCopy );
    phi[1][1] := 2*One( f );
    phi[2][2] := (2*One( f ))^-1;

    # construct the eichler transformation
    eichler := List( id, ShallowCopy );
    eichler[2][1] := -One( f );
    eichler[2][4] := -One( f );
    eichler[4][1] := 2*One( f );

    # construct the group without calling 'Group'
    g := [ phi*theta2, theta*eichler*delta ];
    g:=List(g,i->ImmutableMatrix(f,i,true));
    g := GroupWithGenerators( g );
    SetDimensionOfMatrixGroup( g, d );
    SetFieldOfMatrixGroup( g, f );

    # construct the forms
    delta := List( id, ShallowCopy );
    delta{[1,2]}{[1,2]} := [[0,1],[0,0]]*One( f );
    delta[3][3] := One( f )*2;
    delta := ImmutableMatrix( f, delta, true );
    SetInvariantQuadraticFormFromMatrix( g, delta );

    # set the size
    delta  := 1;
    theta  := 1;
    theta2 := 3^2;
    for i  in [ 1 .. d/2-1 ]  do
        theta := theta * theta2;
        delta := delta * (theta-1);
    od;
    SetSize( g, 2*3^(d/2*(d/2-1))*(3^(d/2)-s)*delta );

    return g;
end );


#############################################################################
##
#F  OpmSmall( <s>, <d>, <q> ) . . . . . . . . . . . . . . . . .  O+-_<d>(<q>)
##
##  <q> must be 3 or 5, <d> at least 6,  beta is 1
##
BindGlobal( "OpmSmall", function( s, d, q )
    local   f,  id,  theta,  i,  theta2,  phi,  eichler,  g,  delta;

    # identity matrix over <f>
    f  := GF(q);
    id := Immutable( IdentityMat( d, f ) );

    # construct DELTA: u <-> v, x -> x
    delta := List( id, ShallowCopy );
    delta{[1,2]}{[1,2]} := [[0,1],[1,0]]*One( f );

    # construct THETA: x2 -> ... -> xk -> x2
    theta := List( 0*id, ShallowCopy );
    theta[1][1] := One( f );
    theta[2][2] := One( f );
    theta[3][3] := One( f );
    for i  in [ 4 .. d-1 ]  do
        theta[i][i+1] := One( f );
    od;
    theta[d][4] := One( f );

    # construct THETA2: x2 -> x1 -> x3 -> x2
    theta2 := List( id, ShallowCopy );
    theta2{[3..5]}{[3..5]} := [[0,0,1],[1,0,0],[0,1,0]]*One( f );

    # construct PHI: u -> au, v -> a^-1v, x -> x
    phi := List( id, ShallowCopy );
    phi[1][1] := 2*One( f );
    phi[2][2] := (2*One( f ))^-1;

    # construct the eichler transformation
    eichler := List( id, ShallowCopy );
    eichler[2][1] := -One( f );
    eichler[2][4] := -One( f );
    eichler[4][1] := 2*One( f );

    # construct the group without calling 'Group'
    g := [ phi*theta2, theta*eichler*delta ];
    g:=List(g,i->ImmutableMatrix(f,i,true));
    g := GroupWithGenerators( g );
    SetDimensionOfMatrixGroup( g, d );
    SetFieldOfMatrixGroup( g, f );

    # construct the forms
    delta := List( id, ShallowCopy );
    delta{[1,2]}{[1,2]} := [[0,1],[0,0]]*One( f );
    delta[3][3] := One( f );
    delta := ImmutableMatrix( f, delta, true );
    SetInvariantQuadraticFormFromMatrix( g, delta );

    # set the size
    delta  := 1;
    theta  := 1;
    theta2 := q^2;
    for i  in [ 1 .. d/2-1 ]  do
        theta := theta * theta2;
        delta := delta * (theta-1);
    od;
    SetSize( g, 2*q^(d/2*(d/2-1))*(q^(d/2)-s)*delta );

    return g;
end );


#############################################################################
##
#F  OpmOdd( <s>, <d>, <q> ) . . . . . . . . . . . . . . . . . . O<s>_<d>(<q>)
##
BindGlobal( "OpmOdd", function( s, d, q )
    local   f,  w,  beta,  epsilon,  id,  eb1,  tau,  theta,  i,  phi,
            delta,  eichler,  g;

    # <d> must be at least 4
    if d mod 2 = 1  then
        Error( "<d> must be even" );
    fi;
    if d < 4  then
        Error( "<d> must be at least 4" );
    fi;

    # beta is either 1 or a generator of the field
    f := GF(q);
    w := LogFFE( -1*2^(d-2)*One( f ), PrimitiveRoot( f ) ) mod 2 = 0;
    beta := One( f );
    if s = +1 and (d*(q-1)/4) mod 2 = 0  then
        if not w  then
            beta := PrimitiveRoot( f );
        fi;
    elif s = +1 and (d*(q-1)/4) mod 2 = 1  then
        if w  then
            beta := PrimitiveRoot( f );
        fi;
    elif s = -1 and (d*(q-1)/4) mod 2 = 1  then
        if not w  then
            beta := PrimitiveRoot( f );
        fi;
    elif s = -1 and (d*(q-1)/4) mod 2 = 0  then
        if w  then
            beta := PrimitiveRoot( f );
        fi;
    else
        Error( "<s> must be -1 or +1" );
    fi;

    # special cases
    if q = 3 and d = 4 and s = +1  then
        g := GroupWithGenerators( [
                    [[1,0,0,0],[0,1,2,1],[2,0,2,0],[1,0,0,1]]*One( f ),
                    [[0,2,2,2],[0,1,1,2],[1,0,2,0],[1,2,2,0]]*One( f ) ] );
        SetInvariantQuadraticFormFromMatrix( g, ImmutableMatrix( f,
          [[0,1,0,0],[0,0,0,0],[0,0,2,0],[0,0,0,1]]*One( f ), true ) );
        SetSize( g, 1152 );
        return g;
    elif q = 3 and d = 4 and s = -1  then
        g := GroupWithGenerators( [
                    [[0,2,0,0],[2,1,0,1],[0,2,0,1],[0,0,1,0]]*One( f ),
                    [[2,0,0,0],[1,2,0,2],[1,0,0,1],[0,0,1,0]]*One( f ) ] );
        SetInvariantQuadraticFormFromMatrix( g, ImmutableMatrix( f,
          [[0,1,0,0],[0,0,0,0],[0,0,1,0],[0,0,0,1]]*One( f ), true ) );
        SetSize( g, 1440 );
        return g;
    elif q = 5 and d = 4 and s = +1  then
        return Oplus45();
    elif ( q = 3 or q = 5 ) and 4 < d and beta = One( f )  then
        return OpmSmall( s, d, q );
    elif q = 3 and 4 < d and beta <> One( f )  then
        return Opm3( s, d );
    fi;

    # find an epsilon such that (epsilon^2*beta)^2 <> 1
    if beta = PrimitiveRoot( f )  then
        epsilon := One( f );
    else
        epsilon := PrimitiveRoot( f );
    fi;

    # identity matrix over <f>
    id := Immutable( IdentityMat( d, f ) );

    # construct the reflection TAU_epsilon*x1+x2
    eb1 := epsilon^2*beta+1;
    tau := List( id, ShallowCopy );
    tau[3][3] := 1-2*beta*epsilon^2/eb1;
    tau[3][4] := -2*beta*epsilon/eb1;
    tau[4][3] := -2*epsilon/eb1;
    tau[4][4] := 1-2/eb1;

    # construct THETA
    theta := List( 0*id, ShallowCopy );
    theta[1][1] := One( f );
    theta[2][2] := One( f );
    theta[3][3] := One( f );
    for i  in [ 4 .. d-1 ]  do
        theta[i][i+1] := One( f );
    od;
    theta[d][4] := -One( f );

    # construct PHI: u -> au, v -> a^-1v, x -> x
    phi := List( id, ShallowCopy );
    phi[1][1] := PrimitiveRoot( f );
    phi[2][2] := PrimitiveRoot( f )^-1;

    # construct DELTA: u <-> v, x -> x
    delta := List( id, ShallowCopy );
    delta{[1,2]}{[1,2]} := [[0,1],[1,0]]*One( f );

    # construct the eichler transformation
    eichler := List( id, ShallowCopy );
    eichler[2][1] := -One( f );
    eichler[2][4] := -One( f );
    eichler[4][1] := 2*One( f );

    # construct the group without calling 'Group'
    g := [ phi, theta*tau*eichler*delta ];
    g:=List(g,i->ImmutableMatrix(f,i,true));
    g := GroupWithGenerators( g );
    SetDimensionOfMatrixGroup( g, d );
    SetFieldOfMatrixGroup( g, f );

    # construct the forms
    delta := List( id, ShallowCopy );
    delta{[1,2]}{[1,2]} := [[0,1],[0,0]]*One( f );
    delta[3][3] := beta;
    delta := ImmutableMatrix( f, delta, true );
    SetInvariantQuadraticFormFromMatrix( g, delta );

    # set the size
    delta := 1;
    theta := 1;
    tau   := q^2;
    for i  in [ 1 .. d/2-1 ]  do
        theta := theta * tau;
        delta := delta * (theta-1);
    od;
    SetSize( g, 2*q^(d/2*(d/2-1))*(q^(d/2)-s)*delta );

    return g;
end );


#############################################################################
##
#F  Oplus2( <q> ) . . . . . . . . . . . . . . . . . . . . . . . . . O+_2(<q>)
##
BindGlobal( "Oplus2", function( q )
    local   z,  f,  m1,  m2,  g;

    # a field generator
    z := Z(q);
    f := GF(q);

    # a matrix of order q-1
    m1 := [ [ z, 0*z ], [ 0*z, z^-1 ] ];

    # a matrix of order 2
    m2 := [ [ 0, 1 ], [ 1, 0 ] ] * z^0;

    m1:= ImmutableMatrix( f, m1, true );
    m2:= ImmutableMatrix( f, m2, true );
    # construct the group, set the order, and return
    g := GroupWithGenerators( [ m1, m2 ] );
    SetInvariantQuadraticFormFromMatrix( g, ImmutableMatrix( f,
        [ [ 0, 1 ], [ 0, 0 ] ] * z^0, true ) );
    SetSize( g, 2*(q-1) );
    return g;
end );


#############################################################################
##
#F  Oplus4Even( <q> ) . . . . . . . . . . . . . . . . . . . . . . . O+_4(<q>)
##
BindGlobal( "Oplus4Even", function( q )
    local   f,  id,  rho,  delta,  phi,  eichler,  g;

    # <q> must be even
    if q mod 2 = 1  then
        Error( "<q> must be even" );
    fi;
    f := GF(q);

    # identity matrix over <f>
    id := Immutable( IdentityMat( 4, f ) );

    # construct RHO: x1 <-> y1
    rho := List( id, ShallowCopy );
    rho{[3,4]}{[3,4]} := [[0,1],[1,0]] * One( f );

    # construct DELTA: u <-> v
    delta := List( id, ShallowCopy );
    delta{[1,2]}{[1,2]} := [[0,1],[1,0]]*One( f );

    # construct PHI: u -> au, v -> a^-1v, x -> x
    phi := List( id, ShallowCopy );
    phi[1][1] := PrimitiveRoot( f );
    phi[2][2] := PrimitiveRoot( f )^-1;

    # construct eichler transformation
    eichler := [[1,0,0,0],[0,1,-1,0],[0,0,1,0],[1,0,0,1]] * One( f );

    # construct the group without calling 'Group'
    g := [ phi*rho, rho*eichler*delta ];
    g:=List(g,i->ImmutableMatrix(f,i,true));
    g := GroupWithGenerators( g );
    SetDimensionOfMatrixGroup( g, 4 );
    SetFieldOfMatrixGroup( g, f );

    # set the size
    SetSize( g, 2*q^2*(q^2-1)^2 );

    # construct the forms
    SetInvariantQuadraticFormFromMatrix( g, ImmutableMatrix( f,
      [[0,1,0,0],[0,0,0,0],[0,0,0,1],[0,0,0,0]] * One( f ), true ) );

    # and return
    return g;
end );


#############################################################################
##
#F  OplusEven( <d>, <q> ) . . . . . . . . . . . . . . . . . . . . O+_<d>(<q>)
##
BindGlobal( "OplusEven", function( d, q )
    local   f,  id,  k,  phi,  delta,  theta,  i,  delta2,  eichler,
            rho,  g;

    # <d> and <q> must be even
    if d mod 2 = 1  then
        Error( "<d> must be even" );
    fi;
    if d < 6  then
        Error( "<d> must be at least 6" );
    fi;
    if q mod 2 = 1  then
        Error( "<q> must be even" );
    fi;
    f := GF(q);

    # identity matrix over <f>
    id := Immutable( IdentityMat( d, f ) );

    # V = H | H_1 | ... | H_k
    k := (d-2) / 2;

    # construct PHI: u -> au, v -> a^-1v, x -> x
    phi := List( id, ShallowCopy );
    phi[1][1] := PrimitiveRoot( f );
    phi[2][2] := PrimitiveRoot( f )^-1;

    # construct DELTA: u <-> v, x -> x
    delta := List( id, ShallowCopy );
    delta{[1,2]}{[1,2]} := [[0,1],[1,0]]*One( f );

    # construct THETA: x_2 -> x_3 -> .. -> x_k -> y_2 -> .. -> y_k -> x_2
    theta := List( 0*id, ShallowCopy );
    for i  in [ 1 .. 4 ]  do
        theta[i][i] := One( f );
    od;
    for i  in [ 2 .. k-1 ]  do
        theta[1+2*i][3+2*i] := One( f );
        theta[2+2*i][4+2*i] := One( f );
    od;
    theta[1+2*k][6] := One( f );
    theta[2+2*k][5] := One( f );

    # (k even) construct DELTA2: x_i <-> y_i, 1 <= i <= k-1
    if k mod 2 = 0  then
        delta2 := List( 0*id, ShallowCopy );
        delta2{[1,2]}{[1,2]} := [[1,0],[0,1]] * One( f );
        for i  in [ 1 .. k ]  do
            delta2[1+2*i][2+2*i] := One( f );
            delta2[2+2*i][1+2*i] := One( f );
        od;

    # (k odd) construct DELTA2: x_1 <-> y_1, x_i <-> x_i+1, y_i <-> y_i+1
    else
        delta2 := List( 0*id, ShallowCopy );
        delta2{[1,2]}{[1,2]} := [[1,0],[0,1]] * One( f );
        delta2{[3,4]}{[3,4]} := [[0,1],[1,0]] * One( f );
        for i  in [ 2, 4 .. k-1 ]  do
            delta2[1+2*i][3+2*i] := One( f );
            delta2[3+2*i][1+2*i] := One( f );
            delta2[2+2*i][4+2*i] := One( f );
            delta2[4+2*i][2+2*i] := One( f );
        od;
    fi;

    # construct eichler transformation
    eichler := List( id, ShallowCopy );
    eichler[4][6] := One( f );
    eichler[5][3] := -One( f );

    # construct RHO = THETA * EICHLER
    rho := theta*eichler;

    # construct second eichler transformation
    eichler := List( id, ShallowCopy );
    eichler[2][5] := -One( f );
    eichler[6][1] := One( f );

    # there seems to be something wrong in I/E for p=2
    if k mod 2 = 0  then
        if q = 2  then
            g := [ phi*delta2, rho, eichler, delta ];
        else
            g := [ phi*delta2, rho*eichler*delta, delta ];
        fi;
    elif q = 2  then
        g := [ phi*delta2, rho*eichler*delta, rho*delta ];
    else
        g := [ phi*delta2, rho*eichler*delta ];
    fi;

    # construct the group without calling 'Group'
    g:=List(g,i->ImmutableMatrix(f,i,true));
    g := GroupWithGenerators( g );
    SetDimensionOfMatrixGroup( g, d );
    SetFieldOfMatrixGroup( g, f );

    # construct the forms
    delta := List( 0*id, ShallowCopy );
    for i  in [ 1 .. d/2 ]  do
        delta[2*i-1][2*i] := One( f );
    od;
    SetInvariantQuadraticFormFromMatrix( g, ImmutableMatrix( f, delta, true ) );

    # set the size
    delta := 1;
    theta := 1;
    rho   := q^2;
    for i  in [ 1 .. d/2-1 ]  do
        theta := theta * rho;
        delta := delta * (theta-1);
    od;
    SetSize( g, 2*q^(d/2*(d/2-1))*(q^(d/2)-1)*delta );

    return g;
end );


#############################################################################
##
#F  Ominus2( <q> )  . . . . . . . . . . . . . . . . . . . . . . . . O-_2(<q>)
##
BindGlobal( "Ominus2", function( q )
    local z, f, R, x, t, n, e, bc, m2, m1, g;

    # construct the root
    z := Z(q);

    # find $x^2+x+t$ that is irreducible over GF(`q')
    f:= GF( q );
    R:= PolynomialRing( f );
    x:= Indeterminate( f );
    t:= z^First( [ 0 .. q-2 ], u -> Length( Factors( R, x^2+x+z^u ) ) = 1 );

    # get roots in GF(q^2)
    n := List( Factors( PolynomialRing( GF( q^2 ) ), x^2+x+t ),
               x -> - CoefficientsOfLaurentPolynomial( x )[1][1] );
    e := 4*t-1;

    # construct base change
    bc := [ [ n[1]/e, 1/e ], [ n[2], z^0 ] ];

    # matrix of order 2
    m2 := [ [ -1, 0 ], [ -1, 1 ] ] * z^0;

    # matrix of order q+1 (this will lie in $GF(q)^{d \times d}$)
    z  := Z(q^2)^(q-1);
    m1 := bc^-1 * [[z,0*z],[0*z,z^-1]] * bc;

    # and return the group
    m1:=ImmutableMatrix(GF(q),m1,true);
    m2:=ImmutableMatrix(GF(q),m2,true);
    g := GroupWithGenerators( [ m1, m2 ] );
    SetInvariantQuadraticFormFromMatrix( g, ImmutableMatrix( f,
      [ [ 1, 1 ], [ 0, t ] ] * z^0, true ) );
    SetSize( g, 2*(q+1) );

    return g;
end );


#############################################################################
##
#F  Ominus4Even( <q> )  . . . . . . . . . . . . . . . . . . . . . . O-_4(<q>)
##
BindGlobal( "Ominus4Even", function( q )
    local f, id, rho, delta, phi, R, x, t, eichler, g;

    # <q> must be even
    if q mod 2 = 1  then
        Error( "<q> must be even" );
    fi;
    f := GF(q);

    # identity matrix over <f>
    id := Immutable( IdentityMat( 4, f ) );

    # construct RHO: x1 <-> y1
    rho := List( id, ShallowCopy );
    rho{[3,4]}{[3,4]} := [[0,1],[1,0]] * One( f );

    # construct DELTA: u <-> v
    delta := List( id, ShallowCopy );
    delta{[1,2]}{[1,2]} := [[0,1],[1,0]]*One( f );

    # construct PHI: u -> au, v -> a^-1v, x -> x
    phi := List( id, ShallowCopy );
    phi[1][1] := PrimitiveRoot( f );
    phi[2][2] := PrimitiveRoot( f )^-1;

    # find x^2+x+t that is irreducible over <f>
    R:= PolynomialRing( f, 1 );
    x:= Indeterminate( f );
    t:= First( [ 0 .. q-2 ],
               u -> Length( Factors( R, x^2+x+PrimitiveRoot( f )^u ) ) = 1 );

    # compute square root of <t>
    t := t/2 mod (q-1);
    t := PrimitiveRoot( f )^t;

    # construct eichler transformation
    eichler := [[1,0,0,0],[-t,1,-1,0],[0,0,1,0],[1,0,0,1]] * One( f );

    # construct the group without calling 'Group'
    g := [ phi*rho, rho*eichler*delta ];
    g:=List(g,i->ImmutableMatrix(f,i,true));
    g := GroupWithGenerators( g );
    SetDimensionOfMatrixGroup( g, 4 );
    SetFieldOfMatrixGroup( g, f );

    # set the size
    SetSize( g, 2*q^2*(q^2+1)*(q^2-1) );

    # construct the forms
    SetInvariantQuadraticFormFromMatrix( g, ImmutableMatrix( f,
      [[0,1,0,0],[0,0,0,0],[0,0,t,1],[0,0,0,t]] * One( f ), true ) );

    # and return
    return g;
end );


#############################################################################
##
#F  OminusEven( <d>, <q> )  . . . . . . . . . . . . . . . . . . . O-_<d>(<q>)
##
BindGlobal( "OminusEven", function( d, q )
    local f, id, k, phi, delta, theta, i, delta2, eichler, rho, g, t, R, x;

    # <d> and <q> must be odd
    if d mod 2 = 1  then
        Error( "<d> must be even" );
    elif d < 6  then
        Error( "<d> must be at least 6" );
    elif q mod 2 = 1  then
        Error( "<q> must be even" );
    fi;
    f := GF(q);

    # identity matrix over <f>
    id := Immutable( IdentityMat( d, f ) );

    # V = H | H_1 | ... | H_k
    k := (d-2) / 2;

    # construct PHI: u -> au, v -> a^-1v, x -> x
    phi := List( id, ShallowCopy );
    phi[1][1] := PrimitiveRoot( f );
    phi[2][2] := PrimitiveRoot( f )^-1;

    # construct DELTA: u <-> v, x -> x
    delta := List( id, ShallowCopy );
    delta{[1,2]}{[1,2]} := [[0,1],[1,0]]*One( f );

    # construct THETA: x_2 -> x_3 -> .. -> x_k -> y_2 -> .. -> y_k -> x_2
    theta := List( 0*id, ShallowCopy );
    for i  in [ 1 .. 4 ]  do
        theta[i][i] := One( f );
    od;
    for i  in [ 2 .. k-1 ]  do
        theta[1+2*i][3+2*i] := One( f );
        theta[2+2*i][4+2*i] := One( f );
    od;
    theta[1+2*k][6] := One( f );
    theta[2+2*k][5] := One( f );

    # (k even) construct DELTA2: x_i <-> y_i, 1 <= i <= k-1
    if k mod 2 = 0  then
        delta2 := List( 0*id, ShallowCopy );
        delta2{[1,2]}{[1,2]} := [[1,0],[0,1]] * One( f );
        for i  in [ 1 .. k ]  do
            delta2[1+2*i][2+2*i] := One( f );
            delta2[2+2*i][1+2*i] := One( f );
        od;

    # (k odd) construct DELTA2: x_1 <-> y_1, x_i <-> x_i+1, y_i <-> y_i+1
    else
        delta2 := List( 0*id, ShallowCopy );
        delta2{[1,2]}{[1,2]} := [[1,0],[0,1]] * One( f );
        delta2{[3,4]}{[3,4]} := [[0,1],[1,0]] * One( f );
        for i  in [ 2, 4 .. k-1 ]  do
            delta2[1+2*i][3+2*i] := One( f );
            delta2[3+2*i][1+2*i] := One( f );
            delta2[2+2*i][4+2*i] := One( f );
            delta2[4+2*i][2+2*i] := One( f );
        od;
    fi;

    # find x^2+x+t that is irreducible over GF(`q')
    R:= PolynomialRing( f );
    x:= Indeterminate( f );
    t:= First( [ 0 .. q-2 ],
               u -> Length( Factors( R, x^2+x+PrimitiveRoot( f )^u ) ) = 1 );

    # compute square root of <t>
    t := t/2 mod (q-1);
    t := PrimitiveRoot( f )^t;

    # construct Eichler transformation
    eichler := List( id, ShallowCopy );
    eichler[4][6] := One( f );
    eichler[5][3] := -One( f );
    eichler[5][6] := -t;

    # construct RHO = THETA * EICHLER
    rho := theta*eichler;

    # construct second eichler transformation
    eichler := List( id, ShallowCopy );
    eichler[2][5] := -One( f );
    eichler[6][1] := One( f );

    # there seems to be something wrong in I/E for p=2
    if k mod 2 = 0  then
        if q = 2  then
            g := [ phi*delta2, rho, eichler, delta ];
        else
            g := [ phi*delta2, rho*eichler*delta, delta ];
        fi;
    elif q = 2  then
        g := [ phi*delta2, rho*eichler*delta, rho*delta ];
    else
        g := [ phi*delta2, rho*eichler*delta ];
    fi;

    # construct the group without calling 'Group'
    g:=List(g,i->ImmutableMatrix(f,i,true));
    g := GroupWithGenerators( g );
    SetDimensionOfMatrixGroup( g, d );
    SetFieldOfMatrixGroup( g, f );

    # construct the forms
    delta := List( 0*id, ShallowCopy );
    for i  in [ 1 .. d/2 ]  do
        delta[2*i-1][2*i] := One( f );
    od;
    delta[3][3] := t;
    delta[4][4] := t;
    SetInvariantQuadraticFormFromMatrix( g, ImmutableMatrix( f, delta, true ) );

    # set the size
    delta := 1;
    theta := 1;
    rho   := q^2;
    for i  in [ 1 .. d/2-1 ]  do
        theta := theta * rho;
        delta := delta * (theta-1);
    od;
    SetSize( g, 2*q^(d/2*(d/2-1))*(q^(d/2)+1)*delta );

    return g;
end );


#############################################################################
##
#F  OzeroOdd( <d>, <q>, <b> ) . . . . . . . . . . . . . . . . . . O0_<d>(<q>)
##
##  'OzeroOdd'  construct  the orthogonal   group in  odd dimension  and  odd
##  characteristic. The discriminant of the quadratic form is -(2<b>)^(<d>-2)
##
BindGlobal( "OzeroOdd", function( d, q, b )
    local   id,  phi,  delta,  rho,  i,  eichler,  g,  s,  f,  q2,  q2i;

    # <d> and <q> must be odd
    if d mod 2 = 0  then
        Error( "<d> must be odd" );
    elif d < 3  then
        Error( "<d> must be at least 3" );
    elif q mod 2 = 0  then
        Error( "<q> must be odd" );
    fi;
    f := GF(q);

    # identity matrix over <f>
    id := Immutable( IdentityMat( d, f ) );

    # construct PHI: u -> au, v -> a^-1v, x -> x
    phi := List( id, ShallowCopy );
    phi[1][1] := PrimitiveRoot( f );
    phi[2][2] := PrimitiveRoot( f )^-1;

    # construct DELTA: u <-> v, x -> x
    delta := List( id, ShallowCopy );
    delta{[1,2]}{[1,2]} := [[0,1],[1,0]]*One( f );

    # construct RHO: u -> u, v -> v, x_i -> x_i+1
    rho := List( 0*id, ShallowCopy );
    rho[1][1] := One( f );
    rho[2][2] := One( f );
    for i  in [ 3 .. d-1 ]  do
        rho[i][i+1] := One( f );
    od;
    rho[d][3] := One( f );

    # construct eichler transformation
    eichler := List( id, ShallowCopy );
    eichler{[1..3]}{[1..3]} := [[1,0,0],[-b,1,-1],[2*b,0,1]] * One( f );

    # construct the group without calling 'Group'
    g := [ phi, rho*eichler*delta ];
    g:=List(g,i->ImmutableMatrix(f,i,true));
    g := GroupWithGenerators( g );
    SetDimensionOfMatrixGroup( g, d );
    SetFieldOfMatrixGroup( g, f );

    # and set its size
    s   := 1;
    q2  := q^2;
    q2i := 1;
    for i  in [ 1 .. (d-1)/2 ]  do
        q2i := q2 * q2i;
        s   := s  * (q2i-1);
    od;
    SetSize( g, 2 * q^((d-1)^2/4) * s );

    # construct the forms
    s := List( b*id, ShallowCopy );
    s{[1,2]}{[1,2]} := [[0,1],[0,0]]*One( f );
    SetInvariantQuadraticFormFromMatrix( g, ImmutableMatrix( f, s, true ) );

    # and return
    return g;
end );


#############################################################################
##
#F  OzeroEven( <d>, <q> ) . . . . . . . . . . . . . . . . . . . . O0_<d>(<q>)
##
##  'OzeroEven' constructs the orthogonal group in odd dimension and even
##  characteristic.
##  The generators are constructed via the isomorphism with the symplectic
##  group in dimension $<d>-1$ over the field with <q> elements.
##
##  Removing the first row and the first column from the matrices defines the
##  isomorphism to the symplectic group.
##  This group is *not* equal to the symplectic group constructed with the
##  function `Sp',
##  since the bilinear form of the orthogonal group is the one used in the
##  book of Carter and not the one used for `SP'.
##  (Note that our matrices are transposed, relative to the ones given by
##  Carter, because the group shall act on a *row* space.)
##
##  The generators of the orthogonal groups can be computed as those matrices
##  that project onto the generators of the symplectic group and satisfy the
##  quadratic form
##  $f(x) = x_0^2 + x_1 x_{-1} + x_2 x_{-2} + \cdots + x_l x_{-l}$.
##  This condition results in a quadratic equation system that can be
##  interpreted as a linear equation system because taking square roots is
##  one-to-one in characteristic $2$.
##
BindGlobal( "OzeroEven", function( d, q )
    local f, z, o, n, mat1, mat2, i, g, size, qi, s;

    # <d> must be odd, <q> must be even
    if d mod 2 = 0 then
      Error( "<d> must be odd" );
    elif d < 3 then
      Error( "<d> must be at least 3" );
    elif q mod 2 = 1 then
      Error( "<q> must be even" );
    fi;
    f:= GF(q);
    z:= PrimitiveRoot( f );
    o:= One( f );
    n:= Zero( f );

    if d = 3 then

      # The isomorphic symplectic group is $SL(2,<q>)$.
      if q = 2 then
        mat1:= ImmutableMatrix( f, [ [o,n,n], [o,o,o], [n,n,o] ],true );
        mat2:= ImmutableMatrix( f, [ [o,n,n], [n,n,o], [n,o,n] ],true );
      else
        mat1:= ImmutableMatrix( f, [ [o,n,n], [n,z,n], [n,n,z^-1] ],true );
        mat2:= ImmutableMatrix( f, [ [o,n,n], [o,o,o], [n,o,n] ],true );
      fi;

    elif d = 5 and q = 2  then

      # The isomorphic symplectic group is $SP(4,2)$.
      mat1:= ImmutableMatrix( f, [ [o,n,n,n,n], [o,n,o,n,o], [o,n,o,o,o],
                                   [n,o,n,n,o], [n,o,o,o,o] ],true );
      mat2:= ImmutableMatrix( f, [ [o,n,n,n,n], [n,n,o,n,n], [n,n,n,o,n],
                                   [n,n,n,n,o], [n,o,n,n,n] ],true );

    else

      mat1:= IdentityMat( d, f );
      mat2:= List( 0 * mat1, ShallowCopy );
      mat2[1][1]:= o;
      mat2[d][2]:= o;
      for i in [ 2 .. d-1 ] do
        mat2[i][i+1]:= o;
      od;

      if q = 2 then
        mat1[(d+1)/2][      1]:= o;
        mat1[(d+1)/2][      2]:= o;
        mat1[(d+1)/2][      d]:= o;
        mat1[(d+3)/2][      d]:= o;
      else
        mat1[      2][      2]:= z;
        mat1[(d+1)/2][(d+1)/2]:= z;
        mat1[(d+3)/2][(d+3)/2]:= z^-1;
        mat1[      d][      d]:= z^-1;
        mat2[(d+1)/2][      1]:= o;
        mat2[(d+1)/2][      2]:= o;
        mat2[(d+1)/2][      3]:= o;
        mat2[(d+3)/2][      2]:= o;
      fi;

    fi;

    mat1:= ImmutableMatrix( f, mat1,true );
    mat2:= ImmutableMatrix( f, mat2,true );

    # avoid to call 'Group' because this would check invertibility ...
    g:= GroupWithGenerators( [ mat1, mat2 ] );
    SetDimensionOfMatrixGroup( g, Length( mat1 ) );
    SetFieldOfMatrixGroup( g, f );
    SetIsSubgroupSL( g, true );

    # add the size
    size := 1;
    qi   := 1;
    for i in [ 1 .. (d-1)/2 ] do
      qi   := qi * q^2;
      size := size * (qi-1);
    od;
    SetSize( g, q^(((d-1)/2)^2) * size );

    # construct the forms
    s := List( 0 * One( g ), ShallowCopy );
    s[1][1]:= o;
    for i in [ 2 .. (d+1)/2 ] do
      s[(d-1)/2+i][i]:= o;
    od;
    s:= ImmutableMatrix( f, s, true );
    SetInvariantQuadraticFormFromMatrix( g, s );

    # and return
    return g;
end );


#############################################################################
##
#M  GeneralOrthogonalGroupCons( <e>, <d>, <q> ) . . . . . . .  GO<e>_<d>(<q>)
##
InstallMethod( GeneralOrthogonalGroupCons,
    "matrix group for <e>, dimension, and finite field size",
    [ IsMatrixGroup and IsFinite,
      IsInt,
      IsPosInt,
      IsPosInt ],
    function( filter, e, d, q )
    local   g,  i;

    # <e> must be -1, 0, +1
    if e <> -1 and e <> 0 and e <> +1  then
        Error( "sign <e> must be -1, 0, +1" );
    fi;

    # if <e> = 0  then <d> must be odd
    if e = 0 and d mod 2 = 0  then
        Error( "sign <e> = 0 but dimension <d> is even" );

    # if <e> <> 0  then <d> must be even
    elif e <> 0 and d mod 2 = 1  then
        Error( "sign <e> <> 0 but dimension <d> is odd" );
    fi;

    # construct the various orthogonal groups
    if   e = 0 and q mod 2 <> 0  then
        g := OzeroOdd( d, q, 1 );
    elif e = 0  then
        g := OzeroEven( d, q );

    # O+(2,q) = D_2(q-1)
    elif e = +1 and d = 2  then
        g := Oplus2(q);

    # if <d> = 4 and <q> even use 'Oplus4Even'
    elif e = +1 and d = 4 and q mod 2 = 0  then
        g := Oplus4Even(q);

    # if <q> is even use 'OplusEven'
    elif e = +1 and q mod 2 = 0  then
        g := OplusEven( d, q );

    # if <q> is odd use 'OpmOdd'
    elif e = +1 and q mod 2 = 1  then
        g := OpmOdd( +1, d, q );

    # O-(2,q) = D_2(q+1)
    elif e = -1 and d = 2  then
         g := Ominus2(q);

    # if <d> = 4 and <q> even use 'Ominus4Even'
    elif e = -1 and d = 4 and q mod 2 = 0  then
        g := Ominus4Even(q);

    # if <q> is even use 'OminusEven'
    elif e = -1 and q mod 2 = 0  then
        g := OminusEven( d, q );

    # if <q> is odd use 'OpmOdd'
    elif e = -1 and q mod 2 = 1  then
        g := OpmOdd( -1, d, q );
    fi;

    # set name
    if e = +1  then i := "+";  else i := "";  fi;
    SetName( g, Concatenation( "GO(", i, String(e), ",", String(d), ",",
                                   String(q), ")" ) );

    SetIsFullSubgroupGLorSLRespectingQuadraticForm( g, true );
    if q mod 2 = 1 then
      SetIsFullSubgroupGLorSLRespectingBilinearForm( g, true );
#T in which cases does characteristic 2 imply `false'?
    fi;

    # and return
    return g;
end );

InstallMethod( GeneralOrthogonalGroupCons,
    "matrix group for dimension and finite field",
    [ IsMatrixGroup and IsFinite,
      IsInt,
      IsPosInt,
      IsField and IsFinite ],
function(filt,sign,n,f)
  return GeneralOrthogonalGroupCons(filt,sign,n,Size(f));
end);


#############################################################################
##
#M  SpecialOrthogonalGroupCons( <e>, <d>, <q> ) . . . . . . .  GO<e>_<d>(<q>)
##
##  SO has index $1$ in GO if the characteristic is even
##  and index $2$ if the characteristic is odd.
##
##  In the latter case, the generators of GO are $a$ and $b$.
##  When GO is constructed with `OzeroOdd', `Oplus2', and `Ominus2' then by
##  construction $a$ has determinant $1$, and $b$ has determinant $-1$.
##  The group $\langle a, b^{-1} a b, b^2 \rangle$ is therefore equal to SO.
##  (Note that it is clearly contained in SO, and each word in terms of $a$
##  and $b$ can be written as a word in terms of the three generators above
##  or $b$ times such a word.)
##  So the case `OpmOdd' is left, which deals with three exceptions
##  $(s,d,q) \in \{ (1,4,3), (-1,4,3), (1,4,5) \}$, two series for small $q$
##  (via `OpmSmall' and `Opm3'), and the generic remainder;
##  exactly in the two of the three exceptional cases where $s = 1$ holds,
##  the determinant of the first generator is $-1$; in these cases, the
##  determinant of the second generator is $1$, so we get the generating set
##  $\{ a^2, a^{-1} b a, b \}$.
##
InstallMethod( SpecialOrthogonalGroupCons,
    "matrix group for <e>, dimension, and finite field size",
    [ IsMatrixGroup and IsFinite,
      IsInt,
      IsPosInt,
      IsPosInt ],
    function( filter, e, d, q )
    local G, gens, U, i;

    G:= GeneralOrthogonalGroupCons( filter, e, d, q );
    if q mod 2 = 1 then

      # Deal with the special cases.
      gens:= GeneratorsOfGroup( G );
      if e = 1 and d = 4 and q in [ 3, 5 ] then
        gens:= Reversed( gens );
      fi;

      Assert( 1, Length( gens ) = 2 and IsOne( DeterminantMat( gens[1] ) ) );

      # Construct the group.
      U:= GroupWithGenerators( [ gens[1], gens[1]^gens[2], gens[2]^2 ] );

      # Set the group order.
      SetSize( U, Size( G ) / 2 );

      # Set the name.
      if e = +1  then i := "+";  else i := "";  fi;
      SetName( U, Concatenation( "SO(", i, String(e), ",", String(d), ",",
                                     String(q), ")" ) );

      # Set the invariant quadratic form and the symmetric bilinear form.
      SetInvariantBilinearForm( U, InvariantBilinearForm( G ) );
      SetInvariantQuadraticForm( U, InvariantQuadraticForm( G ) );
      SetIsFullSubgroupGLorSLRespectingQuadraticForm( U, true );
      if q mod 2 = 1 then
        SetIsFullSubgroupGLorSLRespectingBilinearForm( U, true );
      fi;
      G:= U;

    fi;
    return G;
    end );

InstallMethod( SpecialOrthogonalGroupCons,
    "matrix group for dimension and finite field",
    [ IsMatrixGroup and IsFinite,
      IsInt,
      IsPosInt,
      IsField and IsFinite ],
function(filt,sign,n,f)
  return SpecialOrthogonalGroupCons(filt,sign,n,Size(f));
end);


#############################################################################
##
#F  OmegaZero( <d>, <q> ) . . . . . . . . . . . . . . . . \Omega^0_{<d>}(<q>)
##
BindGlobal( "OmegaZero", function( d, q )
    local f, o, m, mo, n, i, x, g, xi, h, s, q2, q2i;

    # <d> must be odd
    if d mod 2 = 0 then
      Error( "<d> must be odd" );
    elif d < 3 then
      Error( "<d> must be at least 3" );
    elif q mod 2 = 0 then
      # For even q, the generators claimed in [RylandsTalor98] are wrong:
      # For (d,q) = (5,2), the matrices generate only S4(2)' not S4(2).
      # In the other cases, the matrices would have to be transposed
      # in order to respect a form as required;
      # thus they describe a group of the right isomorphism type
      # but not an orthogonal group.
      # We return the isomorphic group SO(d,q) in these cases;
      # note that this is the definition of Omega(d,q) for odd d and even q
      # in the ATLAS of Finite Groups [CCN85, p. xi].
      return SO( d, q );
    fi;
    f:= GF(q);
    o:= One( f );
    m:= ( d-1 ) / 2;

    if 3 < d then
      # Omega(0,d,q) for d=2m+1, m >= 2, Section 4.5
      if d mod 4 = 3 then
        mo:= -o;  # (-1)^m
      else
        mo:= o;
      fi;

      n:= NullMat( d, d, f );
      n[ m+2 ][1]:= mo;
      n[m][d]:= mo;
      n[ m+1 ][ m+1 ]:= -o;
      for i in [ 1 .. m-1 ] do
        n[i][i+1]:= o;
        n[ d+1-i ][ d-i ]:= o;
      od;

      # $x = x_{\alpha_1}(1)$
      x:= IdentityMat( d, f );
      x[m][ m+1 ]:= 2*o;
      x[ m+1 ][ m+2 ]:= -o;
      x[m][ m+2 ]:= -o;

      if q <= 3 then
        # the matrices $x$ and $n$
        g:= [ x, n ];
      else
        # the matrices $h$ and $x n$
        xi:= Z(q);
        h:= IdentityMat( d, f );
        h[1][1]:= xi;
        h[m][m]:= xi;
        h[ m+2 ][ m+2 ]:= xi^-1;
        h[d][d]:= xi^-1;
        g:= [ h, x*n ];
      fi;

    else
      # Omega(0,3,q), Section 4.6
      if q <= 3 then
        # the matrices $x$ and $n$
        g:= [ [[1,0,0],[1,1,0],[-1,-2,1]],
              [[0,0,-1],[0,-1,0],[-1,0,0]] ] * o;
      else
        # the matrices $n x$ and $h$
        xi:= Z(q);
        g:= [ [[1,2,-1],[-1,-1,0],[-1,0,0]],
              [[xi^-2,0,0],[0,1,0],[0,0,xi^2]] ] * o;
      fi;
    fi;

    # construct the group without calling 'Group'
    g:= List( g, i -> ImmutableMatrix( f, i, true ) );
    g:= GroupWithGenerators( g );
    SetDimensionOfMatrixGroup( g, d );
    SetFieldOfMatrixGroup( g, f );

    # and set its size
    s  := 1;
    q2 := q^2;
    q2i:= 1;
    for i in [ 1 .. m ] do
      q2i:= q2 * q2i;
      s  := s * (q2i-1);
    od;
    if q mod 2 = 1 then
      s:= s/2;
    fi;
    SetSize( g, q^(m^2) * s );

    # construct the forms
    x:= NullMat( d, d, f );
    for i in [ 1 .. m ] do
      x[i,d-i+1] := o;
    od;
    x[m+1,m+1] := (Characteristic(f)+1)/4*o;
    SetInvariantQuadraticFormFromMatrix(g, ImmutableMatrix( f, x, true ) );

    # and return
    return g;
    end );


#############################################################################
##
#F  OmegaPlus( <d>, <q> ) . . . . . . . . . . . . . . . . \Omega^+_{<d>}(<q>)
##
BindGlobal( "OmegaPlus", function( d, q )
    local f, o, m, xi, g, a, mo, n, i, x1, x2, x, h, s, q2, q2i;

    # <d> must be even
    if d mod 2 = 1 then
      Error( "<d> must be even" );
    fi;
    f:= GF(q);
    o:= One( f );
    m:= d / 2;
    xi:= Z(q);

    if m = 1 then
      # Omega(+1,2,q), Section 4.4
      g:= [ [[xi^2,0],[0,xi^-2]] ] * o;
    elif m = 2 then
      # Omega(+1,4,q), Section 4.3
      xi:= Z(q^2)^(q-1);
      a:= xi + xi^-1;
      g:= [ [[0,-1,0,-1],[1,a,-1,a],[0,0,0,1],[0,0,-1,a]],
            [[0,0,1,-1],[0,0,0,-1],[-1,-1,a,-a],[0,1,0,a]] ] * o;
    else
      # Omega(+1,d,q) for d=2m, Sections 4.1 and 4.2
      if d mod 4 = 2 then
        mo:= -o;  # (-1)^m
      else
        mo:= o;
      fi;

      n:= NullMat( d, d, f );
      n[ m+2 ][1]:= mo;
      n[ m-1 ][d]:= mo;
      n[m][ m+1 ]:= o;
      n[ m+1 ][m]:= o;
      for i in [ 1 .. m-2 ] do
        n[i][ i+1 ]:= o;
        n[ d+1-i ][ d-i ]:= o;
      od;

      if m mod 2 = 0 then
        x1:= IdentityMat( d, f );
        if q = 2 then
          x1[ m-1 ][ m+1 ]:= -o;
          x1[m][ m+2 ]:= o;
        else
          x1[ m+2 ][m]:= o;
          x1[ m+1 ][ m-1 ]:= -o;
        fi;
        x2:= IdentityMat( d, f );
        x2[ m-2 ][ m-1 ]:= o;
        x2[ m+2 ][ m+3 ]:= -o;
        x:= x1 * x2;
      else
        x:= IdentityMat( d, f );
        x[ m-1 ][ m+1 ]:= -o;
        x[m][ m+2 ]:= o;
      fi;

      if ( m mod 2 = 0 and q = 2 ) or ( m mod 2 = 1 and q <= 3 ) then
        # the matrices $x$ and $n$
        g:= [ x, n ];
      else
        # the matrices $h$ and $x n$
        h:= IdentityMat( d, f );
        h[ m-1 ][ m-1 ]:= xi;
        h[ m+2 ][ m+2 ]:= xi^-1;
        if m mod 2 = 0 then
          h[ m ][ m ]:= xi^-1;
          h[ m+1 ][ m+1 ]:= xi;
        else
          h[ m ][ m ]:= xi;
          h[ m+1 ][ m+1 ]:= xi^-1;
        fi;
        g:= [ h, x*n ];
      fi;
    fi;

    # construct the group without calling 'Group'
    g:= List( g, i -> ImmutableMatrix( f, i, true ) );
    g:= GroupWithGenerators( g );
    SetDimensionOfMatrixGroup( g, d );
    SetFieldOfMatrixGroup( g, f );

    # and set its size
    s  := 1;
    q2 := q^2;
    q2i:= 1;
    for i in [ 1 .. m-1 ] do
      q2i:= q2 * q2i;
      s  := s * (q2i-1);
    od;
    if q mod 2 = 1 then
      s:= s/2;
    fi;
    SetSize( g, q^(m*(m-1)) * (q^m-1) * s );

    # construct the forms
    x:= NullMat( d, d, f );
    for i in [ 1 .. m ] do
      x[i,d-i+1] := o;
    od;
    x:= ImmutableMatrix( f, x, true );
    SetInvariantQuadraticFormFromMatrix( g, x );

    # and return
    return g;
    end );


#############################################################################
##
#F  OmegaMinus( <d>, <q> )  . . . . . . . . . . . . . . . \Omega^-_{<d>}(<q>)
##
BindGlobal( "OmegaMinus", function( d, q )
    local f, o, m, xi, mo, nu, nubar, h, x, n, i, g, s,q2, q2i;

    # <d> must be even
    if d mod 2 = 1 then
      Error( "<d> must be even" );
    elif d < 4 then
      # The construction in the paper does not apply to the case d = 2
      Error( "<d> = 2 is not supported" );
    fi;
    f:= GF(q);
    o:= One( f );
    m:= d / 2 - 1;
    xi:= Z(q);

    if d mod 4 = 2 then
      mo:= -o;  # (-1)^(m-1)
    else
      mo:= o;
    fi;

    nu:= Z(q^2);
    nubar:= nu^q;

    h:= IdentityMat( d, f );
    h[m][m]:= nu * nubar;
    h[ m+3 ][ m+3 ]:= (nu * nubar)^-1;
    h{ [ m+1 .. m+2 ] }{ [ m+1 .. m+2 ] }:= [
        [-1,nu^-1 + nubar^-1],
        [-nu-nubar, 1 + nu*nubar^-1 + nu^-1*nubar]] * o;
    x:= IdentityMat( d, f );
    x{ [ m .. m+3 ] }{ [ m .. m+3 ] }:= [[1,1,0,1],[0,1,0,2],
                                         [0,0,1,nu+nubar],
                                         [0,0,0,1]] * o;

    n:= NullMat( d, d, f );
    n[ m+3 ][1]:= mo;
    n[m][d]:= mo;
    n[ m+1 ][ m+1 ]:= -o;
    n[ m+2 ][ m+1 ]:= -nu - nubar;
    n[ m+2 ][ m+2 ]:= o;
    for i in [ 1 .. m-1 ] do
      n[i][ i+1 ]:= o;
      n[ d+1-i ][ d-i ]:= o;
    od;

    g:= [ h, x*n ];

    # construct the group without calling 'Group'
    g:= List( g, i -> ImmutableMatrix( f, i, true ) );
    g:= GroupWithGenerators( g );
    SetDimensionOfMatrixGroup( g, d );
    SetFieldOfMatrixGroup( g, f );

    # and set its size
    m:= d/2;
    s  := 1;
    q2 := q^2;
    q2i:= 1;
    for i in [ 1 .. m-1 ] do
      q2i:= q2 * q2i;
      s  := s * (q2i-1);
    od;
    if q mod 2 = 1 then
      s:= s/2;
    fi;
    SetSize( g, q^(m*(m-1)) * (q^m+1) * s );

    # construct the forms
    x:= NullMat( d, d, f );
    for i in [ 1 .. m-1 ] do
      x[i,d-i+1] := o;
    od;
    x[m,d-m+1] := -nu - nubar;
    if q mod 2 = 1 then
      x[m,d-m] := -o;
      x[m+1,d-m+1] := xi^( (q+1)/2 );
    fi;
    x:= ImmutableMatrix( f, x, true );
    SetInvariantQuadraticFormFromMatrix( g, x );

    # and return
    return g;
    end );


#############################################################################
##
#M  OmegaCons( <filter>, <e>, <d>, <q> )  . . . . . . . . .  orthogonal group
##
InstallMethod( OmegaCons,
    "matrix group for <e>, dimension, and finite field size",
    [ IsMatrixGroup and IsFinite,
      IsInt,
      IsPosInt,
      IsPosInt ],
    function( filter, e, d, q )
    local g, i;

    # if <e> = 0  then <d> must be odd
    if e = 0 and d mod 2 = 0  then
        Error( "sign <e> = 0 but dimension <d> is even" );

    # if <e> <> 0  then <d> must be even
    elif e <> 0 and d mod 2 = 1  then
        Error( "sign <e> <> 0 but dimension <d> is odd" );
    fi;

    # construct the various orthogonal groups
    if   e = 0 then
      g:= OmegaZero( d, q );
    elif e = 1 then
      g:= OmegaPlus( d, q );
    elif e = -1 then
      g:= OmegaMinus( d, q );
    else
      Error( "sign <e> must be -1, 0, +1" );
    fi;

    # set name
    if e = +1  then i := "+";  else i := "";  fi;
    SetName( g, Concatenation( "Omega(", i, String(e), ",", String(d), ",",
                               String(q), ")" ) );

    # and return
    return g;
end );


#############################################################################
##
#M  Omega( [<filt>, ][<e>, ]<d>, <q> )
##
InstallMethod( Omega,
    [ IsPosInt, IsPosInt ],
    function( d, q )
    return OmegaCons( IsMatrixGroup, 0, d, q );
    end );

InstallMethod( Omega,
    [ IsInt, IsPosInt, IsPosInt ],
    function( e, d, q )
    return OmegaCons( IsMatrixGroup, e, d, q );
    end );

InstallMethod( Omega,
    [ IsFunction, IsPosInt, IsPosInt ],
    function( filt, d, q )
    return OmegaCons( filt, 0, d, q );
    end );

InstallMethod( Omega,
    [ IsFunction, IsInt, IsPosInt, IsPosInt ],
    OmegaCons );


#############################################################################
##
#F  WreathProductOfMatrixGroup( <M>, <P> )  . . . . . . . . .  wreath product
##
BindGlobal( "WreathProductOfMatrixGroup", function( M, P )
    local   m,  d,  id,  gens,  b,  ran,  raN,  mat,  gen,  G;

    m := DimensionOfMatrixGroup( M );
    d := LargestMovedPoint( P );
    id := IdentityMat( m * d, DefaultFieldOfMatrixGroup( M ) );
    gens := [  ];
    for b  in [ 1 .. d ]  do
        ran := ( b - 1 ) * m + [ 1 .. m ];
        for mat  in GeneratorsOfGroup( M )  do
            gen := StructuralCopy( id );
            gen{ ran }{ ran } := mat;
            Add( gens, gen );
        od;
    od;
    for gen  in GeneratorsOfGroup( P )  do
        mat := StructuralCopy( id );
        for b  in [ 1 .. d ]  do
            ran := ( b - 1 ) * m + [ 1 .. m ];
            raN := ( b^gen - 1 ) * m + [ 1 .. m ];
            mat{ ran } := id{ raN };
        od;
        Add( gens, mat );
    od;
    G := GroupWithGenerators( gens );
    if HasName( M )  and  HasName( P )  then
        SetName( G, Concatenation( Name( M ), " wr ", Name( P ) ) );
    fi;
    return G;
end );


# Permutation constructors by using `IsomorphismPermGroup'
PermConstructor(GeneralLinearGroupCons,[IsPermGroup,IsInt,IsObject],
  IsMatrixGroup and IsFinite);
PermConstructor(GeneralOrthogonalGroupCons,[IsPermGroup,IsInt,IsInt,IsObject],
  IsMatrixGroup and IsFinite);
PermConstructor(GeneralUnitaryGroupCons,[IsPermGroup,IsInt,IsObject],
  IsMatrixGroup and IsFinite);

PermConstructor(SpecialLinearGroupCons,[IsPermGroup,IsInt,IsObject],
  IsMatrixGroup and IsFinite);
PermConstructor(SpecialOrthogonalGroupCons,[IsPermGroup,IsInt,IsInt,IsObject],
  IsMatrixGroup and IsFinite);
PermConstructor(SpecialUnitaryGroupCons,[IsPermGroup,IsInt,IsObject],
  IsMatrixGroup and IsFinite);

PermConstructor(SymplecticGroupCons,[IsPermGroup,IsInt,IsObject],
  IsMatrixGroup and IsFinite);

PermConstructor(OmegaCons,[IsPermGroup,IsInt,IsObject],
  IsMatrixGroup and IsFinite);


#############################################################################
##
#E

