#############################################################################
##
##  This file is part of GAP, a system for computational discrete algebra.
##  This file's authors include Thomas Breuer, Willem de Graaf.
##
##  Copyright of GAP belongs to its developers, whose names are too numerous
##  to list here. Please refer to the COPYRIGHT file for details.
##
##  SPDX-License-Identifier: GPL-2.0-or-later
##
##  This file contains those functions that mainly deal with matrix algebras,
##  that is, associative matrix algebras and matrix Lie algebras.
##


#############################################################################
##
#M  RingByGenerators( <mats> )  . . . .  ring generated by a list of matrices
##
##  If <mats> is a list of matrices over a field then we construct a matrix
##  algebra over its prime field.
##
InstallOtherMethod( RingByGenerators,
    "for a list of matrices over a finite field",
    true,
    [ IsFFECollCollColl ], 0,
    mats -> FLMLORByGenerators( GF( Characteristic( mats ) ), mats ) );

InstallOtherMethod( RingByGenerators,
    "for a list of matrices over the Cyclotomics",
    true,
    [ IsCyclotomicCollCollColl ], 0,
    mats -> FLMLORByGenerators( Integers, mats ) );


#############################################################################
##
#M  DefaultRingByGenerators( <mats> )  . . ring containing a list of matrices
##
##  If <mats> is a list of matrices over a field then we construct a matrix
##  algebra over its prime field.
##  (So this may differ from the result of `RingByGenerators' if the
##  characteristic is zero.)
##
InstallOtherMethod( DefaultRingByGenerators,
    "for a list of matrices over a finite field",
    true,
    [ IsFFECollCollColl ], 0,
    mats -> FLMLORByGenerators( GF( Characteristic( mats ) ), mats ) );

InstallOtherMethod( DefaultRingByGenerators,
    "for a list of matrices over the Cyclotomics",
    true,
    [ IsCyclotomicCollCollColl ], 0,
    mats -> FLMLORByGenerators( Rationals, mats ) );


#############################################################################
##
#M  RingByGenerators( <mats> )  . .  ring generated by a list of Lie matrices
##
##  If <mats> is a list of Lie matrices over a finite field then we construct
##  a matrix Lie algebra over its prime field.
##
InstallOtherMethod( RingByGenerators,
    "for a list of Lie matrices over a finite field",
    true,
    [ IsLieObjectCollection and IsMatrixCollection ], 0,
    function( mats )

    local Fam;

    # Check whether the matrices lie in the Lie family of a family of
    # ordinary matrices.
    Fam:= UnderlyingFamily( ElementsFamily( FamilyObj( mats ) ) );
    if not HasElementsFamily( Fam ) then
      TryNextMethod();
    fi;
    Fam:= ElementsFamily( Fam );
    if not HasElementsFamily( Fam ) then
      TryNextMethod();
    fi;
    Fam:= ElementsFamily( Fam );

    # Handle the cases that the entries of the matrices
    # are FFEs or cyclotomics.
    if IsFFEFamily( Fam ) then
      return FLMLORByGenerators( GF( Characteristic( Fam ) ), mats );
    elif IsIdenticalObj( Fam, CyclotomicsFamily ) then
      return FLMLORByGenerators( Integers, mats );
    else
      TryNextMethod();
    fi;
    end );


#############################################################################
##
#M  DefaultRingByGenerators( <mats> )   . . ring cont. a list of Lie matrices
##
##  If <mats> is a list of Lie matrices over a field then we construct
##  a matrix Lie algebra over its prime field.
##  (So this may differ from the result of `RingByGenerators' if the
##  characteristic is zero.)
##
InstallOtherMethod( DefaultRingByGenerators,
    "for a list of Lie matrices",
    true,
    [ IsLieObjectCollection and IsMatrixCollection ], 0,
    function( mats )

    local Fam;

    # Check whether the matrices lie in the Lie family of a family of
    # ordinary matrices.
    Fam:= UnderlyingFamily( ElementsFamily( FamilyObj( mats ) ) );
    if not HasElementsFamily( Fam ) then
      TryNextMethod();
    fi;
    Fam:= ElementsFamily( Fam );
    if not HasElementsFamily( Fam ) then
      TryNextMethod();
    fi;
    Fam:= ElementsFamily( Fam );

    # Handle the cases that the entries of the matrices
    # are FFEs or cyclotomics.
    if IsFFEFamily( Fam ) then
      return AlgebraByGenerators( GF( Characteristic( Fam ) ), mats );
    elif IsIdenticalObj( Fam, CyclotomicsFamily ) then
      return AlgebraByGenerators( Rationals, mats );
    else
      TryNextMethod();
    fi;
    end );


#############################################################################
##
#M  RingWithOneByGenerators( <mats> )  ring-with-one gen. by list of matrices
##
##  If <mats> is a list of matrices over a field then we construct a matrix
##  algebra over its prime field.
##
InstallOtherMethod( RingWithOneByGenerators,
    "for a list of matrices over a finite field",
    true,
    [ IsFFECollCollColl ], 0,
    mats -> FLMLORWithOneByGenerators( GF( Characteristic(mats) ), mats ) );

InstallOtherMethod( RingWithOneByGenerators,
    "for a list of matrices over the Cyclotomics",
    true,
    [ IsCyclotomicCollCollColl ], 0,
    mats -> FLMLORWithOneByGenerators( Integers, mats ) );

#T how to formulate this for any field?


#############################################################################
##
#M  FLMLORByGenerators( <F>, <mats> )
#M  FLMLORByGenerators( <F>, <empty>, <zero> )
#M  FLMLORByGenerators( <F>, <mats>, <zero> )
##
InstallMethod( FLMLORByGenerators,
    "for division ring and list of ordinary matrices over it",
    IsElmsCollColls,
    [ IsDivisionRing, IsCollection and IsList ], 0,
    function( F, mats )
    local dims, A;

    # Check that all entries in `mats' are square matrices of the same shape.
    if not IsOrdinaryMatrix( mats[1] ) then
      TryNextMethod();
    fi;
    dims:= DimensionsMat( mats[1] );
    if    dims[1] <> dims[2]
       or not ForAll( mats, mat ->     IsOrdinaryMatrix( mat )
                                   and DimensionsMat( mat ) = dims ) then
      TryNextMethod();
    fi;

    if ForAll( mats, mat -> ForAll( mat, row -> IsSubset( F, row ) ) ) then
      A:= Objectify( NewType( FamilyObj( mats ),
                                  IsFLMLOR
                              and IsGaussianMatrixSpace
                              and IsAttributeStoringRep ),
                     rec() );
    else
      A:= Objectify( NewType( FamilyObj( mats ),
                                  IsFLMLOR
                              and IsVectorSpace
                              and IsNonGaussianMatrixSpace
                              and IsAttributeStoringRep ),
                     rec() );
    fi;

    SetLeftActingDomain( A, F );
    SetGeneratorsOfLeftOperatorRing( A, AsList( mats ) );
    SetDimensionOfVectors( A, dims );

    # If the generators are associative elements then so is `A'.
    if ForAll( mats, IsAssociativeElement ) then
      SetIsAssociative( A, true );
    fi;

    # Return the result.
    return A;
    end );

