#############################################################################
####
##
#A  anusp.gi                    ANUPQ package                  Eamonn O'Brien
#A                                                             Alice Niemeyer 
##
#Y  Copyright 1993-2001,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
#Y  Copyright 1993-2001,  School of Mathematical Sciences, ANU,     Australia
##

#############################################################################
##
#F  ANUPQSPerror( <param> )  . . . . . . . . . . . . report illegal parameter
##
InstallGlobalFunction( ANUPQSPerror, function( param )
    Error(
    "Valid Options:\n",
    "    \"ClassBound\", <bound>\n",
    "    \"PcgsAutomorphisms\"\n",
    "    \"Exponent\", <exponent>\n",
    "    \"Metabelian\"\n",
    "    \"OutputLevel\", <level>\n",
    "    \"SetupFile\", <file>\n",
    "Illegal Parameter: \"", param, "\"" );
end );

#############################################################################
##
#F  ANUPQSPextractArgs( <args> )  . . . . . . . . . . . . parse argument list
##
InstallGlobalFunction( ANUPQSPextractArgs, function( args )
    local   CR,  i,  act,  G,  match;

    # allow to give only a prefix
    match := function( g, w )
    	return 1 < Length(g) and 
            Length(g) <= Length(w) and 
            w{[1..Length(g)]} = g;
    end;

    # extract arguments
    G  := args[2];
    CR := rec( group := G );
    i  := 3;
    while i <= Length(args)  do
        act := args[i];

        # "ClassBound", <class>
        if match( act, "ClassBound" )  then
            i := i + 1;
            CR.ClassBound := args[i];
            if CR.ClassBound <= PClassPGroup(G)  then
                Error( "\"ClassBound\" must be at least ", PClassPGroup(G)+1 );
            fi;

        # "PcgsAutomorphisms"
        elif match( act, "PcgsAutomorphisms" )  then
            CR.PcgsAutomorphisms := true;

        #this may be available later
        # "SpaceEfficient"
        #elif match( act, "SpaceEfficient" ) then
        #    CR.SpaceEfficient := true;

        # "Exponent", <exp>
        elif match( act, "Exponent" )  then
            i := i + 1;
            CR.Exponent := args[i];

        # "Metabelian"
        elif match( act, "Metabelian" ) then
            CR.Metabelian := true;

        # "Verbose"
        elif match( act, "Verbose" )  then
            CR.Verbose := true;

        # "SetupFile", <file>
        elif match( act, "SetupFile" )  then
            i := i + 1;
            CR.SetupFile := args[i];

    	# "TmpDir", <dir>
    	elif match( act, "TmpDir" )  then
    	    i := i + 1;
    	    CR.TmpDir := args[i];

        # "Output", <level>
        elif match( act, "OutputLevel" )  then
            i := i + 1;
            CR.OutputLevel := args[i];
            CR.Verbose     := true;

        # signal an error
        else
            ANUPQSPerror(act);
        fi;
        i := i + 1;
    od;
    return CR;

end );

#############################################################################
##
#F  PqFpGroupPcGroup( <G> ) . . . . . .  corresponding fp group of a pc group
##
InstallGlobalFunction( PqFpGroupPcGroup, 
    G -> Image( IsomorphismFpGroup( G ) )
);

#############################################################################
##
#M  FpGroupPcGroup( <G> ) . . . . . . .  corresponding fp group of a pc group
##
InstallMethod( FpGroupPcGroup, "pc group", [IsPcGroup], 0, PqFpGroupPcGroup );

