#############################################################################
##
#W  lag.gi                   GAP library                Richard Rossmanith
##
##
##
##
#Y  Copyright (C)  1997,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
#Y  (C) 1998 School Math and Comp. Sci., University of St.  Andrews, Scotland
##
##  This file contains the methods applicable to associated Lie algebras of
##  associative algebras, in particular group algebras.
##


#############################################################################
##
##  The following makes this version of Lag compatible both with 4.1, and 4.2:
##
if VERSION{[1,2,3]} = "4r1" or VERSION{[1,2,3]} = "4.1" then
    IsLieObjectsModule:= IsLieObjectsModuleRep;
fi;

#############################################################################
##
#M  LieAlgebraByDomain( <A> )
##  

InstallMethod( LieAlgebraByDomain,
#+  This method takes an associative algebra as argument, and constructs
#+  its associated Lie algebra.
#+  The user, however, will {\bf never use} this command, but will rather use
#+  LieAlgebra( <A> ), which either returns the Lie algebra in case
#+  it is already constructed, or refers to LieAlgebraByDomain in case 
#+  it is not.

    "for an associative algebra",
    true,
    [ IsAlgebra and IsAssociative ], 0,
    function( A )

       local fam,L;

       fam:= LieFamily( ElementsFamily( FamilyObj( A ) ) );

       L:= Objectify( NewType( CollectionsFamily( fam ) ,
                        IsLieAlgebraByAssociativeAlgebra and 
                        IsLieObjectsModule and
                        IsAttributeStoringRep ),
                   rec() );

       # Set the necessary attributes.
       SetLeftActingDomain( L, LeftActingDomain( A ) );
       SetUnderlyingAssociativeAlgebra( L, A );
       
       # Test for inherited properties from A and set them where appropriate:
       
       if HasIsGroupRing(A) then
         SetIsLieAlgebraOfGroupRing(L, IsGroupRing(A));
       fi;

       if HasIsFiniteDimensional(A) then 
         SetIsFiniteDimensional(L, IsFiniteDimensional(A));
       fi;

       if HasDimension(A) then 
         SetDimension(L, Dimension(A));
       fi;

       if HasIsFinite(A) then 
         SetDimension(L, IsFinite(A));
       fi;

       if HasSize(A) then 
         SetDimension(L, Size(A));
       fi;
     
       return L;
  end );


InstallMethod( \in,
  "for a Lie algebra that comes from an associative algebra and a Lie object",
    true,
    [ IsLieObject, IsLieAlgebra and IsLieAlgebraByAssociativeAlgebra ], 0,
    function( x, L )

      return x![1] in UnderlyingAssociativeAlgebra( L ); 

end );


InstallMethod( GeneratorsOfLeftOperatorRing,
    "for a Lie algebra coming from an associative algebra",
    true,
    [ IsLieAlgebra and IsLieAlgebraByAssociativeAlgebra ], 0,
    function( L )

       return List( BasisVectors( Basis( UnderlyingAssociativeAlgebra( L ) ) ),
                       LieObject );
end); 


InstallMethod( IsFiniteDimensional,
    "for a Lie algebra coming from an associative algebra",
    true,
    [ IsLieAlgebra and IsLieAlgebraByAssociativeAlgebra ], 0,
    L -> IsFiniteDimensional( UnderlyingAssociativeAlgebra( L ) ) );
      

InstallMethod( Dimension,
    "for a Lie algebra coming from an associative algebra",
    true,
    [ IsLieAlgebra and IsLieAlgebraByAssociativeAlgebra ], 0,
    L -> Dimension ( UnderlyingAssociativeAlgebra( L ) ) );
      

InstallMethod( IsFinite,
    "for a Lie algebra coming from an associative algebra",
    true,
    [ IsLieAlgebra and IsLieAlgebraByAssociativeAlgebra ], 0,
    L -> IsFinite ( UnderlyingAssociativeAlgebra( L ) ) );
      

InstallMethod( Size,
    "for a Lie algebra coming from an associative algebra",
    true,
    [ IsLieAlgebra and IsLieAlgebraByAssociativeAlgebra ], 0,
    L -> Size ( UnderlyingAssociativeAlgebra( L ) ) );
      

InstallMethod( AsSSortedList,
    "for a Lie algebra coming from an associative algebra",
    true,
    [ IsLieAlgebra and IsLieAlgebraByAssociativeAlgebra ], 0,
    L -> List(AsSSortedList(UnderlyingAssociativeAlgebra(L)), LieObject)
);
      