InstallOtherMethod( FLMLORByGenerators,
    "for division ring, empty list, and square ordinary matrix",
    function( FamF, Famempty, Famzero )
        return IsElmsColls( FamF, Famzero );
    end,
    [ IsDivisionRing, IsList and IsEmpty, IsOrdinaryMatrix ], 0,
    function( F, empty, zero )
    local dims, A;

    # Check whether this method is the right one.
    dims:= DimensionsMat( zero );
    if dims[1] <> dims[2] then
      Error( "<zero> must be a square matrix" );
    fi;

    A:= Objectify( NewType( CollectionsFamily( FamilyObj( zero ) ),
                                IsFLMLOR
                            and IsGaussianMatrixSpace
                            and IsTrivial
                            and IsAttributeStoringRep ),
                   rec() );
    SetDimension( A, 0 );
    SetLeftActingDomain( A, F );
    SetGeneratorsOfLeftModule( A, empty );
    SetZero( A, zero );
    SetDimensionOfVectors( A, dims );

    # Return the result.
    return A;
    end );

InstallOtherMethod( FLMLORByGenerators,
    "for division ring, list of matrices over it, and ordinary matrix",
    function( FamF, Fammats, Famzero )
        return IsElmsCollColls( FamF, Fammats );
    end,
    [ IsDivisionRing, IsCollection and IsList, IsOrdinaryMatrix ], 0,
    function( F, mats, zero )
    local dims, A;

    # Check that all entries in `mats' are matrices of the same shape.
    if not IsOrdinaryMatrix( mats[1] ) then
      TryNextMethod();
    fi;
    dims:= DimensionsMat( mats[1] );
    if    dims[1] <> dims[2]
       or not ForAll( mats, mat ->     IsOrdinaryMatrix( mat )
                                   and DimensionsMat( mat ) = dims ) then
      TryNextMethod();
    fi;

    if ForAll( mats, mat -> ForAll( mat, row -> IsSubset( F, row ) ) ) then
      A:= Objectify( NewType( FamilyObj( mats ),
                                  IsFLMLOR
                              and IsGaussianMatrixSpace
                              and IsAttributeStoringRep ),
                     rec() );
    else
      A:= Objectify( NewType( FamilyObj( mats ),
                                  IsFLMLOR
                              and IsVectorSpace
                              and IsNonGaussianMatrixSpace
                              and IsAttributeStoringRep ),
                     rec() );
    fi;

    SetLeftActingDomain( A, F );
    SetGeneratorsOfLeftOperatorRing( A, AsList( mats ) );
    SetZero( A, zero );
    SetDimensionOfVectors( A, dims );

    # If the generators are associative elements then so is `A'.
    if ForAll( mats, IsAssociativeElement ) then
      SetIsAssociative( A, true );
    fi;

    # Return the result.
    return A;
    end );


#############################################################################
##
#M  FLMLORByGenerators( <F>, <lie-mats> )
#M  FLMLORByGenerators( <F>, <empty>, <lie-zero> )
#M  FLMLORByGenerators( <F>, <lie-mats>, <lie-zero> )
##
InstallMethod( FLMLORByGenerators,
    "for division ring and list of Lie matrices over it",
    IsElmsCollLieColls,
    [ IsDivisionRing, IsLieObjectCollection and IsList ], 0,
    function( F, mats )
    local dims, A;

    # Check that all entries in `mats' are square matrices of the same shape.
    if not IsLieMatrix( mats[1] ) then
      TryNextMethod();
    fi;
    dims:= DimensionsMat( mats[1] );
    if    dims[1] <> dims[2]
       or not ForAll( mats, mat ->     IsLieMatrix( mat )
                                   and DimensionsMat( mat ) = dims ) then
      TryNextMethod();
    fi;

    if ForAll( mats, mat -> ForAll( mat, row -> IsSubset( F, row ) ) ) then
      A:= Objectify( NewType( FamilyObj( mats ),
                                  IsFLMLOR
                              and IsGaussianMatrixSpace
                              and IsAttributeStoringRep ),
                     rec() );
    else
      A:= Objectify( NewType( FamilyObj( mats ),
                                  IsFLMLOR
                              and IsVectorSpace
                              and IsNonGaussianMatrixSpace
                              and IsAttributeStoringRep ),
                     rec() );
    fi;

    SetLeftActingDomain( A, F );
    SetGeneratorsOfLeftOperatorRing( A, AsList( mats ) );
    SetDimensionOfVectors( A, dims );

    # `A' consists of Lie objects, so it is a Lie algebra.
    SetIsLieAlgebra( A, true );

    # Return the result.
    return A;
    end );

InstallOtherMethod( FLMLORByGenerators,
    "for division ring, empty list, and Lie matrix",
    function( FamF, Famempty, Famzero )
        return IsElmsLieColls( FamF, Famzero );
    end,
    [ IsDivisionRing, IsList and IsEmpty, IsLieMatrix and IsLieObject ], 0,
    function( F, empty, zero )
    local dims, A;

    # Check whether this method is the right one.
    dims:= DimensionsMat( zero );
    if dims[1] <> dims[2] then
      Error( "<zero> must be a square matrix" );
    fi;

    A:= Objectify( NewType( CollectionsFamily( FamilyObj( zero ) ),
                                IsFLMLOR
                            and IsGaussianMatrixSpace
                            and IsTrivial
                            and IsAttributeStoringRep ),
                   rec() );
    SetDimension( A, 0 );
    SetLeftActingDomain( A, F );
    SetGeneratorsOfLeftModule( A, empty );
    SetZero( A, zero );
    SetDimensionOfVectors( A, dims );

    # Return the result.
    return A;
    end );

InstallOtherMethod( FLMLORByGenerators,
    "for division ring, list of Lie matrices over it, and Lie matrix",
    function( FamF, Fammats, Famzero )
        return IsElmsCollLieColls( FamF, Fammats );
    end,
    [ IsDivisionRing, IsLieObjectCollection and IsList,
      IsLieObject and IsLieMatrix ], 0,
    function( F, mats, zero )
    local dims, A;

    # Check that all entries in `mats' are matrices of the same shape.
    if not IsLieMatrix( mats[1] ) then
      TryNextMethod();
    fi;
    dims:= DimensionsMat( mats[1] );
    if    dims[1] <> dims[2]
       or not ForAll( mats, mat ->     IsLieMatrix( mat )
                                   and DimensionsMat( mat ) = dims ) then
      TryNextMethod();
    fi;

    if ForAll( mats, mat -> ForAll( mat, row -> IsSubset( F, row ) ) ) then
      A:= Objectify( NewType( FamilyObj( mats ),
                                  IsFLMLOR
                              and IsGaussianMatrixSpace
                              and IsAttributeStoringRep ),
                     rec() );
    else
      A:= Objectify( NewType( FamilyObj( mats ),
                                  IsFLMLOR
                              and IsVectorSpace
                              and IsNonGaussianMatrixSpace
                              and IsAttributeStoringRep ),
                     rec() );
    fi;

    SetLeftActingDomain( A, F );
    SetGeneratorsOfLeftOperatorRing( A, AsList( mats ) );
    SetZero( A, zero );
    SetDimensionOfVectors( A, dims );

    # `A' consists of Lie objects, so it is a Lie algebra.
    SetIsLieAlgebra( A, true );

    # Return the result.
    return A;
    end );