#############################################################################
##
#F  PQ_EPIMORPHISM_STANDARD_PRESENTATION( <args> ) . (epi. onto) SP for group
##
InstallGlobalFunction( PQ_EPIMORPHISM_STANDARD_PRESENTATION, 
function( args )
    local   datarec, rank, Q, Qclass, automorphisms, generators, x,
            images, i, r, j, aut, result, desc, k;

    datarec := ANUPQ_ARG_CHK("StandardPresentation", args);

    if datarec.calltype = "interactive" and IsBound(datarec.SPepi) then
       # Note: the `pq' binary seg-faults if called twice to 
       # calculate the standard presentation of a group
      return datarec.SPepi;
    fi;

    if VALUE_PQ_OPTION("pQuotient") = fail and
       VALUE_PQ_OPTION("Prime", datarec) <> fail then
       # Ensure a saved value of `Prime' has precedence
       # over a saved value of `pQuotient'.
        Unbind(datarec.pQuotient);
    fi;

    if VALUE_PQ_OPTION("pQuotient", datarec) <> fail then
        PQ_AUT_GROUP( datarec.pQuotient );
        datarec.Prime := PrimePGroup( datarec.pQuotient );
    elif VALUE_PQ_OPTION("Prime", datarec) <> fail then
        rank := Number( List( AbelianInvariants(datarec.group), 
                              x -> Gcd(x, datarec.Prime) ),
                        y -> y = datarec.Prime );

        # construct free group with <rank> generators
        Q := FreeGroup( IsSyllableWordsFamily, rank, "q" );
    
        # construct power-relation
        Q := Q / List( GeneratorsOfGroup(Q), x -> x^datarec.Prime );
    
        # construct pc group
        Q := PcGroupFpGroup(Q);
    
        # construct automorphism
        automorphisms := [];
        generators := GeneratorsOfGroup(Q);
        for x in GeneratorsOfGroup( GL(rank, datarec.Prime) ) do
            images := [];
            for i  in [ 1 .. rank ]  do
                r := One(Q);
                for j  in [ 1 .. rank ]  do
                    r := r * generators[j]^Int(x[i][j]);
                od;
                images[i] := r;
            od;
            aut := GroupHomomorphismByImages( Q, Q, generators, images );
            SetIsBijective( aut, true );
            Add( automorphisms, aut );
        od;
        SetAutomorphismGroup( Q, GroupByGenerators( automorphisms ) );
        datarec.pQuotient := Q;
    fi;
    
    #PushOptions(rec(nonuser := true));
    Qclass := PClassPGroup( datarec.pQuotient );
    if VALUE_PQ_OPTION("ClassBound", 63) <= Qclass then
        Error( "option `ClassBound' must be greater than `pQuotient' class (",
               Qclass, ")\n" );
    fi;
    PQ_PC_PRESENTATION(datarec, "SP" : ClassBound := Qclass);

    PQ_SP_STANDARD_PRESENTATION(datarec);

    PQ_SP_ISOMORPHISM(datarec);

    if datarec.calltype = "non-interactive" then
        PQ_COMPLETE_NONINTERACTIVE_FUNC_CALL(datarec);
        if IsBound( datarec.setupfile ) then
            #PopOptions();
            return true;
        fi;
    fi;

    # try to read output
    result := ANUPQReadOutput( ANUPQData.SPimages );

    if not IsBound(result.ANUPQmagic)  then
        Error("something wrong with `pq' binary. Please check installation\n");
    fi;

    desc := rec();
    result.ANUPQgroups[Length(result.ANUPQgroups)](desc);
#    if result.ANUPQautos <> fail and 
#       Length( result.ANUPQautos ) = Length( result.ANUPQgroups ) then
#    	result.ANUPQautos[ Length(result.ANUPQgroups) ]( desc.group );
#    fi;

    # revise images to correspond to images of user-supplied generators 
    datarec.SP := desc.group;
    x  := Length( desc.map );
    k  := Length( GeneratorsOfGroup( datarec.group ) );
    # images of user supplied generators are last k entries in .pqImages 

    datarec.SPepi := GroupHomomorphismByImagesNC( 
                         datarec.group, 
                         datarec.SP, 
                         GeneratorsOfGroup(datarec.group),
                         desc.map{[x - k + 1..x]} );
    #PopOptions();
    return datarec.SPepi;
end );

#############################################################################
##
#F  EpimorphismPqStandardPresentation( <arg> ) . . . epi. onto SP for p-group
##
InstallGlobalFunction( EpimorphismPqStandardPresentation, function( arg )
    return PQ_EPIMORPHISM_STANDARD_PRESENTATION( arg );
end );