InstallMethod( Representative,
    "for a Lie algebra coming from an associative algebra",
    true,
    [ IsLieAlgebra and IsLieAlgebraByAssociativeAlgebra ], 0,
    L -> LieObject(Representative(UnderlyingAssociativeAlgebra( L ) ) ) );


InstallMethod( Random,
    "for a Lie algebra coming from an associative algebra",
    true,
    [ IsLieAlgebra and IsLieAlgebraByAssociativeAlgebra ], 0,
    L -> LieObject(Random(UnderlyingAssociativeAlgebra( L ) ) ) );


#############################################################################
##
#M  CanonicalBasis( <L> )
##  

InstallMethod( CanonicalBasis,
#+  This method transfers the canonical basis of an associative algebra
#+  to its associated Lie algebra $L$.
    "for a Lie algebra coming from an associative algebra",
    true,
    [ IsLieAlgebraByAssociativeAlgebra ], 0,
    function(L)
      local A,B,t;
      
      A:=UnderlyingAssociativeAlgebra(L);
      t:=NaturalBijectionToLieAlgebra(A);
      B:=BasisNC(L, List(AsSSortedList(CanonicalBasis(A)), x -> x^t));
      SetIsCanonicalBasis(B, true);
      return B;
    end);


#############################################################################
##
#M  CanonicalBasis( <L> )
##  

InstallMethod( CanonicalBasis,
#+  This method directly computes the canonical basis of the Lie algebra
#+  of a group algebra without referring to the group algebra, i.e. by
#+  sending the group elements directly to the Lie algebra.
    "for a Lie algebra of a group algebra",
    true,
    [ IsLieAlgebraByAssociativeAlgebra and IsLieAlgebraOfGroupRing ], 0,
    function(L)
      local G,t,B;
      
      G:=UnderlyingGroup(L);
      t:=NaturalMapping(G,L);
      B:=BasisNC(L, List(AsSSortedList(G), x -> x^t));
      SetIsCanonicalBasis(B, true);
      SetIsBasisOfLieAlgebraOfGroupRing(B, true);
      return B;
    end);


#############################################################################
##
#M  StructureConstantsTable( <B> )
##  

InstallMethod( StructureConstantsTable,
#+  A very fast implementation for calculating the structure constants
#+  of a Lie algebra of a group ring w.r.t. its canonical basis $B$ by
#+  using the special structure of $B$.
   "for a basis of a Lie algebra of a group algebra",
    true,
    [ IsBasis and IsCanonicalBasis and IsBasisOfLieAlgebraOfGroupRing ], 0,
    function(B)
      local L,F,e,o,G,n,X,T,i,j,g,h;
      L:=UnderlyingLeftModule(B);
      F:=LeftActingDomain(L);
      e:=One(F);
      o:=Zero(F);
      G:=UnderlyingGroup(L);
      n:=Size(G);
      X:=AsSSortedList(G);
      T:=EmptySCTable(n,o,"antisymmetric");
      for i in [1..n-1] do
        for j in [i+1..n] do
          g:=X[i]*X[j];
          h:=X[j]*X[i];
          if g<>h then
            SetEntrySCTable(T,i,j,[e,Position(X,g),-e,Position(X,h)]);
          fi;
        od;
      od;
      return T;
    end);


InstallMethod( UnderlyingGroup,
    "for a Lie algebra of a group ring",
    true,
    [ IsLieAlgebraByAssociativeAlgebra and IsLieAlgebraOfGroupRing ], 0,
    L -> UnderlyingMagma( UnderlyingAssociativeAlgebra( L ) ) );


InstallMethod( NaturalBijectionToLieAlgebra,
    "for an associative algebra",
    true,
    [ IsAlgebra and IsAssociative ], 0,
    function( A )
      local map;

      map:= MappingByFunction( A,  LieAlgebra( A ),
                               LieObject,
                               y -> y![1] );
      SetIsLeftModuleGeneralMapping( map, true );

      return map;
    end );


InstallMethod( NaturalBijectionToAssociativeAlgebra,
    "for a Lie algebra",
    true,
    [ IsLieAlgebraByAssociativeAlgebra ], 0,
    L->InverseGeneralMapping(NaturalBijectionToLieAlgebra(UnderlyingAssociativeAlgebra(L)))
);


InstallMethod( IsLieAlgebraOfGroupRing,
    "for a Lie algebra",
    true,
    [ IsLieAlgebraByAssociativeAlgebra ], 0,
    L->IsGroupRing(UnderlyingAssociativeAlgebra(L))
);