#############################################################################
##
#M  FLMLORWithOneByGenerators( <F>, <mats> )
#M  FLMLORWithOneByGenerators( <F>, <empty>, <zero> )
#M  FLMLORWithOneByGenerators( <F>, <mats>, <zero> )
##
InstallMethod( FLMLORWithOneByGenerators,
    "for division ring and list of ordinary matrices over it",
    IsElmsCollColls,
    [ IsDivisionRing, IsCollection and IsList ], 0,
    function( F, mats )
    local dims, A;

    # Check that all entries in `mats' are square matrices of the same shape.
    if not IsOrdinaryMatrix( mats[1] ) then
      TryNextMethod();
    fi;
    dims:= DimensionsMat( mats[1] );
    if    dims[1] <> dims[2]
       or not ForAll( mats, mat ->     IsOrdinaryMatrix( mat )
                                   and DimensionsMat( mat ) = dims ) then
      TryNextMethod();
    fi;

    if ForAll( mats, mat -> ForAll( mat, row -> IsSubset( F, row ) ) ) then
      A:= Objectify( NewType( FamilyObj( mats ),
                                  IsFLMLORWithOne
                              and IsGaussianMatrixSpace
                              and IsAttributeStoringRep ),
                     rec() );
    else
      A:= Objectify( NewType( FamilyObj( mats ),
                                  IsFLMLORWithOne
                              and IsVectorSpace
                              and IsNonGaussianMatrixSpace
                              and IsAttributeStoringRep ),
                     rec() );
    fi;

    SetLeftActingDomain( A, F );
    SetGeneratorsOfLeftOperatorRingWithOne( A, AsList( mats ) );
    SetDimensionOfVectors( A, dims );

    # If the generators are associative elements then so is `A'.
    if ForAll( mats, IsAssociativeElement ) then
      SetIsAssociative( A, true );
    fi;

    # Return the result.
    return A;
    end );

InstallOtherMethod( FLMLORWithOneByGenerators,
    "for division ring, empty list, and square ordinary matrix",
    function( FamF, Famempty, Famzero )
        return IsElmsColls( FamF, Famzero );
    end,
    [ IsDivisionRing, IsList and IsEmpty, IsOrdinaryMatrix ], 0,
    function( F, empty, zero )
    local A;

    # Check whether this method is the right one.
    if Length( zero ) <> Length( zero[1] ) then
      Error( "<zero> must be a square matrix" );
    fi;

    A:= Objectify( NewType( CollectionsFamily( FamilyObj( zero ) ),
                                IsFLMLORWithOne
                            and IsGaussianMatrixSpace
                            and IsAssociative
                            and IsAttributeStoringRep ),
                   rec() );
    SetLeftActingDomain( A, F );
    SetGeneratorsOfLeftOperatorRingWithOne( A, empty );
    SetZero( A, zero );
    SetDimensionOfVectors( A, DimensionsMat( zero ) );

    # Return the result.
    return A;
    end );

InstallOtherMethod( FLMLORWithOneByGenerators,
    "for division ring, list of matrices over it, and ordinary matrix",
    function( FamF, Fammats, Famzero )
        return     HasCollectionsFamily( FamF )
               and IsElmsColls( CollectionsFamily( FamF ), Fammats );
    end,
    [ IsDivisionRing, IsCollection and IsList, IsOrdinaryMatrix ], 0,
    function( F, mats, zero )
    local dims, A;

    # Check that all entries in `mats' are matrices of the same shape.
    if not IsOrdinaryMatrix( mats[1] ) then
      TryNextMethod();
    fi;
    dims:= DimensionsMat( mats[1] );
    if    dims[1] <> dims[2]
       or not ForAll( mats, mat ->     IsOrdinaryMatrix( mat )
                                   and DimensionsMat( mat ) = dims ) then
      TryNextMethod();
    fi;

    if ForAll( mats, mat -> ForAll( mat, row -> IsSubset( F, row ) ) ) then
      A:= Objectify( NewType( FamilyObj( mats ),
                                  IsFLMLORWithOne
                              and IsGaussianMatrixSpace
                              and IsAttributeStoringRep ),
                     rec() );
    else
      A:= Objectify( NewType( FamilyObj( mats ),
                                  IsFLMLORWithOne
                              and IsVectorSpace
                              and IsNonGaussianMatrixSpace
                              and IsAttributeStoringRep ),
                     rec() );
    fi;

    SetLeftActingDomain( A, F );
    SetGeneratorsOfLeftOperatorRingWithOne( A, AsList( mats ) );
    SetZero( A, zero );
    SetDimensionOfVectors( A, dims );

    # If the generators are associative elements then so is `A'.
    if ForAll( mats, IsAssociativeElement ) then
      SetIsAssociative( A, true );
    fi;

    # Return the result.
    return A;
    end );


#############################################################################
##
#M  TwoSidedIdealByGenerators( <A>, <mats> )
##
InstallMethod( TwoSidedIdealByGenerators,
    "for Gaussian matrix algebra and list of matrices",
    IsIdenticalObj,
    [ IsMatrixFLMLOR and IsGaussianMatrixSpace,
      IsCollection and IsList ], 0,
    function( A, mats )
    local dims, I;

    # Check that all entries in `mats' are square matrices of the same shape.
    dims:= DimensionOfVectors( A );
    if not ForAll( mats, mat ->     IsMatrix( mat )
                                and DimensionsMat( mat ) = dims ) then
      Error( "entries of <mats> do not have the right dimension" );
    fi;

    I:= Objectify( NewType( FamilyObj( mats ),
                                IsFLMLOR
                            and IsGaussianMatrixSpace
                            and IsAttributeStoringRep ),
                   rec() );

    SetLeftActingDomain( I, LeftActingDomain( A ) );
    SetGeneratorsOfTwoSidedIdeal( I, mats );
    SetLeftActingRingOfIdeal( I, A );
    SetRightActingRingOfIdeal( I, A );
    SetDimensionOfVectors( I, dims );

    # Return the result.
    return I;
    end );


InstallMethod( TwoSidedIdealByGenerators,
    "for non-Gaussian matrix algebra and list of matrices",
    IsIdenticalObj,
    [ IsMatrixFLMLOR and IsNonGaussianMatrixSpace,
      IsCollection and IsList ], 0,
    function( A, mats )
    local dims, I;

    # Check that all entries in `mats' are square matrices of the same shape.
    dims:= DimensionOfVectors( A );
    if not ForAll( mats, mat ->     IsMatrix( mat )
                                and DimensionsMat( mat ) = dims ) then
      Error( "entries of <mats> do not have the right dimension" );
    fi;

    I:= Objectify( NewType( FamilyObj( mats ),
                                IsFLMLOR
                            and IsVectorSpace
                            and IsNonGaussianMatrixSpace
                            and IsAttributeStoringRep ),
                   rec() );

    SetLeftActingDomain( I, LeftActingDomain( A ) );
    SetGeneratorsOfTwoSidedIdeal( I, mats );
    SetLeftActingRingOfIdeal( I, A );
    SetRightActingRingOfIdeal( I, A );
    SetDimensionOfVectors( I, dims );

    # Return the result.
    return I;
    end );


InstallMethod( TwoSidedIdealByGenerators,
    "for matrix algebra and empty list",
    true,
    [ IsMatrixFLMLOR, IsList and IsEmpty ], 0,
    function( A, mats )
    local I;

    I:= Objectify( NewType( FamilyObj( mats ),
                                IsFLMLOR
                            and IsGaussianMatrixSpace
                            and IsTrivial
                            and IsAttributeStoringRep ),
                   rec() );

    SetDimension( I, 0 );
    SetLeftActingDomain( I, LeftActingDomain( A ) );
    SetGeneratorsOfTwoSidedIdeal( I, mats );
    SetGeneratorsOfLeftOperatorRing( I, mats );
    SetGeneratorsOfLeftModule( I, mats );
    SetLeftActingRingOfIdeal( I, A );
    SetRightActingRingOfIdeal( I, A );
    SetDimensionOfVectors( I, DimensionOfVectors( A ) );

    # Return the result.
    return I;
    end );


