#############################################################################
##
#W  finiteness.gi                   NilMat                       Alla Detinko
#W                                                               Bettina Eick
#W                                                              Dane Flannery
##
##
## This file contains methods for check whether a given f.g. nilpotent
## matrix group over Q is finite. Further, it contains methods to determine
## the order of a f.g. nilpotent matrix group over GF(q) or Q.
##
#############################################################################
##
#F IsFiniteNilpotentMatGroup( G )   . . . . . . . . . . . . decide finiteness
##
## This function takes a f.g. nilpotent matrix group G over Q and tests
## whether it is finite. The test uses results of the nilpotency test
## IsNilpotentMatGroup and hence it is particularly fast, if this test
## has been used for checking the nilpotence of G.
##
## Note that this function does not check whether G is nilpotent and it
## may return wrong results if it is not.
##
InstallGlobalFunction( IsFiniteNilpotentMatGroup, function(G)
    local n, g, d, a, p, t, pcgs, kern, F;

    # the trivial case
    F := FieldOfMatrixGroup(G);
    if IsFinite(F) then return true; fi;
    if ForAll(GeneratorsOfGroup(G), x -> x = One(G)) then return true; fi;

    # set up
    g := GeneratorsOfGroup(G);
    d := List(g, JordanDecomposition);
    n := DimensionOfMatrixGroup(G);
    a := NullMat(n,n,Rationals);

    # check whether G contains unipotent part
    if ForAny(d, x -> not x[2] = a) then return false; fi;

    # compute kernel of congruence hom
    p := DetermineAdmissiblePrime(g);
    t := InducedByField(g, GF(p));
    pcgs := CPCS_finite_word( t, n+2 );
    kern := POL_NormalSubgroupGeneratorsOfK_p( pcgs, g );

    # check whether kernel is trivial
    kern := Filtered(kern, x -> not x = One(G));
    return (Length(kern) = 0);
end );

InstallMethod( IsFinite, [IsCyclotomicMatrixGroup and IsNilpotentGroup],
function(G)
    if IsRationalMatrixGroup(G) then return IsFiniteNilpotentMatGroup(G); fi;
    TryNextMethod();
end );

###############################################################################
##
#F SizeByKnownClass(G). . . . . . . .determine the order by the nilpotency class
##
## This function takes a nilpotent matrix group over GF(q) generated by
## semisimple matrices and determines its order.

SizeByKnownClass := function(G)
    local n, F, l, C, s, c, a;

    n := DimensionOfMatrixGroup(G);
    F := FieldOfMatrixGroup(G);
    l := ClassLimit(n, F);
    C := G;
    s := 1;

    while not IsAbelian(C) do
        a := SecondCentralElement(G,C,l);
        if IsBool(a) then return fail; fi;
        C := OrbitStabGens(C, a);
        s := s * C!.index;
    od;

    return s * Size(C);
end;


#############################################################################
##
#F SizeOfNilpotentMatGroupFF( G ) . . . . . . . . . . . . determine the order
##
## This function takes a nilpotent matrix group G over GF(q) and determines
## its order. The function uses results of the nilpotency test
## IsNilpotentMatGroupFF and hence it is particularly fast, if this test
## has been used for checking the nilpotence of G.
##
## Note that this function does not check whether G is nilpotent and it
## may return wrong results if it is not.
##
## The calls to 'Size' below may need some further refinement and improve-
## ments.

SizeOfNilpotentMatGroupFF := function(G)
    local J, S, U, P, B, C, syl;

    # catch a trivial case
    if ForAll(GeneratorsOfGroup(G), x -> x = One(G)) then
        return 1;
    elif Length(Set(GeneratorsOfGroup(G))) = 1 then
        return Order(GeneratorsOfGroup(G)[1]);
    fi;

    # get available info from nilpotency testing
    J := JordanSplitting(G);
    S := J[1];
    U := J[2];

    P := PiPrimarySplitting(S);
    B := P[1];
    C := P[2];

    # now G = B x C x U
    if IsAbelian(B) then
        return Size(U) * Size(C) * Size(B);
    else
        # in this case a Sylow system for B is known from nilpotency testing
        syl := SylowSubgroupsOfNilpotentFFMatGroup(B);
        return Size(U) * Size(C) * Product(List(syl, SizeByKnownClass));
    fi;
end;

#############################################################################
##
#F SizeOfNilpotentMatGroupRN( G ) . . . . . . . . . . . . determine the order
##
## This function takes a nilpotent matrix group G over Q and determines its
## order.
##
## Note that this function does not check whether G is nilpotent and it may
## return wrong results if it is not.
##
SizeOfNilpotentMatGroupRN := function(G)
   local g, p, t, H;
   g := GeneratorsOfGroup(G);
   p := DetermineAdmissiblePrime(g);
   t := InducedByField(g, GF(p));
   H := MakeMatGroup(DimensionOfMatrixGroup(G), GF(p), t);
   return SizeOfNilpotentMatGroupFF(H);
end;

#############################################################################
##
#F SizeOfNilpotentMatGroup( G ) . . . . . . . . . . . . . determine the order
##
InstallGlobalFunction( SizeOfNilpotentMatGroup, function(G)
    if IsFFEMatrixGroup(G) then
        return SizeOfNilpotentMatGroupFF(G);
    fi;
    if IsCyclotomicMatrixGroup(G) and IsRationalMatrixGroup(G) then
        return SizeOfNilpotentMatGroupRN(G);
    fi;
    return fail;
end );

##
## we cannot install a similar method to finite field groups, as the
## algorithm for those groups uses a call to 'Size' for some smaller
## subgroups.
##

InstallMethod( Size, true, [IsCyclotomicMatrixGroup and IsNilpotentGroup], 0,
function(G)
   if IsRationalMatrixGroup(G) then
       return SizeOfNilpotentMatGroupRN(G);
   fi;
   TryNextMethod();
end);