InstallMethod( NaturalMapping,
    "from a group to the Lie algebra of the group ring",
    true,
    [ IsMagma,
      IsLieAlgebraByAssociativeAlgebra and IsLieAlgebraOfGroupRing ], 0,
    function( U, L )
      local A;
      A:=UnderlyingAssociativeAlgebra(L);
      return Embedding(U,A)*NaturalBijectionToLieAlgebra(A);
    end
);


InstallMethod( AugmentationHomomorphism,
    "for a group ring",
    true,
    [ IsAlgebraWithOne and IsGroupRing ], 0,
    function(FG)
      local G,X,F,e;
      G:=UnderlyingMagma(FG);
      X:=GeneratorsOfMagmaWithOne(G);
      F:=LeftActingDomain(FG);
      e:=Embedding(G,FG);
      return AlgebraHomomorphismByImages(
               FG,
               F,
               List(X, x -> x^e),
               ListWithIdenticalEntries(Length(X), One(F))
             );
      end);


#############################################################################
##
#M  LieDerivedSubalgebra( <L> )
##  

InstallMethod( LieDerivedSubalgebra,
#+  The (Lie) derived subalgebra of a Lie algebra of a group ring can
#+  be calculated very fast by considering the conjugacy classes of the group.
#+  
#+  Note that he prefix `Lie' is consistently used to
#+  distinguish properties of Lie algebras from the analogous properties
#+  of groups (or of general algebras). Not using this prefix may
#+  result in error messages, or even in wrong results without warning.
    "for a Lie algebra of a group ring",
    true,
    [ IsLieAlgebraByAssociativeAlgebra and IsLieAlgebraOfGroupRing ], 0,
    function(L)
      local G, CC, t, B, C, s, K, j, i, x;
      G:=UnderlyingGroup(L);
      CC:=ConjugacyClasses(G);
      t:=NaturalMapping(G,L);
      B:=[];                # This list will collect the basis elements
      for C in CC do
        s:=Size(C);
        if s>1 then         # no need to consider central elements
          K:=AsSSortedList(C);   # Need to have all elements of C
          x:=K[s]^t;        # shift last element in C into Lie algebra L
          j:=Length(B);     # remember length to add more elements
          for i in [1..s-1] do   # commutators in L are just differences of
            B[j+i]:= K[i]^t - x; # conjugate elements of G (shifted into
          od;                    # the Lie algebra L via the map t). (The
        fi;                      # construction mimicks a matrix with 1's on
      od;                        # the diag and -1's in the last column.)
      return Subalgebra(L, B, "basis");
    end);


#############################################################################
##
#M  LieCentre( <L> )
##  

InstallMethod( LieCentre,
#+  The (Lie) centre of a Lie algebra of a group ring corresponds to the
#+  centre of the underlying group ring, and it can
#+  be calculated very fast by considering the conjugacy classes
#+  of the group.
#+  Since the corresponding method for the centre of the group ring
#+  does just that, it is being referred to by the method at hand.
#+  
#+  Note that he prefix `Lie' is consistently used to
#+  distinguish properties of Lie algebras from the analogous properties
#+  of groups (or of general algebras). Not using this prefix may
#+  result in error messages, or even in wrong results without warning.
#+  This is particularly important for the command LieCentre.
    "for a Lie algebra of a group ring",
    true,
    [ IsLieAlgebraByAssociativeAlgebra and IsLieAlgebraOfGroupRing ], 0,
    function(L)
      local A;
      A:=UnderlyingAssociativeAlgebra(L);
      return Subalgebra(L,
                        ImagesSet(NaturalBijectionToLieAlgebra(A),
                                  Basis(Centre(A))
                                 ),
                        "basis"
                       );
    end);


#############################################################################
##
#M  IsLieAbelian( <L> )
##  

InstallMethod( IsLieAbelian,
#+  The Lie algebra $L$ of an associative algebra  $A$ is Lie abelian,
#+  if and only if $A$ is abelian, so this method refers
#+  to IsAbelian( <A> ).
#+  
#+  Note that he prefix `Lie' is consistently used to
#+  distinguish properties of Lie algebras from the analogous properties
#+  of groups (or of general algebras). Not using this prefix may
#+  result in error messages, or even in wrong results without warning.
#+  This is particularly important for the command IsLieAbelian.
    "for a Lie algebra of an associative algebra",
    true,
    [ IsLieAlgebraByAssociativeAlgebra ], 0,
    L -> IsAbelian( UnderlyingAssociativeAlgebra( L ) ) );