#############################################################################
##
#M  LeftIdealByGenerators( <A>, <mats> )
##
InstallMethod( LeftIdealByGenerators,
    "for Gaussian matrix algebra and list of matrices",
    IsIdenticalObj,
    [ IsMatrixFLMLOR and IsGaussianMatrixSpace,
      IsCollection and IsList ], 0,
    function( A, mats )
    local dims, I;

    # Check that all entries in `mats' are square matrices of the same shape.
    dims:= DimensionOfVectors( A );
    if not ForAll( mats, mat ->     IsMatrix( mat )
                                and DimensionsMat( mat ) = dims ) then
      Error( "entries of <mats> do not have the right dimension" );
    fi;

    I:= Objectify( NewType( FamilyObj( mats ),
                                IsFLMLOR
                            and IsGaussianMatrixSpace
                            and IsAttributeStoringRep ),
                   rec() );

    SetLeftActingDomain( I, LeftActingDomain( A ) );
    SetGeneratorsOfLeftIdeal( I, AsList( mats ) );
    SetLeftActingRingOfIdeal( I, A );
    SetDimensionOfVectors( I, dims );

    # Return the result.
    return I;
    end );


InstallMethod( LeftIdealByGenerators,
    "for non-Gaussian matrix algebra and list of matrices",
    IsIdenticalObj,
    [ IsMatrixFLMLOR and IsNonGaussianMatrixSpace,
      IsCollection and IsList ], 0,
    function( A, mats )
    local dims, I;

    # Check that all entries in `mats' are square matrices of the same shape.
    dims:= DimensionOfVectors( A );
    if not ForAll( mats, mat ->     IsMatrix( mat )
                                and DimensionsMat( mat ) = dims ) then
      Error( "entries of <mats> do not have the right dimension" );
    fi;

    I:= Objectify( NewType( FamilyObj( mats ),
                                IsFLMLOR
                            and IsVectorSpace
                            and IsNonGaussianMatrixSpace
                            and IsAttributeStoringRep ),
                   rec() );

    SetLeftActingDomain( I, LeftActingDomain( A ) );
    SetGeneratorsOfLeftIdeal( I, AsList( mats ) );
    SetLeftActingRingOfIdeal( I, A );
    SetDimensionOfVectors( I, dims );

    # Return the result.
    return I;
    end );


InstallMethod( LeftIdealByGenerators,
    "for matrix algebra and empty list",
    true,
    [ IsMatrixFLMLOR, IsList and IsEmpty ], 0,
    function( A, mats )
    local I;

    I:= Objectify( NewType( FamilyObj( mats ),
                                IsFLMLOR
                            and IsGaussianMatrixSpace
                            and IsTrivial
                            and IsAttributeStoringRep ),
                   rec() );

    SetDimension( I, 0 );
    SetLeftActingDomain( I, LeftActingDomain( A ) );
    SetGeneratorsOfLeftIdeal( I, mats );
    SetGeneratorsOfLeftOperatorRing( I, mats );
    SetGeneratorsOfLeftModule( I, mats );
    SetLeftActingRingOfIdeal( I, A );
    SetDimensionOfVectors( I, DimensionOfVectors( A ) );

    # Return the result.
    return I;
    end );


#############################################################################
##
#M  RightIdealByGenerators( <A>, <mats> )
##
InstallMethod( RightIdealByGenerators,
    "for Gaussian matrix algebra and list of matrices",
    IsIdenticalObj,
    [ IsMatrixFLMLOR and IsGaussianMatrixSpace,
      IsCollection and IsList ], 0,
    function( A, mats )
    local dims, I;

    # Check that all entries in `mats' are square matrices of the same shape.
    dims:= DimensionOfVectors( A );
    if not ForAll( mats, mat ->     IsMatrix( mat )
                                and DimensionsMat( mat ) = dims ) then
      Error( "entries of <mats> do not have the right dimension" );
    fi;

    I:= Objectify( NewType( FamilyObj( mats ),
                                IsFLMLOR
                            and IsGaussianMatrixSpace
                            and IsAttributeStoringRep ),
                   rec() );

    SetLeftActingDomain( I, LeftActingDomain( A ) );
    SetGeneratorsOfRightIdeal( I, mats );
    SetRightActingRingOfIdeal( I, A );
    SetDimensionOfVectors( I, dims );

    # Return the result.
    return I;
    end );


InstallMethod( RightIdealByGenerators,
    "for non-Gaussian matrix algebra and list of matrices",
    IsIdenticalObj,
    [ IsMatrixFLMLOR and IsNonGaussianMatrixSpace,
      IsCollection and IsList ], 0,
    function( A, mats )
    local dims, I;

    # Check that all entries in `mats' are square matrices of the same shape.
    dims:= DimensionOfVectors( A );
    if not ForAll( mats, mat ->     IsMatrix( mat )
                                and DimensionsMat( mat ) = dims ) then
      Error( "entries of <mats> do not have the right dimension" );
    fi;

    I:= Objectify( NewType( FamilyObj( mats ),
                                IsFLMLOR
                            and IsVectorSpace
                            and IsNonGaussianMatrixSpace
                            and IsAttributeStoringRep ),
                   rec() );

    SetLeftActingDomain( I, LeftActingDomain( A ) );
    SetGeneratorsOfRightIdeal( I, mats );
    SetRightActingRingOfIdeal( I, A );
    SetDimensionOfVectors( I, dims );

    # Return the result.
    return I;
    end );


InstallMethod( RightIdealByGenerators,
    "for matrix algebra and empty list",
    true,
    [ IsMatrixFLMLOR, IsList and IsEmpty ], 0,
    function( A, mats )
    local I;

    I:= Objectify( NewType( FamilyObj( mats ),
                                IsFLMLOR
                            and IsGaussianMatrixSpace
                            and IsTrivial
                            and IsAttributeStoringRep ),
                   rec() );

    SetDimension( I, 0 );
    SetLeftActingDomain( I, LeftActingDomain( A ) );
    SetGeneratorsOfRightIdeal( I, mats );
    SetGeneratorsOfLeftOperatorRing( I, mats );
    SetGeneratorsOfLeftModule( I, mats );
    SetRightActingRingOfIdeal( I, A );
    SetDimensionOfVectors( I, DimensionOfVectors( A ) );

    # Return the result.
    return I;
    end );


#############################################################################
##
#M  IsUnit( <A>, <mat> )  . . . . . . . . . . .  for matrix FLMLOR and matrix
##
InstallMethod( IsUnit,
    "for matrix FLMLOR and matrix",
    IsCollsElms,
    [ IsMatrixFLMLOR, IsMatrix ], 0,
    function ( A, m )
    m:= m^-1;
    return m <> fail and m in A;
    end );