#############################################################################
##
#F  PqStandardPresentation( <arg> : <options> ) . . . . . . .  SP for p-group
##
InstallGlobalFunction( PqStandardPresentation, function( arg )
    local SPepi;

    SPepi := PQ_EPIMORPHISM_STANDARD_PRESENTATION( arg );
    if SPepi = true then
      return true; # the SetupFile case
    fi;
    return Range( SPepi );
end );

#############################################################################
##
#M  EpimorphismStandardPresentation( <F> ) . . . . . epi. onto SP for p-group
#M  EpimorphismStandardPresentation( [<i>] )
##
InstallMethod( EpimorphismStandardPresentation, 
               "fp group", [IsFpGroup], 0,
               EpimorphismPqStandardPresentation );

InstallMethod( EpimorphismStandardPresentation, 
               "pc group", [IsPcGroup], 0,
               EpimorphismPqStandardPresentation );

InstallMethod( EpimorphismStandardPresentation, 
               "positive integer", [IsPosInt], 0,
               EpimorphismPqStandardPresentation );

InstallOtherMethod( EpimorphismStandardPresentation,
                    "", [], 0,
                    EpimorphismPqStandardPresentation );

#############################################################################
##
#M  StandardPresentation( <F> ) . . . . . . . . . . . . . . .  SP for p-group
#M  StandardPresentation( [<i>] )
##
InstallMethod( StandardPresentation, 
               "fp group", [IsFpGroup], 0,
               PqStandardPresentation );

InstallMethod( StandardPresentation, 
               "pc group", [IsPcGroup], 0,
               PqStandardPresentation );

InstallMethod( StandardPresentation, 
               "positive integer", [IsPosInt], 0,
               PqStandardPresentation );

InstallOtherMethod( StandardPresentation,
                    "", [], 0,
                    PqStandardPresentation );

#############################################################################
##
#F  IsPqIsomorphicPGroup( <G>, <H> )  . . . . . . . . . . .  isomorphism test
##
InstallGlobalFunction( IsPqIsomorphicPGroup, function( G, H )
    local   p,  class,  SG,  SH,  Ggens,  Hgens;
    
    # <G> and <H> must both be pc groups and p-groups
    if not IsPcGroup(G)  then
        Error( "<G> must be a pc group" );
    fi;
    if not IsPcGroup(H)  then
        Error( "<H> must be a pc group" );
    fi;
    if Size(G) <> Size(H)  then
        return false;
    fi;
    p := SmallestRootInt(Size(G));
    if not IsPrimeInt(p)  then
        Error( "<G> must be a p-group" );
    fi;
    
    # check the Frattini factor
    if RankPGroup(G) <> RankPGroup(H)  then
        return false;
    fi;

    # check the exponent-p length and the sizes of the groups in the
    # p-central series of both groups 
    if List(PCentralSeries(G,p), Size) <> List(PCentralSeries(H,p), Size) then
        return false;
    fi;

    # if the groups are elementary abelian they are isomorphic
    class := PClassPGroup(G);
    if class = 1  then
        return true;
    fi;
    
    # compute a standard presentation for both
    SG := PqStandardPresentation(PqFpGroupPcGroup(G)
                                 : Prime := p, ClassBound := class);
    SH := PqStandardPresentation(PqFpGroupPcGroup(H)
                                 : Prime := p, ClassBound := class);
    
    # the groups are equal if the presentation are equal
    Ggens := GeneratorsOfGroup( FreeGroupOfFpGroup( SG ) );
    Hgens := GeneratorsOfGroup( FreeGroupOfFpGroup( SH ) );
    return RelatorsOfFpGroup(SG)
           = List( RelatorsOfFpGroup(SH), 
                   x -> MappedWord( x, Hgens, Ggens ) );
    
end );

#############################################################################
##
#M  IsIsomorphicPGroup( <F>, <G> )
##
InstallMethod( IsIsomorphicPGroup, "pc group, pc group",
               [IsPcGroup, IsPcGroup], 0,
               IsPqIsomorphicPGroup );

#E  anusp.gi  . . . . . . . . . . . . . . . . . . . . . . . . . . . ends here