#############################################################################
##
#M  IsLieSolvable( <L> )
##  

InstallMethod( IsLieSolvable,
#+  In `Lie solvable group rings', Canad. J. Math. 25, No. 4 (1973), 748-757, 
#+  Passi-Passman-Sehgal have classified all groups $G$ such that the
#+  associated
#+  Lie algebra $L$ of the group ring is (Lie) solvable. This method uses
#+  their classification, making it considerably faster than
#+  the more elementary method which just calculates Lie commutators.
#+  
#+  Note that he prefix `Lie' is consistently used to
#+  distinguish properties of Lie algebras from the analogous properties
#+  of groups (or of general algebras). Not using this prefix may
#+  result in error messages, or even in wrong results without warning.
    "for a Lie algebra of a group ring",
    true,
    [ IsLieAlgebraByAssociativeAlgebra and IsLieAlgebraOfGroupRing ], 0,
    
    function(L)
      local p,s;
      p:=Characteristic(L);
      if p=0 then
        return IsLieAbelian(L);
      fi;
      s:= (   [ p ]
            = Set(Factors(Size(DerivedSubgroup(UnderlyingGroup(L)))))
          );
      if s or p>2 then
        return s;
      else
        return ForAny(SubgroupsOfIndexTwo(UnderlyingGroup(L)),
                      M -> [2]=Set(Factors(Size(DerivedSubgroup(M))))
                     );
      fi;
    end);


InstallMethod( SubgroupsOfIndexTwo,
    "for a group",
    true,
    [ IsGroup ], 0,
    function(G)
      local D,i,f,A,subs;
      D:=DerivedSubgroup(G);
      i:=Index(G,D);
      if i=2 then
        return [D];
      elif 0=(i mod 2) then
        f:=NaturalHomomorphismByNormalSubgroup(G,D);
        A:=ImagesSource(f);
        SetIsAbelian(A, true);
        subs:=Filtered(MaximalSubgroups(A), M->(2=Index(A,M)));
        return List
                 (
                   subs,
                   M->Subgroup
                        ( G,
                          PreImagesSet( f,
                                        GeneratorsOfMagmaWithInverses(M)
                                      )
                        )
                 );
      else
        return [];
      fi;
    end);


InstallMethod( SubgroupsOfIndexTwo,
    "for a group",
    true,
    [ IsGroup and HasMaximalSubgroupClassReps ], 0,
    G -> Filtered(MaximalSubgroupClassReps(G), M->(2=Index(G,M))));


InstallMethod( SubgroupsOfIndexTwo,
    "for a group",
    true,
    [ IsGroup and HasMaximalNormalSubgroups ], 0,
    G -> Filtered(MaximalNormalSubgroups(G), M->(2=Index(G,M))));


#############################################################################
##
#M  IsLieNilpotent( <L> )
##  

InstallMethod( IsLieNilpotent,
#+  In `Lie solvable group rings', Canad. J. Math. 25, No. 4 (1973), 748-757, 
#+  Passi-Passman-Sehgal have classified all groups $G$ such that the
#+  associated
#+  Lie algebra $L$ of the group ring is (Lie) nilpotent. This method uses
#+  their classification, making it considerably faster than
#+  the more elementary method which just calculates Lie commutators.
#+  
#+  Note that he prefix `Lie' is consistently used to
#+  distinguish properties of Lie algebras from the analogous properties
#+  of groups (or of general algebras). Not using this prefix may
#+  result in error messages, or even in wrong results without warning.
    "for a Lie algebra of a group ring",
    true,
    [ IsLieAlgebraByAssociativeAlgebra and IsLieAlgebraOfGroupRing ], 0,
    
    function(L)
      local p,G;
      p:=Characteristic(L);
      if p=0 then
        return IsLieAbelian(L);
      fi;
      G:=UnderlyingGroup(L);
      return
        [p]=Set(Factors(Size(DerivedSubgroup(G))))
        and IsNilpotent(G);
    end);


InstallMethod( IsLieMetabelian,
    "for a Lie algebra",
    true,
    [ IsAlgebra and IsLieAlgebra], 0,
    L->(0=Dimension(LieDerivedSubalgebra(LieDerivedSubalgebra(L))))
);


#############################################################################
##
#M  IsLieMetabelian( <L> )
##  