#############################################################################
##
#M  RadicalOfAlgebra( <A> ) . . . . . . . . for an associative matrix algebra
##
##  The radical of an associative algebra <A>  defined as the ideal of all
##  elements $x$ of <A> such that $x y$ is nilpotent for all $y$ in <A>.
##
##  For Gaussian matrix algebras this is easy to calculate,
##  for arbitrary associative algebras the task is reduced to the
##  Gaussian matrix algebra case.
##
##  The implementation of the characterisitc p>0 part is by Craig Struble.
##
InstallMethod( RadicalOfAlgebra,
    "for associative Gaussian matrix algebra",
    true,
    [ IsAlgebra and IsGaussianMatrixSpace and IsMatrixFLMLOR ], 0,
    function( A )

    local F,           # the field of A
          p,           # the characteristic of F
          q,           # the size of F
          n,           # the dimension of A
          ident,       # the identity matrix
          bas,         # a list of basis vectors of A
          minusOne,    # -1 in F
          eqs,         # equation set
          i,j,         # loop variables
          G,           # Gram matrix
          I,           # a list of basis vectors of an ideal of A
          I_prime,     # I \cup ident
          changed,     # flag denoted if $I_i <> I_{i-1}$ in ideal sequence
          pexp,        # a power of the prime p
          dim,         # the dimension of the vector space where A acts on
          charPoly,    # characteristic polynomials of elements in A
          invFrob,     # inverse of the Frobenius map of F
          invFrobexp,  # a power of the invFrob
          r, r_prime;  # the length of I and I_prime

    # Check associativity.
    if not IsAssociative( A ) then
      TryNextMethod();
    fi;

    if Dimension( A ) = 0 then return A; fi;

    F:= LeftActingDomain( A );
    p:= Characteristic( F );
    n:= Dimension( A );
    bas:= BasisVectors( Basis( A ) );

    if p = 0 then

      # First we treat the characteristic 0 case.
      # According to Dickson's theorem we have that in this case
      # the radical of `A' is $\{ x\in A \mid Tr(xy) = 0 \forall y \in A \}$,
      # so it can be computed by solving a system of linear equations.

      eqs:= List( [ 1 .. n ], x -> [] );

      for i in [1..n] do
        for j in [i..n] do
          eqs[i][j]:= TraceMat( bas[i] * bas[j] );
          eqs[j][i]:= eqs[i][j];
        od;
      od;

      return SubalgebraNC( A, List( NullspaceMat( eqs ),
                                    x -> LinearCombination( bas, x ) ),
                           "basis" );

    else

      # If `p' is greater than 0, then the situation is more difficult.
      # We implement the algorithm presented in
      # "Cohen, Arjeh M, G\'{a}bor Ivanyos, and David B. Wales,
      # 'Finding the radical of an algebra of linear transformations,'
      # Journal of Pure and Applied Algebra 117 & 118 (1997), 177-193".

      q := Size( F );
      dim := Length( bas[1] );
      pexp := 1;
      invFrob := InverseGeneralMapping(FrobeniusAutomorphism(F));
      invFrobexp := IdentityMapping(F);
      minusOne := -One(F);
      ident := IdentityMat( dim, F );
      changed := true;
      I := ShallowCopy( bas );

      # Compute the sequence of ideals $I_i$ (see the paper by Cohen, et.al.)
      while pexp <= dim do
          # These values need recomputation only when $I_i <> I_{i-1}$
          if changed then
              I_prime := ShallowCopy( I );
              if not ident in I_prime then
                  Add( I_prime, ident );
              fi;
              r := Length( I );
              r_prime := Length( I_prime );
              eqs := NullMat( r, r_prime, F );
              charPoly := List( [1..r], x -> [] );
              for i in [1..r] do
                  for j in [1..r_prime] do
                      charPoly[i][j] :=
                          CoefficientsOfUnivariatePolynomial( CharacteristicPolynomial( F, F, I[i]*I_prime[j] ) );
                  od;
              od;
              changed := false;
          fi;

          for i in [1..r] do
              for j in [1..r_prime] do
                  eqs[i][j] := minusOne^pexp * charPoly[i][j][dim-pexp+1];
              od;
          od;

          G := NullspaceMat( eqs );

          if Length( G ) = 0 then
              return TrivialSubalgebra( A );
          elif Length( G ) <> r then
              # $I_i <> I_{i-1}$, so compute the basis for $I_i$
              changed := true;
              G := List( G, x -> List( x, y -> y^invFrobexp ) );
              I := List( G, x -> LinearCombination( I, x ) );
          fi;

          # prepare for next step

          invFrobexp := invFrobexp * invFrob;
          pexp := pexp*p;
      od;

      return SubalgebraNC( A, I, "basis" );
    fi;
    end );




#############################################################################
##
#F  CentralizerInAssociativeGaussianMatrixAlgebra( <base>, <gens> )
##
##  is a list of basis vectors of the $R$-FLMLOR that is the centralizer
##  of the $R$-FLMLOR with generators <gens> in the $R$-FLMLOR
##  with $R$-basis vectors <base>.
##
##  View the centralizer condition for the matrix $a$ as an equation system
##  \[ \sum_{b\in B} c_b ( b a - a b ) = 0 \ , \]
##  where $B$ is a basis of <A> and $c_b$ in the coefficients field.
##  Then the centralizer is spanned by the matrices
##  $\sum_{b\in B} c_b b$ where the vector $c$ is a basis vector of
##  the solution space.
##
CentralizerInAssociativeGaussianMatrixAlgebra := function( base, gens )
#T better use structure constants ?

    local gen, mat, sol;

    for gen in gens do

      mat:= List( base, b -> Concatenation( b * gen - gen * b ) );
      sol:= NullspaceMat( mat );

      # Replace `base' by a vector space base of the centralizer.
      base:= List( sol, x -> LinearCombination( base, x ) );

    od;

    return base;
end;


#############################################################################
##
#M  Centralizer( <A>, <mat> ) . . . . . . . . .  for matrix FLMLOR and matrix
##
InstallMethod( CentralizerOp,
    "for associative Gaussian matrix FLMLOR, and ordinary matrix",
    IsCollsElms,
    [ IsMatrixFLMLOR and IsAssociative and IsGaussianSpace,
      IsOrdinaryMatrix ], 0,
    function( A, mat )
    return SubalgebraNC( A,
               CentralizerInAssociativeGaussianMatrixAlgebra(
                   BasisVectors( Basis( A ) ),
                   [ mat ] ),
               "basis" );
    end );


#############################################################################
##
#M  Centralizer( <A>, <C> ) . . . . . . . for matrix FLMLOR and matrix FLMLOR
##
InstallMethod( CentralizerOp,
    "for associative Gaussian matrix FLMLOR, and FLMLOR",
    IsIdenticalObj,
    [ IsMatrixFLMLOR and IsAssociative and IsGaussianSpace,
      IsMatrixFLMLOR ], 0,
    function( A, C )
    return SubalgebraNC( A,
               CentralizerInAssociativeGaussianMatrixAlgebra(
                   BasisVectors( Basis( A ) ),
                   GeneratorsOfAlgebra( C ) ),
               "basis" );
    end );


#############################################################################
##
#M  Centralizer( <A>, <mat> ) . . . . . for matrix FLMLOR-with-one and matrix
##
InstallMethod( CentralizerOp,
    "for associative Gaussian matrix FLMLOR-with-one, and ordinary matrix",
    IsCollsElms,
    [ IsMatrixFLMLOR and IsFLMLORWithOne
                     and IsAssociative and IsGaussianSpace,
      IsOrdinaryMatrix ], 0,
    function( A, mat )
    return SubalgebraWithOneNC( A,
               CentralizerInAssociativeGaussianMatrixAlgebra(
                   BasisVectors( Basis( A ) ),
                   [ mat ] ),
               "basis" );
    end );


#############################################################################
##
#M  Centralizer( <A>, <C> ) . .  for matrix FLMLOR-with-one and matrix FLMLOR
##
InstallMethod( CentralizerOp,
    "for associative Gaussian matrix FLMLOR-with-one, and FLMLOR",
    IsIdenticalObj,
    [ IsMatrixFLMLOR and IsFLMLORWithOne
                     and IsAssociative and IsGaussianSpace,
      IsMatrixFLMLOR ], 0,
    function( A, C )
    return SubalgebraWithOneNC( A,
               CentralizerInAssociativeGaussianMatrixAlgebra(
                   BasisVectors( Basis( A ) ),
                   GeneratorsOfAlgebra( C ) ),
               "basis" );
    end );


#############################################################################
##
#F  FullMatrixAlgebraCentralizer( <F>, <lst> )
##
##  Compute the centralizer of the list of matrices <lst> in the full
##  matrix algebra over <F>.
##
InstallGlobalFunction( FullMatrixAlgebraCentralizer, function( F, lst )
    local len,      # length of `lst'
          dims,     # dimensions of the matrices
          n,        # number of rows/columns
          n2,       # square of `n'
          eq,       # equation system
          u,i,j,k,  # loop variables
          bc,       # basis of solutions of the equation system
          Bcen,     # basis vectors of the centralizer
          M;        # one centralizing matrix

    len:= Length( lst );
    if len = 0 then
      Error( "cannot compute the centralizer of an empty set" );
    elif not (IsSubset( F, DefaultScalarDomainOfMatrixList( lst ) ) or
	      IsSubset( F, FieldOfMatrixList( lst ) ) ) then
      Error( "not all entries of the matrices in <lst> lie in <F>" );
    fi;

    dims:= DimensionsMat( lst[1] );
    n:= dims[1];
    n2:= n*n;

    # In the equations matrices are viewed as vectors of length `n*n'.
    # Position `(i,j)' in the matrix corresponds with position `(i-1)*n+j'
    # in the vector.
    eq:= NullMat( n2, n2 * len, F );
    for u in [ 1 .. len ] do
      for i in [1..n] do
        for j in [1..n] do
          for k in [1..n] do
            eq[(i-1)*n+k][(u-1)*n2+(i-1)*n+j]:=
              eq[(i-1)*n+k][(u-1)*n2+(i-1)*n+j] + lst[u][k][j];
            eq[(k-1)*n+j][(u-1)*n2+(i-1)*n+j]:=
              eq[(k-1)*n+j][(u-1)*n2+(i-1)*n+j] - lst[u][i][k];
          od;
        od;
      od;
    od;

    # Translate the vectors back to matrices.
    bc:= NullspaceMat( eq );
    Bcen:= [];
    for i in bc do
      M:= [];
      for j in [ 1 .. n ] do
        M[j]:= i{ [ (j-1)*n+1 .. j*n ] };
      od;
      Add( Bcen, M );
    od;

    return AlgebraWithOne( F, Bcen, "basis" );
end );


#############################################################################
##
#M  CentralizerOp( <A>, <S> ) . . . . . . for full associative matrix algebra
##
InstallMethod( CentralizerOp,
    "for full (associative) matrix FLMLOR, and FLMLOR",
    IsIdenticalObj,
    [ IsMatrixFLMLOR and IsFLMLORWithOne
                     and IsAssociative and IsGaussianSpace
                     and IsFullMatrixModule,
      IsMatrixFLMLOR ], 0,
    function( A, S )
    if not IsAssociative( A ) then
      TryNextMethod();
    fi;
    return FullMatrixAlgebraCentralizer( LeftActingDomain( A ),
                                         GeneratorsOfAlgebra( S ) );
    end );

InstallMethod( CentralizerOp,
    "for full (associative) matrix FLMLOR, and left module",
    IsIdenticalObj,
    [ IsMatrixFLMLOR and IsFLMLORWithOne
                     and IsAssociative and IsGaussianSpace
                     and IsFullMatrixModule,
      IsLeftModule ], 0,
    function( A, S )
    if not IsAssociative( A ) then
      TryNextMethod();
    fi;
    return FullMatrixAlgebraCentralizer( LeftActingDomain( A ),
                                         GeneratorsOfLeftModule( S ) );
    end );

InstallMethod( CentralizerOp,
    "for full (associative) matrix FLMLOR, and list of matrices",
    IsIdenticalObj,
    [ IsMatrixFLMLOR and IsFLMLORWithOne
                     and IsAssociative and IsGaussianSpace
                     and IsFullMatrixModule,
      IsCollection and IsList ], 0,
    function( A, S )
    if not IsAssociative( A ) then
      TryNextMethod();
    fi;
    return FullMatrixAlgebraCentralizer( LeftActingDomain( A ), S );
    end );

InstallMethod( CentralizerOp,
    "for full (associative) matrix FLMLOR, and empty list",
    true,
    [ IsMatrixFLMLOR and IsFullMatrixModule, IsList and IsEmpty ], 0,
    function( A, S )
    if not IsAssociative( A ) then
      TryNextMethod();
    fi;
    return A;
    end );


#############################################################################
##
#F  FullMatrixFLMLOR( <R>, <n> )  . . .  FLMLOR of <n>-dim. matrices over <R>
##
##  Let $E_{i,j}$ be the matrix with value 1 in row $i$ and $column $j$, and
##  zero otherwise.
##  Clearly the full associative matrix algebra is generated by all
##  $E_{i,j}$, $i$ and $j$ ranging from 1 to <n>.
##  Define the matrix $F$ as permutation matrix for the permutation
##  $(1, 2, \ldots, n)$.  Then $E_{i,j} = F^{i-1} E_{1,1} F^{1-j}$.
##  Thus $F$ and $E_{1,1}$ are sufficient to generate the algebra.
##
InstallGlobalFunction( FullMatrixFLMLOR, function( R, n )

    local i,      # loop over the rows
          gens,   # list of generators
          one,    # the identity of the field
          A;      # algebra, result

    gens:= NullMat( n, n, R );
    gens:= [ gens, List( gens, ShallowCopy ) ];
    one:= One( R );

    # Construct the generators.
    gens[1][1][1]:= one;
    gens[2][1][n]:= one;
    for i in [ 2 .. n ] do
      gens[2][i][i-1]:= one;
    od;

    # Construct the FLMLOR.
    A:= AlgebraWithOneByGenerators( R, gens );
    SetIsFullMatrixModule( A, true );
    SetRadicalOfAlgebra( A, TrivialSubalgebra( A ) );

    # Return the FLMLOR.
    return A;
end );


#############################################################################
##
#F  FullMatrixLieAlgebra( <R>, <n> )
#F                  . . full matrix Lie algebra of <n>-dim. matrices over <R>
##
##  The set $\{ E_{1,j}, E_{k,1}; 1 \leq j, k \leq n \}$ is a generating
##  system.
#T  What is a nicer generating system ?
##
InstallGlobalFunction( FullMatrixLieFLMLOR, function( F, n )

    local null,   # null matrix
          one,    # identity of `F'
          gen,    # one generator
          gens,   # list of generators
          i,      # loop over the rows
          A;      # algebra, result

    # Construct the generators.
    null:= NullMat( n, n, F );
    one:= One( F );


    gen:= List( null, ShallowCopy );
    gen[1][1]:= one;
    gens:= [ LieObject( gen ) ];

    for i in [ 2 .. n ] do

      gen:= List( null, ShallowCopy );
      gen[1][i]:= one;
      Add( gens, LieObject( gen ) );

      gen:= List( null, ShallowCopy );
      gen[i][1]:= one;
      Add( gens, LieObject( gen ) );

    od;

    # construct the algebra
    A:= AlgebraByGenerators( F, gens );

    SetIsFullMatrixModule( A, true );

    # return the algebra
    return A;
end );