InstallMethod( IsLieMetabelian,
#+  In `Lie metabelian group rings', Group and semigroup rings, North-Holland, 
#+  1986, 153-161, Levin and Rosenberger have classified all groups $G$ 
#+  such that the associated
#+  Lie algebra $L$ of the group ring is (Lie) metabelian. This method uses
#+  their classification, making it considerably faster than
#+  the more elementary method which just calculates Lie commutators.
#+  
#+  Note that he prefix `Lie' is consistently used to
#+  distinguish properties of Lie algebras from the analogous properties
#+  of groups (or of general algebras). Not using this prefix may
#+  result in error messages, or even in wrong results without warning.
    "for a Lie algebra of a group ring",
    true,
    [ IsLieAlgebraByAssociativeAlgebra and IsLieAlgebraOfGroupRing ], 0,
    
    function(L)
      local p,G,D;
      if IsLieAbelian(L) then
        return true;
      fi;
      p:=Characteristic(L);
      G:=UnderlyingGroup(L);
      D:=DerivedSubgroup(G);
      return
        (p=3 and Size(D)=3 and IsSubset(Centre(G),D))
         or
        (p=2 and Size(D)<=4 and Exponent(D)=2 and IsSubset(Centre(G),D));
    end
);


InstallMethod( IsLieCentreByMetabelian,
    "for a Lie algebra",
    true,
    [ IsAlgebra and IsLieAlgebra and HasLieDerivedSeries ], 0,
    L->IsSubset(LieCentre(L), LieDerivedSeries(L)[3]));


InstallMethod( IsLieCentreByMetabelian,
    "for a Lie algebra",
    true,
    [ IsAlgebra and IsLieAlgebra ], 0,
    function(L)
      local C,B,d,i,j;
      C:=LieCentre(L);
      B:=Filtered(AsSSortedList(Basis(LieDerivedSubalgebra(L))),
                  x -> not (x in C)
                 );
      d:=Length(B);
      for i in [1..d-1] do
        for j in [i+1..d] do
          if not (B[i]*B[j] in C) then
            return false;
          fi;
        od;
      od;
      return true;
    end);


#############################################################################
##
#M  IsLieCentreByMetabelian( <L> )
##  

InstallMethod( IsLieCentreByMetabelian,
#+  In various papers,
#+  K\"ulshammer, Sahai, Sharma, Srivastava and the author of the share
#+  package have classified all groups $G$ 
#+  such that the associated
#+  Lie algebra $L$ of the group ring is (Lie) centre-by-metabelian. 
#+  (The most general result to date may be found in the preprint
#+  `Lie centre-by-metabelian group algebras over commutative rings',
#+  available on the authors' WWW pages under
#+  http://www.mathematik.uni-jena.de/algebra/skripten/{\#}rossmanith.)
#+  This method uses
#+  the classification, making it considerably faster than
#+  the more elementary method which just calculates Lie commutators.
#+  
#+  Note that he prefix `Lie' is consistently used to
#+  distinguish properties of Lie algebras from the analogous properties
#+  of groups (or of general algebras). Not using this prefix may
#+  result in error messages, or even in wrong results without warning.
    "for a Lie algebra of a group ring",
    true,
    [ IsLieAlgebraByAssociativeAlgebra and IsLieAlgebraOfGroupRing ], 0,
    
    function(L)
      local p,G,D,C,a;
      if IsLieAbelian(L) then
        return true;
      fi;
      G:=UnderlyingGroup(L);
      D:=DerivedSubgroup(G);
      if not IsAbelian(D) then
        return false;
      fi;                      # now we have a nonabelian, metabelian group
      p:=Characteristic(L);
      if p=2 then                   # The checks for the p=2 case could
        if (Size(D) in [2,4]) then  # be formulated more compactly, however
          return true;              # this turns out to be the fastest way.
        else
          C:=Centralizer(G,D);
          if Size(D)=8 and C=G and Exponent(D)=2 then
            return true;
          elif Index(G,C)=2 then
            if IsAbelian(C) then
              return true;
            else
              a:=First(GeneratorsOfGroup(G), x -> not (x in C));
              if Size(D)=8 and Exponent(D)=4       # D = C2xC4
                and DerivedSubgroup(C)=Agemo(D,2)
                and ForAll(GeneratorsOfGroup(D), x -> (x^a=x^-1) )
              then
                return true;
              fi;
            fi;
          elif (G=C) then 
            if ForAny(SubgroupsOfIndexTwo(G), IsAbelian) then
              return true;
            fi;
          fi;
        fi;
        return false;
      elif p=3 then
        return Size(D)=3;
      else
        return false;
      fi;
    end);