#############################################################################
##
#M  DirectSumOfAlgebras( <A1>, <A2> ) . .  for two associative matrix FLMLORs
##
##  Construct an associative matrix FLMLOR.
##
#T embeddings/projections should be provided!
##
InstallOtherMethod( DirectSumOfAlgebras,
    "for two associative matrix FLMLORs",
    IsIdenticalObj,
    [ IsMatrixFLMLOR and IsAssociative,
      IsMatrixFLMLOR and IsAssociative ], 0,
    function( A1, A2 )

    local b1,   # Basis vectors of `A1'.
          b2,   # Basis vectors of `A2'.
          p1,   # Length of the matrices of `b1'.
          p2,   # Length of the matrices of `b2'.
          B,    # A basis of `A1 \oplus A2'.
          i,    # Loop variable.
          Q,    # A matrix.
          A;    # result

    if LeftActingDomain( A1 ) <> LeftActingDomain( A2 ) then
      Error( "<A1> and <A2> must be written over the same domain" );
    fi;

    # We do not really need a basis for the arguments
    # but if we have one then we use it.
#T Do we really have so many algebra generators? (distinguish from basis?)
    if HasBasis( A1 ) and HasBasis( A2 ) then
      b1:= BasisVectors( Basis( A1 ) );
      b2:= BasisVectors( Basis( A2 ) );
    else
      b1:= GeneratorsOfAlgebra( A1 );
      b2:= GeneratorsOfAlgebra( A2 );
    fi;

    p1:= DimensionOfVectors( A1 )[1];
    p2:= DimensionOfVectors( A2 )[1];

    B:= [];
    for i in b1 do
      Q:= NullMat( p1+p2, p1+p2, LeftActingDomain( A1 ) );
      Q{ [ 1 .. p1 ] }{ [ 1 .. p1 ] }:= i;
      Add( B, Q );
    od;
    for i in b2 do
      Q:= NullMat( p1+p2, p1+p2, LeftActingDomain( A1 ) );
      Q{ [ p1+1 .. p1+p2 ] }{ [ p1+1 .. p1+p2 ] }:= i;
      Add( B, Q );
    od;

    A:= AlgebraByGenerators( LeftActingDomain( A1 ), B );
    if HasBasis( A1 ) and HasBasis( A2 ) then
      UseBasis( A, B );
    fi;

    SetIsAssociative( A, true );
#T nec. ?

    return A;
    end );


#############################################################################
##
#M  DirectSumOfAlgebras( <A1>, <A2> ) . . . . . .  for two matrix Lie FLMLORs
##
##  Construct a matrix Lie FLMLOR.
##
#T embeddings/projections should be provided!
##
InstallOtherMethod( DirectSumOfAlgebras,
    "for two matrix Lie FLMLORs",
    IsIdenticalObj,
    [ IsMatrixFLMLOR and IsLieAlgebra,
      IsMatrixFLMLOR and IsLieAlgebra ], 0,
    function( A1, A2 )

    local b1,   # Basis vectors of `A1'.
          b2,   # Basis vectors of `A2'.
          p1,   # Length of the matrices of `b1'.
          p2,   # Length of the matrices of `b2'.
          B,    # A basis of `A1 \oplus A2'.
          i,    # Loop variable.
          Q,    # A matrix.
          A;    # result

    if LeftActingDomain( A1 ) <> LeftActingDomain( A2 ) then
      Error( "<A1> and <A2> must be written over the same domain" );
    fi;

    # We do not really need a basis for the arguments
    # but if we have one then we use it.
#T Do we really have so many algebra generators? (distinguish from basis?)
    if HasBasis( A1 ) and HasBasis( A2 ) then
      b1:= BasisVectors( Basis( A1 ) );
      b2:= BasisVectors( Basis( A2 ) );
    else
      b1:= GeneratorsOfAlgebra( A1 );
      b2:= GeneratorsOfAlgebra( A2 );
    fi;

    p1:= DimensionOfVectors( A1 )[1];
    p2:= DimensionOfVectors( A2 )[1];
    
    B:= [];
    for i in b1 do
      Q:= NullMat( p1+p2, p1+p2, LeftActingDomain( A1 ) );
      Q{ [ 1 .. p1 ] }{ [ 1 .. p1 ] }:= i;
      Add( B, LieObject( Q ) );
    od;
    for i in b2 do
      Q:= NullMat( p1+p2, p1+p2, LeftActingDomain( A1 ) );
      Q{ [ p1+1 .. p1+p2 ] }{ [ p1+1 .. p1+p2 ] }:= i;
      Add( B, LieObject( Q ) );
    od;

    A:= AlgebraByGenerators( LeftActingDomain( A1 ), B );
    if HasBasis( A1 ) and HasBasis( A2 ) then
      UseBasis( A, B );
    fi;
    SetIsLieAlgebra( A, true );

    return A;
    end );


#############################################################################
##
#M  Units( <A> )  . . . . . . for a full matrix algebra (over a finite field)
##
InstallMethod( Units,
    "for a full matrix algebra (over a finite field)",
    [ IsAlgebra and IsFullMatrixModule and IsFFECollCollColl ],
    function( A )
    local F;
    F:= LeftActingDomain( A );
    if IsField( F ) and IsFinite( F ) then
      return GL( DimensionOfVectors( A )[1], Size( F ) );
    else
      TryNextMethod();
    fi;
    end );


#############################################################################
##
#F  EmptyMatrix( <char> )
##
InstallGlobalFunction( EmptyMatrix, function( char )

    local Fam, mat;

    # Get the right family.
    if char = 0 then
      Fam:= CollectionsFamily( CollectionsFamily( CyclotomicsFamily ) );
    else
      Fam:= CollectionsFamily( CollectionsFamily( FFEFamily( char ) ) );
    fi;

    # Construct the matrix.
    mat := ObjectifyWithAttributes( rec(),
             NewType( Fam,
                          IsList
                      and IsEmpty
                      and IsOrdinaryMatrix
                      and IsZero
                      and IsAssociativeElement
                      and IsCommutativeElement
                      and IsJacobianElement
                      and IsAttributeStoringRep ),
             Name,          Concatenation("EmptyMatrix( ", String(char), " )" ),
             DimensionsMat, [ 0, 0 ],
             Length,        0,
             NrRows,        0,
             NrCols,        0) ;
    SetInverse( mat, mat );
    SetAdditiveInverse( mat, mat );
    return mat;
end );


InstallMethod( \+,
    "for two empty matrices",
    IsIdenticalObj,
    [ IsMatrix and IsEmpty, IsMatrix and IsEmpty ], 0,
    function( m1, m2 ) return m1; end );

InstallOtherMethod( \*,
    "for two empty matrices",
    IsIdenticalObj,
    [ IsMatrix and IsEmpty, IsMatrix and IsEmpty ], 0,
    function( m1, m2 ) return m1; end );

InstallOtherMethod( \*,
    "for empty matrix, and empty list",
    true,
    [ IsMatrix and IsEmpty, IsList and IsEmpty ], 0,
    function( m1, m2 ) return []; end );

InstallOtherMethod( \*,
    "for empty list, and empty matrix",
    true,
    [ IsList and IsEmpty, IsMatrix and IsEmpty ], 0,
    function( m1, m2 ) return []; end );

InstallOtherMethod( \*,
    "for empty matrix, and ring element",
    true,
    [ IsMatrix and IsEmpty, IsRingElement ], 0,
    function( m, scalar ) return m; end );

InstallOtherMethod( \*,
    "for ring element, and empty matrix",
    true,
    [ IsRingElement, IsMatrix and IsEmpty ], 0,
    function( scalar, m ) return m; end );

InstallMethod( \^,
    "for empty matrix, and integer",
    true,
    [ IsMatrix and IsEmpty, IsInt ], 0,
    function( emptymat, n ) return emptymat; end );


#############################################################################
##
#M  NullAlgebra( <R> )  . . . . . . . . . . . . .  null algebra over ring <R>
##
InstallMethod( NullAlgebra,
    "for a ring",
    true,
    [ IsRing ], 0,
    R -> FLMLORByGenerators( R, [], EmptyMatrix( Characteristic( R ) ) ) );


#T #############################################################################
#T ##
#T #F  Fingerprint( <A> ) . . . . . . . . . . . . . . fingerprint of algebra <A>
#T #F  Fingerprint( <A>, <list> ) . . . . . . . . . . fingerprint of algebra <A>
#T ##
#T ##  If there is only one argument then the standard fingerprint is computed.
#T ##  This works for two-generator algebras acting only.
#T ##
#T Fingerprint := function( arg )
#T 
#T     # Check the arguments.
#T     if Length( arg ) = 0 or Length( arg ) > 2 then
#T       Error( "usage: Fingerprint( <matalg> [, <list> ] )" );
#T     fi;
#T 
#T     if   Length( arg ) = 2 then
#T 
#T       return arg[1].operations.Fingerprint( arg[1], arg[2] );
#T 
#T     elif Length( arg ) = 1 then
#T 
#T       if not IsBound( arg[1].fingerprint ) then
#T         arg[1].fingerprint:=
#T                         arg[1].operations.Fingerprint( arg[1], "standard" );
#T       fi;
#T       return arg[1].fingerprint;
#T 
#T     else
#T       Error( "number of generating matrices must be 2" );
#T     fi;
#T     end;
#T 
#T #############################################################################
#T ##
#T #F  Nullity( <mat> )
#T ##
#T Nullity := function( mat )
#T     return Dimensions( BaseNullspace( mat ) )[1];
#T     end;
#T 
#T #############################################################################
#T ##
#T #F  MatrixAssociativeAlgebraOps.Fingerprint( <A>, <list> )
#T ##
#T MatrixAssociativeAlgebraOps.Fingerprint := function( A, list )
#T 
#T     local fp,     # fingerprint, result
#T           a,      # first generator
#T           b,      # second generator
#T           ab,     # product of `a' and `b'
#T           word;   # actual word
#T 
#T     if list = "standard" then
#T 
#T       if Length( AlgebraGenerators( A ) ) <> 2 then
#T         Error( "exactly two generators needed for standard finger print" );
#T       fi;
#T 
#T       # Compute the nullities of the 6 standard elements.
#T       fp:= [];
#T       a:= AlgebraGenerators( A )[1];
#T       b:= AlgebraGenerators( A )[2];
#T       ab:= a * b;
#T       word:= ab + a + b;    fp[1]:= Nullity( word );
#T       word:= word + ab * b; fp[2]:= Nullity( word );
#T       word:= a + b * word;  fp[3]:= Nullity( word );
#T       word:= b + word;      fp[4]:= Nullity( word );
#T       word:= ab + word;     fp[5]:= Nullity( word );
#T       word:= a + word;      fp[6]:= Nullity( word );
#T 
#T     else
#T 
#T       # Compute the nullities of the words with numbers in the list.
#T       fp:= List( list, x -> Nullity( ElementAlgebra( A, x ) ) );
#T 
#T     fi;
#T     return fp;
#T     end;


#############################################################################
##
#M  RepresentativeLinearOperation( <A>, <v>, <w>, <opr> ) . . for matrix alg.
##
##  Let <A> be a finite dimensional algebra over the ring $R$,
##  and <v> and <w> vectors such that <A> acts on them via the *linear*
##  operation <opr>.
##  We compute an element of <A> that maps <v> to <w>.
##
##  We compute the coefficients $a_i$ in the equation system
##  $\sum_{i=1}^n a_i <opr>( <v>, b_i ) = <w>$,
##  where $(b_1, b_2, \ldots, b_n)$ is a basis of <A>.
##
##  For a tuple $(v_1, \ldots, v_k)$ of vectors we simply replace $v b_i$ by
##  the concatenation of the $v_j b_i$ for all $j$, and replace $w$ by the
##  concatenation $(w_1, \ldots, w_k)$, and solve this system.
##
##  (Here we install methods for matrix algebras, acting on row vectors.
##  There are also generic algebra methods for algebras acting on themselves
##  or on their free modules via `OnRight' or `OnTuples'.)
##
InstallMethod( RepresentativeLinearOperation,
    "for a matrix FLMLOR, two row vectors, and `OnRight'",
    IsCollCollsElmsElmsX,
    [ IsMatrixFLMLOR, IsRowVector, IsRowVector, IsFunction ], 0,
    function( A, v, w, opr )

    local B, vectors, a;

    if not ( opr = OnRight or opr = OnPoints ) then
      TryNextMethod();
    fi;

    if IsTrivial( A ) then
      if IsZero( w ) then
        return Zero( A );
      else
        return fail;
      fi;
    fi;

    B:= Basis( A );
    vectors:= BasisVectors( B );

    # Compute the matrix of the equation system,
    # the coefficient vector $a$, \ldots
    a:= SolutionMat( List( vectors, x -> v * x ),
                     w );
    if a = fail then
      return fail;
    fi;

    # \ldots and the representative.
    return LinearCombination( B, a );
    end );

InstallMethod( RepresentativeLinearOperation,
    "for a matrix FLMLOR, two lists of row vectors, and `OnTuples'",
    IsCollsElmsElmsX,
    [ IsFLMLOR, IsMatrix, IsMatrix, IsFunction ], 0,
#T IsOrdinaryMatrix?
    function( A, vs, ws, opr )

    local B, vectors, a;

    if not ( opr = OnTuples or opr = OnPairs ) then
      TryNextMethod();
    fi;

    if IsTrivial( A ) then
      if IsZero( ws ) then
        return Zero( A );
      else
        return fail;
      fi;
    fi;

    B:= Basis( A );
    vectors:= BasisVectors( B );

    # Compute the matrix of the equation system,
    # the coefficient vector $a$, \ldots
    a:= SolutionMat( List( vectors, x -> Concatenation( vs * x ) ),
                     Concatenation( ws ) );
    if a = fail then
      return fail;
    fi;

    # \ldots and the representative.
    return LinearCombination( B, a );
    end );


#############################################################################
##
#M  IsomorphismMatrixFLMLOR( <A> )  . . . . . . . . . . . for a matrix FLMLOR
##
InstallMethod( IsomorphismMatrixFLMLOR,
    "for a matrix FLMLOR",
    true,
    [ IsMatrixFLMLOR ], 0,
    IdentityMapping );


#T #############################################################################
#T ##
#T #F  MatrixAssociativeAlgebraOps.NaturalModule( <A> )
#T ##
#T MatrixAssociativeAlgebraOps.NaturalModule := function( A )
#T 
#T     local gens,   # module generators
#T           N;      # natural module, result
#T 
#T     if Length( AlgebraGenerators( Parent( A ) ) ) = 0 then
#T       gens:= IdentityMat( A.field, Length( Zero( Parent( A ) ) ) );
#T     else
#T       gens:= Parent( A ).algebraGenerators[1]^0;
#T     fi;
#T #T wirklich Erzeuger angeben ?
#T     N:= Module( A, gens );
#T     N.isNaturalModule:= true;
#T     N.spaceGenerators:= gens;
#T     return N;
#T     end;
