Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/matricesforhomalg/gap/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 7.8.2025 mit Größe 231 kB image not shown  

Quelle  Tools.gi   Sprache: unbekannt

 
Spracherkennung für: .gi vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

# SPDX-License-Identifier: GPL-2.0-or-later
# MatricesForHomalg: Matrices for the homalg project
#
# Implementations
#

####################################
#
# methods for operations (you MUST replace for an external CAS):
#
####################################

################################
##
## operations for ring elements:
##
################################

##
InstallMethod( Zero,
        "for homalg rings",
        [ IsHomalgRing ], 10001,
        
  function( R )
    local RP;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.Zero) then
        if IsFunction( RP!.Zero ) then
            return RP!.Zero( R );
        else
            return RP!.Zero;
        fi;
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( One,
        "for homalg rings",
        [ IsHomalgRing ], 1001,
        
  function( R )
    local RP;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.One) then
        if IsFunction( RP!.One ) then
            return RP!.One( R );
        else
            return RP!.One;
        fi;
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( MinusOne,
        "for homalg rings",
        [ IsHomalgRing ],
        
  function( R )
    local RP;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.MinusOne) then
        if IsFunction( RP!.MinusOne ) then
            return RP!.MinusOne( R );
        else
            return RP!.MinusOne;
        fi;
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( IsZero,
        "for homalg ring elements",
        [ IsHomalgRingElement ],
        
  function( r )
    local R, RP;
    
    R := HomalgRing( r );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.IsZero) then
        return RP!.IsZero( r );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( IsOne,
        "for homalg ring elements",
        [ IsHomalgRingElement ],
        
  function( r )
    local R, RP;
    
    R := HomalgRing( r );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.IsOne) then
        return RP!.IsOne( r );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( IsMinusOne,
        "for ring elements",
        [ IsRingElement ],
        
  function( r )
    
    return IsZero( r + One( r ) );
    
end );

## a synonym of `-<elm>':
InstallMethod( AdditiveInverseMutable,
        "for homalg rings elements",
        [ IsHomalgRingElement ],
        
  function( r )
    local R, RP;
    
    R := HomalgRing( r );
    
    if not HasRingElementConstructor( R ) then
        Error( "no ring element constructor found in the ring\n" );
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.Minus) and IsBound(RP!.Zero) and HasRingElementConstructor( R ) then
        return RingElementConstructor( R )( RP!.Minus( Zero( R ), r ), R );
    fi;
    
    ## never fall back to:
    ## return Zero( r ) - r;
    ## this will cause an infinite loop with a method for \- in LIRNG.gi
    
    TryNextMethod( );
    
end );

##
InstallMethod( \/,
        "for homalg ring elements",
        [ IsHomalgRingElement, IsHomalgRingElement ],
        
  function( a, u )
    local R, RP, au;
    
    R := HomalgRing( a );
    
    if not HasRingElementConstructor( R ) then
        Error( "no ring element constructor found in the ring\n" );
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.DivideByUnit) and IsUnit( u ) then
        au := RP!.DivideByUnit( a, u );
        if au = fail then
            return fail;
        fi;
        return RingElementConstructor( R )( au, R );
    fi;
    
    au := RightDivide( HomalgMatrix( [ a ], 1, 1, R ), HomalgMatrix( [ u ], 1, 1, R ) );
    
    if not IsHomalgMatrix( au ) then
        return fail;
    fi;
    
    return au[ 1, 1 ];
    
end );

###########################
##
## operations for matrices:
##
###########################

##  <#GAPDoc Label="IsZeroMatrix:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M" Name="IsZeroMatrix" Label="homalgTable entry"/>
##    <Returns><C>true</C> or <C>false</C></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>M</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>IsZeroMatrix</C> is bound then the standard method
##      for the property <Ref Prop="IsZero" Label="for matrices"/> shown below returns
##      <M>RP</M>!.<C>IsZeroMatrix</C><M>( <A>M</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( IsZero,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.IsZeroMatrix) then
        ## CAUTION: the external system must be able
        ## to check zero modulo possible ring relations!
        
        return RP!.IsZeroMatrix( M ); ## with this, \= can fall back to IsZero
    fi;
    
    #=====# the fallback method #=====#
    
    ## from the GAP4 documentation: ?Zero
    ## `ZeroSameMutability( <obj> )' is equivalent to `0 * <obj>'.
    
    return M = 0 * M; ## hence, by default, IsZero falls back to \= (see below)
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##----------------------
## the methods for Eval:
##----------------------

##  <#GAPDoc Label="Eval:IsInitialMatrix">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with HomalgInitialMatrix"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix <A>C</A> was created using
##      <Ref Meth="HomalgInitialMatrix" Label="constructor for initial matrices filled with zeros"/>
##      then the filter <C>IsInitialMatrix</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      (&see; <Ref Meth="InitialMatrix" Label="homalgTable entry for initial matrices"/>)
##      will be used to set the attribute <C>Eval</C> and resets the filter <C>IsInitialMatrix</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (IsInitialMatrix)",
        [ IsHomalgMatrix and IsInitialMatrix and
          HasNumberRows and HasNumberColumns ],
        
  function( C )
    local R, RP, z, zz;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    if IsBound( RP!.InitialMatrix ) then
        ResetFilterObj( C, IsInitialMatrix );
        SetEval( C, RP!.InitialMatrix( C ) );
        return Eval( C );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called InitialMatrix in the ",
               "homalgTable to evaluate a non-internal initial matrix\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    z := Zero( HomalgRing( C ) );
    
    ResetFilterObj( C, IsInitialMatrix );
    
    zz := ListWithIdenticalEntries( NumberColumns( C ), z );
    
    SetEval( C, homalgInternalMatrixHull( List( [ 1 .. NumberRows( C ) ], i -> ShallowCopy( zz ) ) ) );
    
    return Eval( C );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="InitialMatrix:homalgTable_entry">
##  <ManSection>
##    <Func Arg="C" Name="InitialMatrix" Label="homalgTable entry for initial matrices"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>InitialMatrix</C> is bound then the method
##      <Ref Meth="Eval" Label="for matrices created with HomalgInitialMatrix"/>
##      resets the filter <C>IsInitialMatrix</C> and returns <M>RP</M>!.<C>InitialMatrix</C><M>( <A>C</A> )</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:IsInitialIdentityMatrix">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with HomalgInitialIdentityMatrix"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix <A>C</A> was created using
##      <Ref Meth="HomalgInitialIdentityMatrix" Label="constructor for initial quadratic matrices with ones on the diagonal"/>
##      then the filter <C>IsInitialIdentityMatrix</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      (&see; <Ref Meth="InitialIdentityMatrix" Label="homalgTable entry for initial identity matrices"/>)
##      will be used to set the attribute <C>Eval</C> and resets the filter <C>IsInitialIdentityMatrix</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (IsInitialIdentityMatrix)",
        [ IsHomalgMatrix and IsInitialIdentityMatrix and
          HasNumberRows and HasNumberColumns ],
        
  function( C )
    local R, RP, o, z, zz, id;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    if IsBound( RP!.InitialIdentityMatrix ) then
        ResetFilterObj( C, IsInitialIdentityMatrix );
        SetEval( C, RP!.InitialIdentityMatrix( C ) );
        return Eval( C );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called InitialIdentityMatrix in the ",
               "homalgTable to evaluate a non-internal initial identity matrix\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    z := Zero( HomalgRing( C ) );
    o := One( HomalgRing( C ) );
    
    ResetFilterObj( C, IsInitialIdentityMatrix );
    
    zz := ListWithIdenticalEntries( NumberColumns( C ), z );
    
    id := List( [ 1 .. NumberRows( C ) ],
                function(i)
                  local z;
                  z := ShallowCopy( zz ); z[i] := o; return z;
                end );
    
    SetEval( C, homalgInternalMatrixHull( id ) );
    
    return Eval( C );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="InitialIdentityMatrix:homalgTable_entry">
##  <ManSection>
##    <Func Arg="C" Name="InitialIdentityMatrix" Label="homalgTable entry for initial identity matrices"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>InitialIdentityMatrix</C> is bound then the method
##      <Ref Meth="Eval" Label="for matrices created with HomalgInitialIdentityMatrix"/>
##      resets the filter <C>IsInitialIdentityMatrix</C> and returns <M>RP</M>!.<C>InitialIdentityMatrix</C><M>( <A>C</A> )</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##
InstallMethod( Eval,
        "for homalg matrices (HasEvalMatrixOperation)",
        [ IsHomalgMatrix and HasEvalMatrixOperation ],
        
  function( C )
    local func_arg;
    
    func_arg := EvalMatrixOperation( C );
    
    ResetFilterObj( C, HasEvalMatrixOperation );
    
    ## delete the component which was left over by GAP
    Unbind( C!.EvalMatrixOperation );
    
    return CallFuncList( func_arg[1], func_arg[2] );
    
end );

##  <#GAPDoc Label="Eval:HasEvalInvolution">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with Involution"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Meth="Involution" Label="for matrices"/>
##      then the filter <C>HasEvalInvolution</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      <Ref Meth="Involution" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalInvolution)",
        [ IsHomalgMatrix and HasEvalInvolution ],
        
  function( C )
    local R, RP, M;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    M :=  EvalInvolution( C );
    
    if IsBound(RP!.Involution) then
        return RP!.Involution( M );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called Involution ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    return homalgInternalMatrixHull( TransposedMat( Eval( M )!.matrix ) );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Involution:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M" Name="Involution" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>Involution</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with Involution"/> returns
##      <M>RP</M>!.<C>Involution</C> applied to the content of the attribute <C>EvalInvolution</C><M>( <A>C</A> ) = <A>M</A></M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:HasEvalTransposedMatrix">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with TransposedMatrix"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Meth="TransposedMatrix" Label="for matrices"/>
##      then the filter <C>HasEvalTransposedMatrix</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      <Ref Meth="TransposedMatrix" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalTransposedMatrix)",
        [ IsHomalgMatrix and HasEvalTransposedMatrix ],
        
  function( C )
    local R, RP, M;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    M :=  EvalTransposedMatrix( C );
    
    if IsBound(RP!.TransposedMatrix) then
        return RP!.TransposedMatrix( M );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called TransposedMatrix ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    return homalgInternalMatrixHull( TransposedMat( Eval( M )!.matrix ) );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="TransposedMatrix:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M" Name="TransposedMatrix" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>TransposedMatrix</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with TransposedMatrix"/> returns
##      <M>RP</M>!.<C>TransposedMatrix</C> applied to the content of the attribute <C>EvalTransposedMatrix</C><M>( <A>C</A> ) = <A>M</A></M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:HasEvalCoercedMatrix">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with CoercedMatrix"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Meth="CoercedMatrix" Label="copy a matrix over a different ring"/>
##      then the filter <C>HasEvalCoercedMatrix</C> for <A>C</A> is set to true and the <C>Eval</C> value
##      of a copy of <C>EvalCoercedMatrix(</C><A>C</A><C>)</C> in <C>HomalgRing(</C><A>C</A><C>)</C>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalCoercedMatrix)",
        [ IsHomalgMatrix and HasEvalCoercedMatrix ],
        
  function( C )
    local R, RP, m;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    m := EvalCoercedMatrix( C );
    
    # delegate to the non-lazy coercening
    return Eval( R * m );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:HasEvalCertainRows">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with CertainRows"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Meth="CertainRows" Label="for matrices"/>
##      then the filter <C>HasEvalCertainRows</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      <Ref Meth="CertainRows" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalCertainRows)",
        [ IsHomalgMatrix and HasEvalCertainRows ],
        
  function( C )
    local R, RP, e, M, plist;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    e :=  EvalCertainRows( C );
    
    M := e[1];
    plist := e[2];
    
    ResetFilterObj( C, HasEvalCertainRows );
    
    ## delete the component which was left over by GAP
    Unbind( C!.EvalCertainRows );
    
    if IsBound(RP!.CertainRows) then
        return RP!.CertainRows( M, plist );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called CertainRows ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    return homalgInternalMatrixHull( Eval( M )!.matrix{ plist } );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="CertainRows:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M, plist" Name="CertainRows" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>CertainRows</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with CertainRows"/> returns
##      <M>RP</M>!.<C>CertainRows</C> applied to the content of the attribute
##      <C>EvalCertainRows</C><M>( <A>C</A> ) = [ <A>M</A>, <A>plist</A> ]</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:HasEvalCertainColumns">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with CertainColumns"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Meth="CertainColumns" Label="for matrices"/>
##      then the filter <C>HasEvalCertainColumns</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      <Ref Meth="CertainColumns" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalCertainColumns)",
        [ IsHomalgMatrix and HasEvalCertainColumns ],
        
  function( C )
    local R, RP, e, M, plist;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    e :=  EvalCertainColumns( C );
    
    M := e[1];
    plist := e[2];
    
    ResetFilterObj( C, HasEvalCertainColumns );
    
    ## delete the component which was left over by GAP
    Unbind( C!.EvalCertainColumns );
    
    if IsBound(RP!.CertainColumns) then
        return RP!.CertainColumns( M, plist );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called CertainColumns ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    return homalgInternalMatrixHull(
                   Eval( M )!.matrix{[ 1 .. NumberRows( M ) ]}{plist} );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="CertainColumns:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M, plist" Name="CertainColumns" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>CertainColumns</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with CertainColumns"/> returns
##      <M>RP</M>!.<C>CertainColumns</C> applied to the content of the attribute
##      <C>EvalCertainColumns</C><M>( <A>C</A> ) = [ <A>M</A>, <A>plist</A> ]</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:HasEvalUnionOfRows">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with UnionOfRows"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Func="UnionOfRows" Label="for a homalg ring, an integer and a list of homalg matrices"/>
##      then the filter <C>HasEvalUnionOfRows</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      <Ref Meth="UnionOfRows" Label="homalgTable entry"/> or the <C>homalgTable</C> function
##      <Ref Meth="UnionOfRowsPair" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalUnionOfRows)",
        [ IsHomalgMatrix and HasEvalUnionOfRows ],
        
  function( C )
    local R, RP, e, i, combine;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    # Make it mutable
    e := ShallowCopy( EvalUnionOfRows( C ) );
    
    # In case of nested UnionOfRows, we try to avoid
    # recursion, since the gap stack is rather small
    # additionally unpack PreEvals
    i := 1;
    while i <= Length( e ) do
        
        if HasPreEval( e[i] ) and not HasEval( e[i] ) then
            
            e[i] := PreEval( e[i] );
            
        elif HasEvalUnionOfRows( e[i] ) and not HasEval( e[i] ) then
            
            e := Concatenation( e{[ 1 .. (i-1) ]}, EvalUnionOfRows( e[i] ), e{[ (i+1) .. Length( e ) ]}  );
            
        else
            
            i := i + 1;
            
        fi;
        
    od;
    
    # Combine zero matrices
    i := 1;
    while i + 1 <= Length( e ) do
        
        if HasIsZero( e[i] ) and IsZero( e[i] ) and HasIsZero( e[i+1] ) and IsZero( e[i+1] ) then
            
            e[i] := HomalgZeroMatrix( NumberRows( e[i] ) + NumberRows( e[i+1] ), NumberColumns( e[i] ), HomalgRing( e[i] ) );
            
            Remove( e, i + 1 );
            
        else
            
            i := i + 1;
            
        fi;
        
    od;
    
    # After combining zero matrices only a single one might be left
    if Length( e ) = 1 then
        
        return e[1];
        
    fi;
    
    # Use RP!.UnionOfRows if available
    if IsBound(RP!.UnionOfRows) then
        
        return RP!.UnionOfRows( e );
        
    fi;
    
    # Fall back to RP!.UnionOfRowsPair or manual fallback for internal matrices
    # Combine the matrices
    # Use a balanced binary tree to keep the sizes small (heuristically)
    # to avoid a huge memory footprint
    
    if not IsBound(RP!.UnionOfRowsPair) and not IsHomalgInternalMatrixRep( C ) then
        Error( "could neither find a procedure called UnionOfRows ",
               "nor a procedure called UnionOfRowsPair ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    combine := function( A, B )
      local result, U;
        
        if IsBound(RP!.UnionOfRowsPair) then
            
            result := RP!.UnionOfRowsPair( A, B );
            
        else
            
            #=====# can only work for homalg internal matrices #=====#
            
            U := ShallowCopy( Eval( A )!.matrix );
            
            U{ [ NumberRows( A ) + 1 .. NumberRows( A ) + NumberRows( B ) ] } := Eval( B )!.matrix;
            
            result := homalgInternalMatrixHull( U );
            
        fi;
        
        return HomalgMatrixWithAttributes( [
                    Eval, result,
                    EvalUnionOfRows, [ A, B ],
                    NumberRows, NumberRows( A ) + NumberRows( B ),
                    NumberColumns, NumberColumns( A ),
                    ], R );
        
    end;
    
    while Length( e ) > 1 do
        
        for i in [ 1 .. Int( Length( e ) / 2 ) ] do
            
            e[ 2 * i - 1 ] := combine( e[ 2 * i - 1 ], e[ 2 * i ] );
            Unbind( e[ 2 * i ] );
            
        od;
        
        e := Compacted( e );
        
    od;
    
    return Eval( e[1] );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="UnionOfRows:homalgTable_entry">
##  <ManSection>
##    <Func Arg="L" Name="UnionOfRows" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>UnionOfRows</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with UnionOfRows"/> returns
##      <M>RP</M>!.<C>UnionOfRows</C> applied to the content of the attribute
##      <C>EvalUnionOfRows</C><M>( <A>C</A> ) = <A>L</A></M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="UnionOfRowsPair:homalgTable_entry">
##  <ManSection>
##    <Func Arg="A, B" Name="UnionOfRowsPair" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>UnionOfRowsPair</C> is bound
##      and the <C>homalgTable</C> component <M>RP</M>!.<C>UnionOfRows</C> is not bound then
##      the method <Ref Meth="Eval" Label="for matrices created with UnionOfRows"/> returns
##      <M>RP</M>!.<C>UnionOfRowsPair</C> applied recursively to a balanced binary tree created from
##      the content of the attribute <C>EvalUnionOfRows</C><M>( <A>C</A> )</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:HasEvalUnionOfColumns">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with UnionOfColumns"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Func="UnionOfColumns" Label="for a homalg ring, an integer and a list of homalg matrices"/>
##      then the filter <C>HasEvalUnionOfColumns</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      <Ref Meth="UnionOfColumns" Label="homalgTable entry"/> or the <C>homalgTable</C> function
##      <Ref Meth="UnionOfColumnsPair" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalUnionOfColumns)",
        [ IsHomalgMatrix and HasEvalUnionOfColumns ],
        
  function( C )
    local R, RP, e, i, combine;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    # Make it mutable
    e := ShallowCopy( EvalUnionOfColumns( C ) );
    
    # In case of nested UnionOfColumns, we try to avoid
    # recursion, since the gap stack is rather small
    # additionally unpack PreEvals
    i := 1;
    while i <= Length( e ) do
        
        if HasPreEval( e[i] ) and not HasEval( e[i] ) then
            
            e[i] := PreEval( e[i] );
            
        elif HasEvalUnionOfColumns( e[i] ) and not HasEval( e[i] ) then
            
            e := Concatenation( e{[ 1 .. (i-1) ]}, EvalUnionOfColumns( e[i] ), e{[ (i+1) .. Length( e ) ]}  );
            
        else
            
            i := i + 1;
            
        fi;
        
    od;
    
    # Combine zero matrices
    i := 1;
    while i + 1 <= Length( e ) do
        
        if HasIsZero( e[i] ) and IsZero( e[i] ) and HasIsZero( e[i+1] ) and IsZero( e[i+1] ) then
            
            e[i] := HomalgZeroMatrix( NumberRows( e[i] ), NumberColumns( e[i] ) + NumberColumns( e[i+1] ), HomalgRing( e[i] ) );
            
            Remove( e, i + 1 );
            
        else
            
            i := i + 1;
            
        fi;
        
    od;
    
    # After combining zero matrices only a single one might be left
    if Length( e ) = 1 then
        
        return e[1];
        
    fi;
    
    # Use RP!.UnionOfColumns if available
    if IsBound(RP!.UnionOfColumns) then
        
        return RP!.UnionOfColumns( e );
        
    fi;
    
    # Fall back to RP!.UnionOfColumnsPair or manual fallback for internal matrices
    # Combine the matrices
    # Use a balanced binary tree to keep the sizes small (heuristically)
    # to avoid a huge memory footprint
    
    if not IsBound(RP!.UnionOfColumnsPair) and not IsHomalgInternalMatrixRep( C ) then
        Error( "could neither find a procedure called UnionOfColumns ",
               "nor a procedure called UnionOfColumnsPair ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    combine := function( A, B )
      local result, U;
        
        if IsBound(RP!.UnionOfColumnsPair) then
            
            result := RP!.UnionOfColumnsPair( A, B );
            
        else
            
            #=====# can only work for homalg internal matrices #=====#
            
            U := List( Eval( A )!.matrix, ShallowCopy );
            
            U{ [ 1 .. NumberRows( A ) ] }
              { [ NumberColumns( A ) + 1 .. NumberColumns( A ) + NumberColumns( B ) ] }
              := Eval( B )!.matrix;
            
            result := homalgInternalMatrixHull( U );
            
        fi;
        
        return HomalgMatrixWithAttributes( [
                    Eval, result,
                    EvalUnionOfColumns, [ A, B ],
                    NumberRows, NumberRows( A ),
                    NumberColumns, NumberColumns( A ) + NumberColumns( B )
                    ], R );
        
    end;
    
    while Length( e ) > 1 do
        
        for i in [ 1 .. Int( Length( e ) / 2 ) ] do
            
            e[ 2 * i - 1 ] := combine( e[ 2 * i - 1 ], e[ 2 * i ] );
            Unbind( e[ 2 * i ] );
            
        od;
        
        e := Compacted( e );
        
    od;
    
    return Eval( e[1] );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="UnionOfColumns:homalgTable_entry">
##  <ManSection>
##    <Func Arg="L" Name="UnionOfColumns" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>UnionOfColumns</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with UnionOfColumns"/> returns
##      <M>RP</M>!.<C>UnionOfColumns</C> applied to the content of the attribute
##      <C>EvalUnionOfColumns</C><M>( <A>C</A> ) = <A>L</A></M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="UnionOfColumnsPair:homalgTable_entry">
##  <ManSection>
##    <Func Arg="A, B" Name="UnionOfColumnsPair" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>UnionOfColumnsPair</C> is bound
##      and the <C>homalgTable</C> component <M>RP</M>!.<C>UnionOfColumns</C> is not bound then
##      the method <Ref Meth="Eval" Label="for matrices created with UnionOfColumns"/> returns
##      <M>RP</M>!.<C>UnionOfColumnsPair</C> applied recursively to a balanced binary tree created from
##      the content of the attribute <C>EvalUnionOfRows</C><M>( <A>C</A> )</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:HasEvalDiagMat">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with DiagMat"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Meth="DiagMat" Label="for a homalg ring and a list of homalg matrices"/>
##      then the filter <C>HasEvalDiagMat</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      <Ref Meth="DiagMat" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalDiagMat)",
        [ IsHomalgMatrix and HasEvalDiagMat ],
        
  function( C )
    local R, RP, e, l, z, m, n, diag, mat;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    e :=  EvalDiagMat( C );
    
    if IsBound(RP!.DiagMat) then
        return RP!.DiagMat( e );
    fi;
    
    l := Length( e );
    
    if not IsHomalgInternalMatrixRep( C ) then
        return UnionOfRows(
                       List( [ 1 .. l ],
                             i -> UnionOfColumns(
                                     List( [ 1 .. l ],
                                           function( j )
                                             if i = j then
                                                 return e[i];
                                             fi;
                                             return HomalgZeroMatrix( NumberRows( e[i] ), NumberColumns( e[j] ), R );
                                           end )
                                     )
                             )
                       );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    z := Zero( R );
    
    m := Sum( List( e, NumberRows ) );
    n := Sum( List( e, NumberColumns ) );
    
    diag := List( [ 1 .. m ], a -> List( [ 1 .. n ], b -> z ) );
    
    m := 0;
    n := 0;
    
    for mat in e do
        diag{ [ m + 1 .. m + NumberRows( mat ) ] }{ [ n + 1 .. n + NumberColumns( mat ) ] }
          := Eval( mat )!.matrix;
        
        m := m + NumberRows( mat );
        n := n + NumberColumns( mat );
    od;
    
    return homalgInternalMatrixHull( diag );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="DiagMat:homalgTable_entry">
##  <ManSection>
##    <Func Arg="e" Name="DiagMat" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>DiagMat</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with DiagMat"/> returns
##      <M>RP</M>!.<C>DiagMat</C> applied to the content of the attribute
##      <C>EvalDiagMat</C><M>( <A>C</A> ) = <A>e</A></M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:HasEvalKroneckerMat">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with KroneckerMat"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Meth="KroneckerMat" Label="for matrices"/>
##      then the filter <C>HasEvalKroneckerMat</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      <Ref Meth="KroneckerMat" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalKroneckerMat)",
        [ IsHomalgMatrix and HasEvalKroneckerMat ],
        
  function( C )
    local R, RP, A, B;
    
    R := HomalgRing( C );
    
    if ( HasIsCommutative( R ) and not IsCommutative( R ) ) and
       ( HasIsSuperCommutative( R ) and not IsSuperCommutative( R ) ) then
        Info( InfoWarning, 1, "\033[01m\033[5;31;47m",
              "the Kronecker product is only defined for (super) commutative rings!",
              "\033[0m" );
    fi;
    
    RP := homalgTable( R );
    
    A :=  EvalKroneckerMat( C )[1];
    B :=  EvalKroneckerMat( C )[2];
    
    if IsBound(RP!.KroneckerMat) then
        return RP!.KroneckerMat( A, B );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called KroneckerMat ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    return homalgInternalMatrixHull(
                   KroneckerProduct( Eval( A )!.matrix, Eval( B )!.matrix ) );
    ## this was easy, thanks GAP :)
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="KroneckerMat:homalgTable_entry">
##  <ManSection>
##    <Func Arg="A, B" Name="KroneckerMat" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>KroneckerMat</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with KroneckerMat"/> returns
##      <M>RP</M>!.<C>KroneckerMat</C> applied to the content of the attribute
##      <C>EvalKroneckerMat</C><M>( <A>C</A> ) = [ <A>A</A>, <A>B</A> ]</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:HasEvalDualKroneckerMat">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with DualKroneckerMat"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Meth="DualKroneckerMat" Label="for matrices"/>
##      then the filter <C>HasEvalDualKroneckerMat</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      <Ref Meth="DualKroneckerMat" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalDualKroneckerMat)",
        [ IsHomalgMatrix and HasEvalDualKroneckerMat ],
        
  function( C )
    local R, RP, A, B;
    
    R := HomalgRing( C );
    
    if ( HasIsCommutative( R ) and not IsCommutative( R ) ) and
       ( HasIsSuperCommutative( R ) and not IsSuperCommutative( R ) ) then
        Info( InfoWarning, 1, "\033[01m\033[5;31;47m",
              "the dual Kronecker product is only defined for (super) commutative rings!",
              "\033[0m" );
    fi;
    
    RP := homalgTable( R );
    
    A :=  EvalDualKroneckerMat( C )[1];
    B :=  EvalDualKroneckerMat( C )[2];
    
    # work around errors in Singular when taking the opposite ring of a ring with ordering lp
    # https://github.com/Singular/Singular/issues/1011
    # fixed in version 4.2.0
    if IsBound(RP!.DualKroneckerMat) and not (
        IsBound( R!.ring ) and
        IsBound( R!.ring!.stream ) and
        IsBound( R!.ring!.stream.cas ) and R!.ring!.stream.cas = "singular" and
        ( not IsBound( R!.ring!.stream.version ) or R!.ring!.stream.version < 4200 ) and
        IsBound( R!.order ) and IsString( R!.order ) and StartsWith( R!.order, "lex" )
    ) then
        
        return RP!.DualKroneckerMat( A, B );
        
    fi;
    
    if HasIsCommutative( R ) and IsCommutative( R ) then
        
        return Eval( KroneckerMat( B, A ) );
        
    else
        
        return Eval(
            TransposedMatrix( Involution(
                KroneckerMat( TransposedMatrix( Involution( B ) ), TransposedMatrix( Involution( A ) ) )
            ) )
        );
        
    fi;
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="DualKroneckerMat:homalgTable_entry">
##  <ManSection>
##    <Func Arg="A, B" Name="DualKroneckerMat" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>DualKroneckerMat</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with DualKroneckerMat"/> returns
##      <M>RP</M>!.<C>DualKroneckerMat</C> applied to the content of the attribute
##      <C>EvalDualKroneckerMat</C><M>( <A>C</A> ) = [ <A>A</A>, <A>B</A> ]</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:HasEvalMulMat">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with MulMat"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Meth="\*" Label="for ring elements and matrices"/>
##      then the filter <C>HasEvalMulMat</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      <Ref Meth="MulMat" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalMulMat)",
        [ IsHomalgMatrix and HasEvalMulMat ],
        
  function( C )
    local R, RP, e, a, A;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    e :=  EvalMulMat( C );
    
    a := e[1];
    A := e[2];
    
    if IsBound(RP!.MulMat) then
        return RP!.MulMat( a, A );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called MulMat ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    return a * Eval( A );
    
end );

InstallMethod( Eval,
        "for homalg matrices (HasEvalMulMatRight)",
        [ IsHomalgMatrix and HasEvalMulMatRight ],
        
  function( C )
    local R, RP, e, A, a;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    e :=  EvalMulMatRight( C );
    
    A := e[1];
    a := e[2];
    
    if IsBound(RP!.MulMatRight) then
        return RP!.MulMatRight( A, a );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called MulMatRight ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    return Eval( A ) * a;
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="MulMat:homalgTable_entry">
##  <ManSection>
##    <Func Arg="a, A" Name="MulMat" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>MulMat</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with MulMat"/> returns
##      <M>RP</M>!.<C>MulMat</C> applied to the content of the attribute
##      <C>EvalMulMat</C><M>( <A>C</A> ) = [ <A>a</A>, <A>A</A> ]</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:HasEvalAddMat">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with AddMat"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Meth="\+" Label="for matrices"/>
##      then the filter <C>HasEvalAddMat</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      <Ref Meth="AddMat" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalAddMat)",
        [ IsHomalgMatrix and HasEvalAddMat ],
        
  function( C )
    local R, RP, e, A, B;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    e :=  EvalAddMat( C );
    
    A := e[1];
    B := e[2];
    
    ResetFilterObj( C, HasEvalAddMat );
    
    ## delete the component which was left over by GAP
    Unbind( C!.EvalAddMat );
    
    if IsBound(RP!.AddMat) then
        return RP!.AddMat( A, B );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called AddMat ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    return Eval( A ) + Eval( B );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="AddMat:homalgTable_entry">
##  <ManSection>
##    <Func Arg="A, B" Name="AddMat" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>AddMat</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with AddMat"/> returns
##      <M>RP</M>!.<C>AddMat</C> applied to the content of the attribute
##      <C>EvalAddMat</C><M>( <A>C</A> ) = [ <A>A</A>, <A>B</A> ]</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:HasEvalSubMat">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with SubMat"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Meth="\-" Label="for matrices"/>
##      then the filter <C>HasEvalSubMat</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      <Ref Meth="SubMat" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalSubMat)",
        [ IsHomalgMatrix and HasEvalSubMat ],
        
  function( C )
    local R, RP, e, A, B;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    e :=  EvalSubMat( C );
    
    A := e[1];
    B := e[2];
    
    ResetFilterObj( C, HasEvalSubMat );
    
    ## delete the component which was left over by GAP
    Unbind( C!.EvalSubMat );
    
    if IsBound(RP!.SubMat) then
        return RP!.SubMat( A, B );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called SubMat ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    return Eval( A ) - Eval( B );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="SubMat:homalgTable_entry">
##  <ManSection>
##    <Func Arg="A, B" Name="SubMat" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>SubMat</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with SubMat"/> returns
##      <M>RP</M>!.<C>SubMat</C> applied to the content of the attribute
##      <C>EvalSubMat</C><M>( <A>C</A> ) = [ <A>A</A>, <A>B</A> ]</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:HasEvalCompose">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with Compose"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Meth="\*" Label="for composable matrices"/>
##      then the filter <C>HasEvalCompose</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      <Ref Meth="Compose" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalCompose)",
        [ IsHomalgMatrix and HasEvalCompose ],
        
  function( C )
    local R, RP, e, A, B;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    e :=  EvalCompose( C );
    
    A := e[1];
    B := e[2];
    
    ResetFilterObj( C, HasEvalCompose );
    
    ## delete the component which was left over by GAP
    Unbind( C!.EvalCompose );
    
    if IsBound(RP!.Compose) then
        return RP!.Compose( A, B );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called Compose ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    return Eval( A ) * Eval( B );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Compose:homalgTable_entry">
##  <ManSection>
##    <Func Arg="A, B" Name="Compose" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>Compose</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with Compose"/> returns
##      <M>RP</M>!.<C>Compose</C> applied to the content of the attribute
##      <C>EvalCompose</C><M>( <A>C</A> ) = [ <A>A</A>, <A>B</A> ]</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:IsIdentityMatrix">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with HomalgIdentityMatrix"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix <A>C</A> was created using
##      <Ref Meth="HomalgIdentityMatrix" Label="constructor for identity matrices"/>
##      then the filter <C>IsOne</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      (&see; <Ref Meth="IdentityMatrix" Label="homalgTable entry"/>)
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (IsOne)",
        [ IsHomalgMatrix and IsOne and HasNumberRows and HasNumberColumns ], 10,
        
  function( C )
    local R, id, RP, o, z, zz;
    
    R := HomalgRing( C );
    
    if IsBound( R!.IdentityMatrices ) then
        id := ElmWPObj( R!.IdentityMatrices!.weak_pointers, NumberColumns( C ) );
        if id <> fail then
            R!.IdentityMatrices!.cache_hits := R!.IdentityMatrices!.cache_hits + 1;
            return id;
        fi;
        ## we do not count cache_misses as it is equivalent to counter
    fi;
    
    RP := homalgTable( R );
    
    if IsBound( RP!.IdentityMatrix ) then
        id := RP!.IdentityMatrix( C );
        SetElmWPObj( R!.IdentityMatrices!.weak_pointers, NumberColumns( C ), id );
        R!.IdentityMatrices!.counter := R!.IdentityMatrices!.counter + 1;
        return id;
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called IdentityMatrix ",
               "homalgTable to evaluate a non-internal identity matrix\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    z := Zero( HomalgRing( C ) );
    o := One( HomalgRing( C ) );
    
    zz := ListWithIdenticalEntries( NumberColumns( C ), z );
    
    id := List( [ 1 .. NumberRows( C ) ],
                function(i)
                  local z;
                  z := ShallowCopy( zz ); z[i] := o; return z;
                end );
    
    id := homalgInternalMatrixHull( id );
    
    SetElmWPObj( R!.IdentityMatrices!.weak_pointers, NumberColumns( C ), id );
    
    return id;
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="IdentityMatrix:homalgTable_entry">
##  <ManSection>
##    <Func Arg="C" Name="IdentityMatrix" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>IdentityMatrix</C> is bound then the method
##      <Ref Meth="Eval" Label="for matrices created with HomalgIdentityMatrix"/> returns
##      <M>RP</M>!.<C>IdentityMatrix</C><M>( <A>C</A> )</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Eval:IsZeroMatrix">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with HomalgZeroMatrix"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix <A>C</A> was created using
##      <Ref Meth="HomalgZeroMatrix" Label="constructor for zero matrices"/>
##      then the filter <C>IsZeroMatrix</C> for <A>C</A> is set to true and the <C>homalgTable</C> function
##      (&see; <Ref Meth="ZeroMatrix" Label="homalgTable entry"/>)
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (IsZero)",
        [ IsHomalgMatrix and IsZero and HasNumberRows and HasNumberColumns ], 40,
        
  function( C )
    local R, RP, z;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    if ( NumberRows( C ) = 0 or NumberColumns( C ) = 0 ) and
       not ( IsBound( R!.SafeToEvaluateEmptyMatrices ) and
             R!.SafeToEvaluateEmptyMatrices = true ) then
        Info( InfoWarning, 1, "\033[01m\033[5;31;47m",
              "an empty matrix is about to get evaluated!",
              "\033[0m" );
    fi;
    
    if IsBound( RP!.ZeroMatrix ) then
        return RP!.ZeroMatrix( C );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called ZeroMatrix ",
               "homalgTable to evaluate a non-internal zero matrix\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    z := Zero( HomalgRing( C ) );
    
    ## copying the rows saves memory;
    ## we assume that the entries are never modified!!!
    return homalgInternalMatrixHull(
                   ListWithIdenticalEntries( NumberRows( C ),
                           ListWithIdenticalEntries( NumberColumns( C ), z ) ) );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="ZeroMatrix:homalgTable_entry">
##  <ManSection>
##    <Func Arg="C" Name="ZeroMatrix" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>ZeroMatrix</C> is bound then the method
##      <Ref Meth="Eval" Label="for matrices created with HomalgZeroMatrix"/> returns
##      <M>RP</M>!.<C>ZeroMatrix</C><M>( <A>C</A> )</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="NumberRows:homalgTable_entry">
##  <ManSection>
##    <Func Arg="C" Name="NumberRows" Label="homalgTable entry"/>
##    <Returns>a nonnegative integer</Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>NumberRows</C> is bound then the standard method
##      for the attribute <Ref Attr="NumberRows"/> shown below returns
##      <M>RP</M>!.<C>NumberRows</C><M>( <A>C</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( NumberRows,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( C )
    local R, RP;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.NumberRows) then
        return RP!.NumberRows( C );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called NumberRows ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    return Length( Eval( C )!.matrix );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="NumberColumns:homalgTable_entry">
##  <ManSection>
##    <Func Arg="C" Name="NumberColumns" Label="homalgTable entry"/>
##    <Returns>a nonnegative integer</Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>NumberColumns</C> is bound then the standard method
##      for the attribute <Ref Attr="NumberColumns"/> shown below returns
##      <M>RP</M>!.<C>NumberColumns</C><M>( <A>C</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( NumberColumns,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( C )
    local R, RP;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.NumberColumns) then
        return RP!.NumberColumns( C );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called NumberColumns ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    return Length( Eval( C )!.matrix[ 1 ] );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Determinant:homalgTable_entry">
##  <ManSection>
##    <Func Arg="C" Name="Determinant" Label="homalgTable entry"/>
##    <Returns>a ring element</Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>Determinant</C> is bound then the standard method
##      for the attribute <Ref Attr="DeterminantMat"/> shown below returns
##      <M>RP</M>!.<C>Determinant</C><M>( <A>C</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( DeterminantMat,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( C )
    local R, RP;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    if NumberRows( C ) <> NumberColumns( C ) then
        Error( "the matrix is not a square matrix\n" );
    fi;
    
    if IsEmptyMatrix( C ) then
        return One( R );
    elif IsZero( C ) then
        return Zero( R );
    fi;
    
    if IsBound(RP!.Determinant) then
        return RingElementConstructor( R )( RP!.Determinant( C ), R );
    fi;
    
    if not IsHomalgInternalMatrixRep( C ) then
        Error( "could not find a procedure called Determinant ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    #=====# can only work for homalg internal matrices #=====#
    
    return Determinant( Eval( C )!.matrix );
    
end );

##
InstallMethod( Determinant,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( C )
    
    return DeterminantMat( C );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

####################################
#
# methods for operations (you probably want to replace for an external CAS):
#
####################################

##
InstallMethod( IsUnit,
        "for homalg ring elements",
        [ IsHomalgRing, IsRingElement ], 100,
        
  function( R, r )
    local RP;
    
    if HasIsZero( r ) and IsZero( r ) then
        return false;
    elif HasIsOne( r ) and IsOne( r ) then
        return true;
    fi;
    
    return not IsBool( LeftInverse( HomalgMatrix( [ r ], 1, 1, R ) ) );
    
end );

##
InstallMethod( IsUnit,
        "for homalg ring elements",
        [ IsHomalgRing, IsHomalgRingElement ], 100,
        
  function( R, r )
    local RP;
    
    if HasIsZero( r ) and IsZero( r ) then
        return false;
    elif HasIsOne( r ) and IsOne( r ) then
        return true;
    elif HasIsMinusOne( r ) and IsMinusOne( r ) then
        return true;
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.IsUnit) then
        return RP!.IsUnit( R, r );
    fi;
    
    #=====# the fallback method #=====#
    
    return not IsBool( LeftInverse( HomalgMatrix( [ r ], 1, 1, R ) ) );
    
end );

##
InstallMethod( IsUnit,
        "for homalg ring elements",
        [ IsHomalgInternalRingRep, IsRingElement ], 100,
        
  function( R, r )
    
    return IsUnit( R!.ring, r );
    
end );

##
InstallMethod( IsUnit,
        "for homalg ring elements",
        [ IsHomalgRingElement ],
        
  function( r )
    
    if HasIsZero( r ) and IsZero( r ) then
        return false;
    elif HasIsOne( r ) and IsOne( r ) then
        return true;
    elif HasIsMinusOne( r ) and IsMinusOne( r ) then
        return true;
    fi;
    
    if not IsBound( r!.IsUnit ) then
        r!.IsUnit := IsUnit( HomalgRing( r ), r );
    fi;
    
    return r!.IsUnit;
    
end );

##  <#GAPDoc Label="ZeroRows:homalgTable_entry">
##  <ManSection>
##    <Func Arg="C" Name="ZeroRows" Label="homalgTable entry"/>
##    <Returns>a (possibly empty) list of positive integers</Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>ZeroRows</C> is bound then the standard method
##      of the attribute <Ref Attr="ZeroRows"/> shown below returns
##      <M>RP</M>!.<C>ZeroRows</C><M>( <A>C</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( ZeroRows,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( C )
    local R, RP, z;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.ZeroRows) then
        return RP!.ZeroRows( C );
    fi;
    
    #=====# the fallback method #=====#
    
    z := HomalgZeroMatrix( 1, NumberColumns( C ), R );
    
    return Filtered( [ 1 .. NumberRows( C ) ], a -> CertainRows( C, [ a ] ) = z );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="ZeroColumns:homalgTable_entry">
##  <ManSection>
##    <Func Arg="C" Name="ZeroColumns" Label="homalgTable entry"/>
##    <Returns>a (possibly empty) list of positive integers</Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>ZeroColumns</C> is bound then the standard method
##      of the attribute <Ref Attr="ZeroColumns"/> shown below returns
##      <M>RP</M>!.<C>ZeroColumns</C><M>( <A>C</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( ZeroColumns,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( C )
    local R, RP, z;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.ZeroColumns) then
        return RP!.ZeroColumns( C );
    fi;
    
    #=====# the fallback method #=====#
    
    z := HomalgZeroMatrix( NumberRows( C ), 1, R );
    
    return Filtered( [ 1 .. NumberColumns( C ) ], a -> CertainColumns( C, [ a ] ) = z );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##
InstallMethod( GetRidOfObsoleteRows,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( C )
    local R, RP, M;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.GetRidOfObsoleteRows) then
        M := HomalgMatrix( RP!.GetRidOfObsoleteRows( C ), R );
        if HasNumberColumns( C ) then
            SetNumberColumns( M, NumberColumns( C ) );
        fi;
        SetZeroRows( M, [ ] );
        return M;
    fi;
    
    #=====# the fallback method #=====#
    
    ## get rid of zero rows
    ## (e.g. those rows containing the ring relations)
    
    M := CertainRows( C, NonZeroRows( C ) );
    
    SetZeroRows( M, [ ] );
    
    ## forgetting C may save memory
    if HasEvalCertainRows( M ) then
        if not IsEmptyMatrix( M ) then
            Eval( M );
        fi;
        ResetFilterObj( M, HasEvalCertainRows );
        Unbind( M!.EvalCertainRows );
    fi;
    
    return M;
    
end );

##
InstallMethod( GetRidOfObsoleteColumns,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( C )
    local R, RP, M;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.GetRidOfObsoleteColumns) then
        M := HomalgMatrix( RP!.GetRidOfObsoleteColumns( C ), R );
        if HasNumberRows( C ) then
            SetNumberRows( M, NumberRows( C ) );
        fi;
        SetZeroColumns( M, [ ] );
        return M;
    fi;
    
    #=====# the fallback method #=====#
    
    ## get rid of zero columns
    ## (e.g. those columns containing the ring relations)
    
    M := CertainColumns( C, NonZeroColumns( C ) );
    
    SetZeroColumns( M, [ ] );
    
    ## forgetting C may save memory
    if HasEvalCertainColumns( M ) then
        if not IsEmptyMatrix( M ) then
            Eval( M );
        fi;
        ResetFilterObj( M, HasEvalCertainColumns );
        Unbind( M!.EvalCertainColumns );
    fi;
    
    return M;
    
end );

##  <#GAPDoc Label="AreEqualMatrices:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M1,M2" Name="AreEqualMatrices" Label="homalgTable entry"/>
##    <Returns><C>true</C> or <C>false</C></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>M1</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>AreEqualMatrices</C> is bound then the standard method
##      for the operation <Ref Oper="\=" Label="for matrices"/> shown below returns
##      <M>RP</M>!.<C>AreEqualMatrices</C><M>( <A>M1</A>, <A>M2</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( \=,
        "for homalg comparable matrices",
        [ IsHomalgMatrix, IsHomalgMatrix ],
        
  function( M1, M2 )
    local R, RP, are_equal;
    
    ## do not touch mutable matrices
    if not ( IsMutable( M1 ) or IsMutable( M2 ) ) then
        
        if IsBound( M1!.AreEqual ) then
            are_equal := _ElmWPObj_ForHomalg( M1!.AreEqual, M2, fail );
            if are_equal <> fail then
                return are_equal;
            fi;
        else
            M1!.AreEqual :=
              ContainerForWeakPointers(
                      TheTypeContainerForWeakPointersOnComputedValues,
                      [ "operation", "AreEqual" ] );
        fi;
        
        if IsBound( M2!.AreEqual ) then
            are_equal := _ElmWPObj_ForHomalg( M2!.AreEqual, M1, fail );
            if are_equal <> fail then
                return are_equal;
            fi;
        fi;
        ## do not store things symmetrically below to ``save'' memory
        
    fi;
    
    R := HomalgRing( M1 );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.AreEqualMatrices) then
        ## CAUTION: the external system must be able to check equality
        ## modulo possible ring relations (known to the external system)!
        are_equal := RP!.AreEqualMatrices( M1, M2 );
    elif IsBound(RP!.Equal) then
        ## CAUTION: the external system must be able to check equality
        ## modulo possible ring relations (known to the external system)!
        are_equal := RP!.Equal( M1, M2 );
    elif IsBound(RP!.IsZeroMatrix) then   ## ensuring this avoids infinite loops
        are_equal := IsZero( M1 - M2 );
    fi;
    
    if IsBound( are_equal ) then
        
        ## do not touch mutable matrices
        if not ( IsMutable( M1 ) or IsMutable( M2 ) ) then
            
            if are_equal then
                MatchPropertiesAndAttributes( M1, M2,
                        LIMAT.intrinsic_properties,
                        LIMAT.intrinsic_attributes,
                        LIMAT.intrinsic_components,
                        LIMAT.intrinsic_attributes_do_not_check_their_equality
                        );
            fi;
            
            ## do not store things symmetrically to ``save'' memory
            _AddTwoElmWPObj_ForHomalg( M1!.AreEqual, M2, are_equal );
            
        fi;
        
        return are_equal;
    fi;
    
    TryNextMethod( );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="IsIdentityMatrix:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M" Name="IsIdentityMatrix" Label="homalgTable entry"/>
##    <Returns><C>true</C> or <C>false</C></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>M</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>IsIdentityMatrix</C> is bound then the standard method
##      for the property <Ref Prop="IsOne"/> shown below returns
##      <M>RP</M>!.<C>IsIdentityMatrix</C><M>( <A>M</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( IsOne,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP;
    
    if NumberRows( M ) <> NumberColumns( M ) then
        return false;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.IsIdentityMatrix) then
        return RP!.IsIdentityMatrix( M );
    fi;
    
    #=====# the fallback method #=====#
    
    return M = HomalgIdentityMatrix( NumberRows( M ), HomalgRing( M ) );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="IsDiagonalMatrix:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M" Name="IsDiagonalMatrix" Label="homalgTable entry"/>
##    <Returns><C>true</C> or <C>false</C></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>M</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>IsDiagonalMatrix</C> is bound then the standard method
##      for the property <Ref Meth="IsDiagonalMatrix"/> shown below returns
##      <M>RP</M>!.<C>IsDiagonalMatrix</C><M>( <A>M</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( IsDiagonalMatrix,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, diag;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.IsDiagonalMatrix) then
        return RP!.IsDiagonalMatrix( M );
    fi;
    
    #=====# the fallback method #=====#
    
    diag := DiagonalEntries( M );
    
    return M = HomalgDiagonalMatrix( diag, NumberRows( M ), NumberColumns( M ), R );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="GetColumnIndependentUnitPositions">
##  <ManSection>
##    <Oper Arg="A, poslist" Name="GetColumnIndependentUnitPositions" Label="for matrices"/>
##    <Returns>a (possibly empty) list of pairs of positive integers</Returns>
##    <Description>
##      The list of column independet unit position of the matrix <A>A</A>.
##      We say that a unit <A>A</A><M>[i,k]</M> is column independet from the unit <A>A</A><M>[l,j]</M>
##      if <M>i>l</M> and <A>A</A><M>[l,k]=0</M>.
##      The rows are scanned from top to bottom and within each row the columns are
##      scanned from right to left searching for new units, column independent from the preceding ones.
##      If <A>A</A><M>[i,k]</M> is a new column independent unit then <M>[i,k]</M> is added to the
##      output list. If <A>A</A> has no units the empty list is returned.<P/>
##      (for the installed standard method see <Ref Meth="GetColumnIndependentUnitPositions" Label="homalgTable entry"/>)
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="GetColumnIndependentUnitPositions:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M, poslist" Name="GetColumnIndependentUnitPositions" Label="homalgTable entry"/>
##    <Returns>a (possibly empty) list of pairs of positive integers</Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>M</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>GetColumnIndependentUnitPositions</C> is bound then the standard method
##      of the operation <Ref Meth="GetColumnIndependentUnitPositions" Label="for matrices"/> shown below returns
##      <M>RP</M>!.<C>GetColumnIndependentUnitPositions</C><M>( <A>M</A>, <A>poslist</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( GetColumnIndependentUnitPositions,
        "for homalg matrices",
        [ IsHomalgMatrix, IsHomogeneousList ],
        
  function( M, poslist )
    local cache, R, RP, rest, pos, i, j, k;
    
    if IsBound( M!.GetColumnIndependentUnitPositions ) then
        cache := M!.GetColumnIndependentUnitPositions;
        if IsBound( cache.(String( poslist )) ) then
            return cache.(String( poslist ));
        fi;
    else
        cache := rec( );
        M!.GetColumnIndependentUnitPositions := cache;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.GetColumnIndependentUnitPositions) then
        pos := RP!.GetColumnIndependentUnitPositions( M, poslist );
        if pos <> [ ] then
            SetIsZero( M, false );
        fi;
        cache.(String( poslist )) := pos;
        return pos;
    fi;
    
    #=====# the fallback method #=====#
    
    rest := [ 1 .. NumberColumns( M ) ];
    
    pos := [ ];
    
    for i in [ 1 .. NumberRows( M ) ] do
        for k in Reversed( rest ) do
            if not [ i, k ] in poslist and
               IsUnit( R, M[ i, k ] ) then
                Add( pos, [ i, k ] );
                rest := Filtered( rest,
                                a -> IsZero( M[ i, a ] ) );
                break;
            fi;
        od;
    od;
    
    if pos <> [ ] then
        SetIsZero( M, false );
    fi;
    
    cache.(String( poslist )) := pos;
    
    return pos;
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="GetRowIndependentUnitPositions">
##  <ManSection>
##    <Oper Arg="A, poslist" Name="GetRowIndependentUnitPositions" Label="for matrices"/>
##    <Returns>a (possibly empty) list of pairs of positive integers</Returns>
##    <Description>
##      The list of row independet unit position of the matrix <A>A</A>.
##      We say that a unit <A>A</A><M>[k,j]</M> is row independet from the unit <A>A</A><M>[i,l]</M>
##      if <M>j>l</M> and <A>A</A><M>[k,l]=0</M>.
##      The columns are scanned from left to right and within each column the rows are
##      scanned from bottom to top searching for new units, row independent from the preceding ones.
##      If <A>A</A><M>[k,j]</M> is a new row independent unit then <M>[j,k]</M> (yes <M>[j,k]</M>) is added to the
##      output list. If <A>A</A> has no units the empty list is returned.<P/>
##      (for the installed standard method see <Ref Meth="GetRowIndependentUnitPositions" Label="homalgTable entry"/>)
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="GetRowIndependentUnitPositions:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M, poslist" Name="GetRowIndependentUnitPositions" Label="homalgTable entry"/>
##    <Returns>a (possibly empty) list of pairs of positive integers</Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>M</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>GetRowIndependentUnitPositions</C> is bound then the standard method
##      of the operation <Ref Meth="GetRowIndependentUnitPositions" Label="for matrices"/> shown below returns
##      <M>RP</M>!.<C>GetRowIndependentUnitPositions</C><M>( <A>M</A>, <A>poslist</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( GetRowIndependentUnitPositions,
        "for homalg matrices",
        [ IsHomalgMatrix, IsHomogeneousList ],
        
  function( M, poslist )
    local cache, R, RP, rest, pos, j, i, k;
    
    if IsBound( M!.GetRowIndependentUnitPositions ) then
        cache := M!.GetRowIndependentUnitPositions;
        if IsBound( cache.(String( poslist )) ) then
            return cache.(String( poslist ));
        fi;
    else
        cache := rec( );
        M!.GetRowIndependentUnitPositions := cache;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.GetRowIndependentUnitPositions) then
        pos := RP!.GetRowIndependentUnitPositions( M, poslist );
        if pos <> [ ] then
            SetIsZero( M, false );
        fi;
        cache.( String( poslist ) ) := pos;
        return pos;
    fi;
    
    #=====# the fallback method #=====#
    
    rest := [ 1 .. NumberRows( M ) ];
    
    pos := [ ];
    
    for j in [ 1 .. NumberColumns( M ) ] do
        for k in Reversed( rest ) do
            if not [ j, k ] in poslist and
               IsUnit( R, M[ k, j ] ) then
                Add( pos, [ j, k ] );
                rest := Filtered( rest,
                                a -> IsZero( M[ a, j ] ) );
                break;
            fi;
        od;
    od;
    
    if pos <> [ ] then
        SetIsZero( M, false );
    fi;
    
    cache.( String( poslist ) ) := pos;
    
    return pos;
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="GetUnitPosition">
##  <ManSection>
##    <Oper Arg="A, poslist" Name="GetUnitPosition" Label="for matrices"/>
##    <Returns>a (possibly empty) list of pairs of positive integers</Returns>
##    <Description>
##      The position <M>[i,j]</M> of the first unit <A>A</A><M>[i,j]</M> in the matrix <A>A</A>, where
##      the rows are scanned from top to bottom and within each row the columns are
##      scanned from left to right. If <A>A</A><M>[i,j]</M> is the first occurrence of a unit
##      then the position pair <M>[i,j]</M> is returned. Otherwise <C>fail</C> is returned.<P/>
##      (for the installed standard method see <Ref Meth="GetUnitPosition" Label="homalgTable entry"/>)
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="GetUnitPosition:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M, poslist" Name="GetUnitPosition" Label="homalgTable entry"/>
##    <Returns>a (possibly empty) list of pairs of positive integers</Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>M</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>GetUnitPosition</C> is bound then the standard method
##      of the operation <Ref Meth="GetUnitPosition" Label="for matrices"/> shown below returns
##      <M>RP</M>!.<C>GetUnitPosition</C><M>( <A>M</A>, <A>poslist</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( GetUnitPosition,
        "for homalg matrices",
        [ IsHomalgMatrix, IsHomogeneousList ],
        
  function( M, poslist )
    local R, RP, pos, m, n, i, j;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.GetUnitPosition) then
        pos := RP!.GetUnitPosition( M, poslist );
        if IsList( pos ) and IsPosInt( pos[1] ) and IsPosInt( pos[2] ) then
            SetIsZero( M, false );
        fi;
        return pos;
    fi;
    
    #=====# the fallback method #=====#
    
    m := NumberRows( M );
    n := NumberColumns( M );
    
    for i in [ 1 .. m ] do
        for j in [ 1 .. n ] do
            if not [ i, j ] in poslist and not j in poslist and
               IsUnit( R, M[ i, j ] ) then
                SetIsZero( M, false );
                return [ i, j ];
            fi;
        od;
    od;
    
    return fail;
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="PositionOfFirstNonZeroEntryPerRow:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M, poslist" Name="PositionOfFirstNonZeroEntryPerRow" Label="homalgTable entry"/>
##    <Returns>a list of nonnegative integers</Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>M</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>PositionOfFirstNonZeroEntryPerRow</C> is bound then the standard method
##      of the attribute <Ref Attr="PositionOfFirstNonZeroEntryPerRow"/> shown below returns
##      <M>RP</M>!.<C>PositionOfFirstNonZeroEntryPerRow</C><M>( <A>M</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( PositionOfFirstNonZeroEntryPerRow,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, pos, entries, r, c, i, k, j;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.PositionOfFirstNonZeroEntryPerRow) then
        return RP!.PositionOfFirstNonZeroEntryPerRow( M );
    elif IsBound(RP!.PositionOfFirstNonZeroEntryPerColumn) then
        return PositionOfFirstNonZeroEntryPerColumn( Involution( M ) );
    fi;
    
    #=====# the fallback method #=====#
    
    entries := EntriesOfHomalgMatrix( M );
    
    r := NumberRows( M );
    c := NumberColumns( M );
    
    pos := ListWithIdenticalEntries( r, 0 );
    
    for i in [ 1 .. r ] do
        k := (i - 1) * c;
        for j in [ 1 .. c ] do
            if not IsZero( entries[k + j] ) then
                pos[i] := j;
                break;
            fi;
        od;
    od;
    
    return pos;
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="PositionOfFirstNonZeroEntryPerColumn:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M, poslist" Name="PositionOfFirstNonZeroEntryPerColumn" Label="homalgTable entry"/>
##    <Returns>a list of nonnegative integers</Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>M</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>PositionOfFirstNonZeroEntryPerColumn</C> is bound then the standard method
##      of the attribute <Ref Attr="PositionOfFirstNonZeroEntryPerColumn"/> shown below returns
##      <M>RP</M>!.<C>PositionOfFirstNonZeroEntryPerColumn</C><M>( <A>M</A> )</M>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( PositionOfFirstNonZeroEntryPerColumn,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, pos, entries, r, c, j, i, k;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.PositionOfFirstNonZeroEntryPerColumn) then
        return RP!.PositionOfFirstNonZeroEntryPerColumn( M );
    elif IsBound(RP!.PositionOfFirstNonZeroEntryPerRow) then
        return PositionOfFirstNonZeroEntryPerRow( Involution( M ) );
    fi;
    
    #=====# the fallback method #=====#
    
    entries := EntriesOfHomalgMatrix( M );
    
    r := NumberRows( M );
    c := NumberColumns( M );
    
    pos := ListWithIdenticalEntries( c, 0 );
    
    for j in [ 1 .. c ] do
        for i in [ 1 .. r ] do
            k := (i - 1) * c;
            if not IsZero( entries[k + j] ) then
                pos[j] := i;
                break;
            fi;
        od;
    od;
    
    return pos;
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##
InstallMethod( DivideEntryByUnit,
        "for homalg matrices",
        [ IsHomalgMatrix, IsPosInt, IsPosInt, IsRingElement ],
        
  function( M, i, j, u )
    local R, RP;
    
    if IsEmptyMatrix( M ) then
        return;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.DivideEntryByUnit) then
        RP!.DivideEntryByUnit( M, i, j, u );
    else
        M[ i, j ] := M[ i, j ] / u;
    fi;
    
    ## caution: we deliberately do not return a new hull for Eval( M )
    
end );
    
##
InstallMethod( DivideRowByUnit,
        "for homalg matrices",
        [ IsHomalgMatrix, IsPosInt, IsRingElement, IsInt ],
        
  function( M, i, u, j )
    local R, RP, a, mat;
    
    if IsEmptyMatrix( M ) then
        return M;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.DivideRowByUnit) then
        RP!.DivideRowByUnit( M, i, u, j );
    else
        
        #=====# the fallback method #=====#
        
        if j > 0 then
            ## the two for's avoid creating non-dense lists:
            for a in [ 1 .. j - 1 ] do
                DivideEntryByUnit( M, i, a, u );
            od;
            for a in [ j + 1 .. NumberColumns( M ) ] do
                DivideEntryByUnit( M, i, a, u );
            od;
            M[ i, j ] := One( R );
        else
            for a in [ 1 .. NumberColumns( M ) ] do
                DivideEntryByUnit( M, i, a, u );
            od;
        fi;
        
    fi;
    
    ## since all what we did had a side effect on Eval( M ) ignoring
    ## possible other Eval's, e.g. EvalCompose, we want to return
    ## a new homalg matrix object only containing Eval( M )
    mat := HomalgMatrixWithAttributes( [
                   Eval, Eval( M ),
                   NumberRows, NumberRows( M ),
                   NumberColumns, NumberColumns( M ),
                   ], R );
    
    if HasIsZero( M ) and not IsZero( M ) then
        SetIsZero( mat, false );
    fi;
    
    return mat;
    
end );

##
InstallMethod( DivideColumnByUnit,
        "for homalg matrices",
        [ IsHomalgMatrix, IsPosInt, IsRingElement, IsInt ],
        
  function( M, j, u, i )
    local R, RP, a, mat;
    
    if IsEmptyMatrix( M ) then
        return M;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.DivideColumnByUnit) then
        RP!.DivideColumnByUnit( M, j, u, i );
    else
        
        #=====# the fallback method #=====#
        
        if i > 0 then
            ## the two for's avoid creating non-dense lists:
            for a in [ 1 .. i - 1 ] do
                DivideEntryByUnit( M, a, j, u );
            od;
            for a in [ i + 1 .. NumberRows( M ) ] do
                DivideEntryByUnit( M, a, j, u );
            od;
            M[ i, j ] := One( R );
        else
            for a in [ 1 .. NumberRows( M ) ] do
                DivideEntryByUnit( M, a, j, u );
            od;
        fi;
        
    fi;
    
    ## since all what we did had a side effect on Eval( M ) ignoring
    ## possible other Eval's, e.g. EvalCompose, we want to return
    ## a new homalg matrix object only containing Eval( M )
    mat := HomalgMatrixWithAttributes( [
                   Eval, Eval( M ),
                   NumberRows, NumberRows( M ),
                   NumberColumns, NumberColumns( M ),
                   ], R );
    
    if HasIsZero( M ) and not IsZero( M ) then
        SetIsZero( mat, false );
    fi;
    
    return mat;
    
end );

##
InstallMethod( CopyRowToIdentityMatrix,
        "for homalg matrices",
        [ IsHomalgMatrix, IsPosInt, IsList, IsPosInt ],
        
  function( M, i, L, j )
    local R, RP, v, vi, l, r;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.CopyRowToIdentityMatrix) then
        RP!.CopyRowToIdentityMatrix( M, i, L, j );
    else
        
        #=====# the fallback method #=====#
        
        if Length( L ) > 0 and IsHomalgMatrix( L[1] ) then
            v := L[1];
        fi;
        
        if Length( L ) > 1 and IsHomalgMatrix( L[2] ) then
            vi := L[2];
        fi;
        
        if IsBound( v ) and IsBound( vi ) then
            ## the two for's avoid creating non-dense lists:
            for l in [ 1 .. j - 1 ] do
                r := M[ i, l ];
                if not IsZero( r ) then
                    v[ j, l ] := -r;
                    vi[ j, l ] := r;
                fi;
            od;
            for l in [ j + 1 .. NumberColumns( M ) ] do
                r := M[ i, l ];
                if not IsZero( r ) then
                    v[ j, l ] := -r;
                    vi[ j, l ] := r;
                fi;
            od;
        elif IsBound( v ) then
            ## the two for's avoid creating non-dense lists:
            for l in [ 1 .. j - 1 ] do
                r := M[ i, l ];
                v[ j, l ] := -r;
            od;
            for l in [ j + 1 .. NumberColumns( M ) ] do
                r := M[ i, l ];
                v[ j, l ] := -r;
            od;
        elif IsBound( vi ) then
            ## the two for's avoid creating non-dense lists:
            for l in [ 1 .. j - 1 ] do
                r := M[ i, l ];
                vi[ j, l ] := r;
            od;
            for l in [ j + 1 .. NumberColumns( M ) ] do
                r := M[ i, l ];
                vi[ j, l ] := r;
            od;
        fi;
        
    fi;
    
end );

##
InstallMethod( CopyColumnToIdentityMatrix,
        "for homalg matrices",
        [ IsHomalgMatrix, IsPosInt, IsList, IsPosInt ],
        
  function( M, j, L, i )
    local R, RP, u, ui, m, k, r;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.CopyColumnToIdentityMatrix) then
        RP!.CopyColumnToIdentityMatrix( M, j, L, i );
    else
        
        #=====# the fallback method #=====#
        
        if Length( L ) > 0 and IsHomalgMatrix( L[1] ) then
            u := L[1];
        fi;
        
        if Length( L ) > 1 and IsHomalgMatrix( L[2] ) then
            ui := L[2];
        fi;
        
        if IsBound( u ) and IsBound( ui ) then
            ## the two for's avoid creating non-dense lists:
            for k in [ 1 .. i - 1 ] do
                r := M[ k, j ];
                if not IsZero( r ) then
                    u[ k, i ] := -r;
                    ui[ k, i ] := r;
                fi;
            od;
            for k in [ i + 1 .. NumberRows( M ) ] do
                r := M[ k, j ];
                if not IsZero( r ) then
                    u[ k, i ] := -r;
                    ui[ k, i ] := r;
                fi;
            od;
        elif IsBound( u ) then
            ## the two for's avoid creating non-dense lists:
            for k in [ 1 .. i - 1 ] do
                r := M[ k, j ];
                u[ k, i ] := -r;
            od;
            for k in [ i + 1 .. NumberRows( M ) ] do
                r := M[ k, j ];
                u[ k, i ] := -r;
            od;
        elif IsBound( ui ) then
            ## the two for's avoid creating non-dense lists:
            for k in [ 1 .. i - 1 ] do
                r := M[ k, j ];
                ui[ k, i ] := r;
            od;
            for k in [ i + 1 .. NumberRows( M ) ] do
                r := M[ k, j ];
                ui[ k, i ] := r;
            od;
        fi;
        
    fi;
    
end );

##
InstallMethod( SetColumnToZero,
        "for homalg matrices",
        [ IsHomalgMatrix, IsPosInt, IsPosInt ],
        
  function( M, i, j )
    local R, RP, zero, k;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.SetColumnToZero) then
        RP!.SetColumnToZero( M, i, j );
    else
        
        #=====# the fallback method #=====#
        
        zero := Zero( R );
        
        ## the two for's avoid creating non-dense lists:
        for k in [ 1 .. i - 1 ] do
            M[ k, j ] := zero;
        od;
        
        for k in [ i + 1 .. NumberRows( M ) ] do
            M[ k, j ] := zero;
        od;
        
    fi;
    
    ## since all what we did had a side effect on Eval( M ) ignoring
    ## possible other Eval's, e.g. EvalCompose, we want to return
    ## a new homalg matrix object only containing Eval( M )
    return HomalgMatrixWithAttributes( [
                 Eval, Eval( M ),
                 NumberRows, NumberRows( M ),
                 NumberColumns, NumberColumns( M ),
                 ], R );
    
end );

##
InstallMethod( GetCleanRowsPositions,
        "for homalg matrices",
        [ IsHomalgMatrix, IsHomogeneousList ],
        
  function( M, clean_columns )
    local R, RP, one, clean_rows, m, j, i;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.GetCleanRowsPositions) then
        return RP!.GetCleanRowsPositions( M, clean_columns );
    fi;
    
    one := One( R );
    
    #=====# the fallback method #=====#
    
    clean_rows := [ ];
    
    m := NumberRows( M );
    
    for j in clean_columns do
        for i in [ 1 .. m ] do
            if IsOne( M[ i, j ] ) then
                Add( clean_rows, i );
                break;
            fi;
        od;
    od;
    
    return clean_rows;
    
end );

##
InstallMethod( Eval,
        "for homalg matrices",
        [ IsHomalgMatrix and HasEvalConvertRowToMatrix ],
        
  function( C )
    local e, M, r, c, R, RP;
    
    e := EvalConvertRowToMatrix( C );
    M := e[1];
    r := e[2];
    c := e[3];
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.ConvertRowToMatrix) then
        return RP!.ConvertRowToMatrix( M, r, c );
    elif IsBound(RP!.ConvertRowToTransposedMatrix) then
        return Eval( TransposedMatrix( ConvertRowToTransposedMatrix( M, c, r ) ) );
    elif IsBound(RP!.ConvertColumnToMatrix) then
        return Eval( TransposedMatrix( ConvertColumnToMatrix( TransposedMatrix( M ), c, r ) ) );
    fi;
    
    if IsHomalgInternalMatrixRep( M ) and IsInternalMatrixHull( Eval( M ) ) then
        return Eval( HomalgMatrix( ListToListList( Eval( M )!.matrix[1], r, c ), r, c, R ) );
    fi;
    
    #=====# the fallback method #=====#
        
    return Eval( UnionOfRows(
                   List( [ 0 .. r - 1 ], i -> CertainColumns( M, [ 1 + i*c .. c + i*c ] ) ) ) );
    
end );

##
InstallMethod( Eval,
        "for homalg matrices",
        [ IsHomalgMatrix and HasEvalConvertColumnToMatrix ],
        
  function( C )
    local e, M, r, c, R, RP;
    
    e := EvalConvertColumnToMatrix( C );
    M := e[1];
    r := e[2];
    c := e[3];
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.ConvertColumnToMatrix) then
        return RP!.ConvertColumnToMatrix( M, r, c );
    elif IsBound(RP!.ConvertColumnToTransposedMatrix) then
        return Eval( TransposedMatrix( ConvertColumnToTransposedMatrix( M, c, r ) ) );
    elif IsBound(RP!.ConvertRowToMatrix) then
        return Eval( TransposedMatrix( ConvertRowToMatrix( TransposedMatrix( M ), c, r ) ) );
    fi;
    
    if IsHomalgInternalMatrixRep( M ) and IsInternalMatrixHull( Eval( M ) ) then
        return Eval( HomalgMatrix( TransposedMat( ListToListList( Flat( Eval( M )!.matrix ), c, r ) ), r, c, R ) );
    fi;
    
    #=====# the fallback method #=====#
    
    return Eval( UnionOfColumns(
                   List( [ 0 .. c - 1 ], i -> CertainRows( M, [ 1 + i*r .. r + i*r ] ) ) ) );
    
end );

##
InstallMethod( Eval,
        "for homalg matrices",
        [ IsHomalgMatrix and HasEvalConvertMatrixToRow ],
        
  function( C )
    local M, r, c, R, RP;
    
    M := EvalConvertMatrixToRow( C );
    
    r := NumberRows( M );
    c := NumberColumns( M );
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.ConvertMatrixToRow) then
        return RP!.ConvertMatrixToRow( M );
    elif IsBound(RP!.ConvertTransposedMatrixToRow) then
        return Eval( ConvertTransposedMatrixToRow( TransposedMatrix( M ) ) );
    elif IsBound(RP!.ConvertMatrixToColumn) then
        return Eval( TransposedMatrix( ConvertMatrixToColumn( TransposedMatrix( M ) ) ) );
    fi;
    
    if IsHomalgInternalMatrixRep( M ) and IsInternalMatrixHull( Eval( M ) ) then
        return Eval( HomalgMatrix( Concatenation( Eval( M )!.matrix ), 1, r * c, R ) );
    fi;
    
    #=====# the fallback method #=====#
    
    return Eval( UnionOfColumns( List( [ 1 .. r ], i -> CertainRows( M, [ i ] ) ) ) );
    
end );

##
InstallMethod( Eval,
        "for homalg matrices",
        [ IsHomalgMatrix and HasEvalConvertMatrixToColumn ],
        
  function( C )
    local M, r, c, R, RP;
    
    M := EvalConvertMatrixToColumn( C );
    
    r := NumberRows( M );
    c := NumberColumns( M );
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.ConvertMatrixToColumn) then
        return RP!.ConvertMatrixToColumn( M );
    elif IsBound(RP!.ConvertTransposedMatrixToColumn) then
        return Eval( ConvertTransposedMatrixToColumn( TransposedMatrix( M ) ) );
    elif IsBound(RP!.ConvertMatrixToRow) then
        return Eval( TransposedMatrix( ConvertMatrixToRow( TransposedMatrix( M ) ) ) );
    fi;
    
    if IsHomalgInternalMatrixRep( M ) and IsInternalMatrixHull( Eval( M ) ) then
        return Eval( HomalgMatrix( Concatenation( TransposedMat( Eval( M )!.matrix ) ), r * c, 1, R ) );
    fi;
    
    #=====# the fallback method #=====#
    
    return Eval( UnionOfRows( List( [ 1 .. c ], j -> CertainColumns( M, [ j ] ) ) ) );
    
end );

##
InstallMethod( ConvertRowToTransposedMatrix,
        "for homalg matrices",
        [ IsHomalgMatrix, IsInt, IsInt ],
        
  function( M, r, c )
    local R, RP, ext_obj, l, j;
    
    if NumberRows( M ) <> 1 then
        Error( "expecting a single row matrix as a first argument\n" );
    fi;
    
    if r = 1 then
        return M;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.ConvertRowToTransposedMatrix) then
        ext_obj := RP!.ConvertRowToTransposedMatrix( M, r, c );
        return HomalgMatrix( ext_obj, r, c, R );
    elif IsBound(RP!.ConvertRowToMatrix) then
        ext_obj := RP!.ConvertRowToMatrix( M, c, r );
        return TransposedMatrix( HomalgMatrix( ext_obj, c, r, R ) );
    fi;
    
    #=====# the fallback method #=====#
    
    ## to use
    ## CreateHomalgMatrixFromString( GetListOfHomalgMatrixAsString( M ), c, r, R )
    ## we would need a transpose afterwards,
    ## which differs from Involution in general:
    
    l := List( [ 1 .. c ], j -> CertainColumns( M, [ (j-1) * r + 1 .. j * r ] ) );
    l := List( l, GetListOfHomalgMatrixAsString );
    l := List( l, a -> CreateHomalgMatrixFromString( a, r, 1, R ) );
    
    return UnionOfColumns( l );
    
end );

##
InstallMethod( ConvertColumnToTransposedMatrix,
        "for homalg matrices",
        [ IsHomalgMatrix, IsInt, IsInt ],
        
  function( M, r, c )
    local R, RP, ext_obj;
    
    if NumberColumns( M ) <> 1 then
        Error( "expecting a single column matrix as a first argument\n" );
    fi;
    
    if c = 1 then
        return M;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.ConvertColumnToTransposedMatrix) then
        ext_obj := RP!.ConvertColumnToTransposedMatrix( M, r, c );
        return HomalgMatrix( ext_obj, r, c, R );
    elif IsBound(RP!.ConvertColumnToMatrix) then
        ext_obj := RP!.ConvertColumnToMatrix( M, c, r );
        return TransposedMatrix( HomalgMatrix( ext_obj, c, r, R ) );
    fi;
    
    #=====# the fallback method #=====#
    
    return CreateHomalgMatrixFromString( GetListOfHomalgMatrixAsString( M ), r, c, R ); ## delicate
    
end );

##
InstallMethod( ConvertTransposedMatrixToRow,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, ext_obj, r, c, l, j;
    
    if NumberRows( M ) = 1 then
        return M;
    fi;
    
    R := HomalgRing( M );
    
    if IsZero( M ) then
        return HomalgZeroMatrix( 1, NumberRows( M ) * NumberColumns( M ), R );
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.ConvertTransposedMatrixToRow) then
        ext_obj := RP!.ConvertTransposedMatrixToRow( M );
        return HomalgMatrix( ext_obj, 1, NumberRows( M ) * NumberColumns( M ), R );
    fi;
    
    #=====# the fallback method #=====#
    
    r := NumberRows( M );
    c := NumberColumns( M );
    
    ## CreateHomalgMatrixFromString( GetListOfHomalgMatrixAsString( "Transpose"( M ) ), 1, r * c, R )
    ## would require a Transpose operation,
    ## which differs from Involution in general:
    
    l := List( [ 1 .. c ], j -> CertainColumns( M, [ j ] ) );
    l := List( l, GetListOfHomalgMatrixAsString );
    l := List( l, a -> CreateHomalgMatrixFromString( a, 1, r, R ) );
    
    return UnionOfColumns( l );
    
end );

##
InstallMethod( ConvertTransposedMatrixToColumn,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, ext_obj;
    
    if NumberColumns( M ) = 1 then
        return M;
    fi;
    
    R := HomalgRing( M );
    
    if IsZero( M ) then
        return HomalgZeroMatrix( NumberRows( M ) * NumberColumns( M ), 1, R );
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.ConvertTransposedMatrixToColumn) then
        ext_obj := RP!.ConvertTransposedMatrixToColumn( M );
        return HomalgMatrix( ext_obj, NumberColumns( M ) * NumberRows( M ), 1, R );
    fi;
    
    #=====# the fallback method #=====#
    
    return CreateHomalgMatrixFromString( GetListOfHomalgMatrixAsString( M ), NumberColumns( M ) * NumberRows( M ), 1, R ); ## delicate
    
end );

##
InstallMethod( Eval,
        "for homalg matrices (HasPreEval)",
        [ IsHomalgMatrix and HasPreEval ],
        
  function( C )
    local R, RP, e;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );
    
    e :=  PreEval( C );
    
    ResetFilterObj( C, HasPreEval );
    
    ## delete the component which was left over by GAP
    Unbind( C!.PreEval );
    
    if IsBound(RP!.PreEval) then
        return RP!.PreEval( e );
    fi;
    
    #=====# the fallback method #=====#
    
    return Eval( e );
    
end );

##
InstallMethod( MaxDimensionalRadicalSubobjectOp,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, rad;
    
    if IsBound( M!.MaxDimensionalRadicalSubobjectOp ) then
        return M!.MaxDimensionalRadicalSubobjectOp;
    fi;
    
    R := HomalgRing( M );
    
    if IsZero( M ) then
        return HomalgZeroMatrix( 0, 1, R );
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.MaxDimensionalRadicalSubobject) then
        rad := RP!.MaxDimensionalRadicalSubobject( M ); ## the external object
        rad := HomalgMatrix( rad, R );
        if IsZero( rad ) then
            return HomalgZeroMatrix( 0, 1, R );
        fi;
        SetNumberColumns( rad, 1 );
        NumberRows( rad );
        IsOne( rad );
        M!.MaxDimensionalRadicalSubobjectOp := rad;
        rad!.MaxDimensionalRadicalSubobjectOp := rad;
        return rad;
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called MaxDimensionalRadicalSubobject ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( RadicalSubobjectOp,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, rad;
    
    if IsBound( M!.RadicalSubobjectOp ) then
        return M!.RadicalSubobjectOp;
    fi;
    
    R := HomalgRing( M );
    
    if IsZero( M ) then
        return HomalgZeroMatrix( 0, 1, R );
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.RadicalSubobject) then
        rad := RP!.RadicalSubobject( M ); ## the external object
        rad := HomalgMatrix( rad, R );
        if IsZero( rad ) then
            rad := HomalgZeroMatrix( 0, 1, R );
        fi;
        SetNumberColumns( rad, 1 );
        NumberRows( rad );
        IsOne( rad );
        if rad = M then
            rad := M;
        fi;
        M!.RadicalSubobjectOp := rad;
        rad!.RadicalSubobjectOp := rad;
        return rad;
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called RadicalSubobject ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( RadicalDecompositionOp,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, triv;
    
    if IsBound( M!.RadicalDecomposition ) then
        return M!.RadicalDecomposition;
    fi;
    
    R := HomalgRing( M );
    
    if IsZero( M ) then
        if NumberColumns( M ) = 0 then
            triv := HomalgZeroMatrix( 0, 0, R );
        else
            triv := HomalgZeroMatrix( 0, 1, R );
        fi;
        M!.RadicalDecomposition := [ triv ];
        return M!.RadicalDecomposition;
    fi;
    
    RP := homalgTable( R );
    
    if IsBound( RP!.RadicalDecomposition ) then
        M!.RadicalDecomposition := RP!.RadicalDecomposition( M );
        return M!.RadicalDecomposition;
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called RadicalDecomposition ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( EquiDimensionalDecompositionOp,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, triv;
    
    if IsBound( M!.EquiDimensionalDecomposition ) then
        return M!.EquiDimensionalDecomposition;
    fi;
    
    R := HomalgRing( M );
    
    if IsZero( M ) then
        if NumberColumns( M ) = 0 then
            triv := HomalgZeroMatrix( 0, 0, R );
        else
            triv := HomalgZeroMatrix( 0, 1, R );
        fi;
        M!.EquiDimensionalDecomposition := [ triv ];
        return M!.EquiDimensionalDecomposition;
    fi;
    
    RP := homalgTable( R );
    
    if IsBound( RP!.EquiDimensionalDecomposition ) then
        M!.EquiDimensionalDecomposition := RP!.EquiDimensionalDecomposition( M );
        return M!.EquiDimensionalDecomposition;
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called EquiDimensionalDecomposition ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( MaxDimensionalSubobjectOp,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, max;
    
    if IsBound( M!.MaxDimensionalSubobjectOp ) then
        return M!.MaxDimensionalSubobjectOp;
    fi;
    
    R := HomalgRing( M );
    
    if IsZero( M ) then
        return HomalgZeroMatrix( 0, 1, R );
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.MaxDimensionalSubobject) then
        max := RP!.MaxDimensionalSubobject( M ); ## the external object
        max := HomalgMatrix( max, R );
        if IsZero( max ) then
            return HomalgZeroMatrix( 0, 1, R );
        fi;
        SetNumberColumns( max, 1 );
        NumberRows( max );
        IsOne( max );
        M!.MaxDimensionalSubobjectOp := max;
        max!.MaxDimensionalSubobjectOp := max;
        return max;
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called MaxDimensionalSubobject ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( PrimaryDecompositionOp,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, triv;
    
    if IsBound( M!.PrimaryDecomposition ) then
        return M!.PrimaryDecomposition;
    fi;
    
    R := HomalgRing( M );
    
    if IsZero( M ) then
        if NumberColumns( M ) = 0 then
            triv := HomalgZeroMatrix( 0, 0, R );
        else
            triv := HomalgZeroMatrix( 0, 1, R );
        fi;
        M!.PrimaryDecomposition := [ [ triv, triv ] ];
        return M!.PrimaryDecomposition;
    fi;
    
    RP := homalgTable( R );
    
    if IsBound( RP!.PrimaryDecomposition ) then
        M!.PrimaryDecomposition := RP!.PrimaryDecomposition( M );
        return M!.PrimaryDecomposition;
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called PrimaryDecomposition ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##  <#GAPDoc Label="Eliminate">
##  <ManSection>
##    <Oper Arg="rel, indets" Name="Eliminate"/>
##    <Returns>a &homalg; matrix</Returns>
##    <Description>
##      Eliminate the independents <A>indets</A> from the matrix (or list of ring elements) <A>rel</A>,
##      i.e. compute a generating set
##      of the ideal defined as the intersection of the ideal generated by the entries of the list <A>rel</A>
##      with the subring generated by all indeterminates except those in <A>indets</A>.
##      by the list of indeterminates <A>indets</A>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( Eliminate,
        "for a homalg matrix and list of homalg ring elements",
        [ IsHomalgMatrix, IsList ],
        
  function( rel, indets )
    local R, RP, elim;
    
    R := HomalgRing( rel );
    
    if IsZero( rel ) then
        return HomalgZeroMatrix( 0, 1, R );
    fi;
    
    rel := EntriesOfHomalgMatrix( rel );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.Eliminate) then
        elim := RP!.Eliminate( rel, indets, R ); ## the external object
        elim := HomalgMatrix( elim, R );
        if IsZero( elim ) then
            return HomalgZeroMatrix( 0, 1, R );
        fi;
        SetNumberColumns( elim, 1 );
        NumberRows( elim );
        return elim;
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called Eliminate ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( Eliminate,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( rel )
    local R, indets, B;
    
    R := HomalgRing( rel );
    
    if HasRelativeIndeterminatesOfPolynomialRing( R ) then
        indets := RelativeIndeterminatesOfPolynomialRing( R );
        B := BaseRing( R );
    else
        indets := Indeterminates( R );
        B := CoefficientsRing( R );
    fi;
    
    return B * Eliminate( rel, indets );
    
end );

##
InstallMethod( Eliminate,
        "for two lists of ring elements and a homalg ring",
        [ IsList, IsList, IsHomalgRing ],
        
  function( rel, indets, R )
    
    rel := HomalgMatrix( rel, Length( rel ), 1, R );
    
    return Eliminate( rel, indets );
    
end );

##
InstallMethod( Eliminate,
        "for two lists of ring elements",
        [ IsList, IsList ],
        
  function( rel, indets )
    local R;
    
    if not rel = [ ] then
        R := HomalgRing( rel[1] );
    elif not indets = [ ] then
        R := HomalgRing( indets[1] );
    else
        Error( "cannot extract ring out of two empty input lists\n" );
    fi;
    
    return Eliminate( rel, indets, R );
    
end );

##
InstallMethod( Eliminate,
        "for a homalg matrix and ring element",
        [ IsHomalgMatrix, IsHomalgRingElement ],
        
  function( rel, v )
    
    return Eliminate( rel, [ v ] );
    
end );

##
InstallMethod( Eliminate,
        "for a list and ring element",
        [ IsList, IsHomalgRingElement ],
        
  function( rel, v )
    
    return Eliminate( rel, [ v ] );
    
end );

##
InstallMethod( Coefficients,
        "for a ring element and a list of indeterminates",
        [ IsHomalgRingElement, IsList ],
        
  function( poly, var )
    local R, RP, both, monomials, coeffs;
    
    R := HomalgRing( poly );
    
    if IsZero( poly ) then
        coeffs := HomalgZeroMatrix( 1, 0, R );
        coeffs!.monomials := MakeImmutable( [ ] );
        return coeffs;
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.Coefficients) then
        both := RP!.Coefficients( poly, var ); ## the pair of external objects
        monomials := HomalgMatrix( both[1], R );
        monomials := EntriesOfHomalgMatrix( monomials );
        coeffs := HomalgMatrix( both[2], Length( monomials ), 1, R );
        coeffs!.monomials := MakeImmutable( monomials );
        return coeffs;
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called Coefficients ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( Coefficients,
        "for a ring element and an indeterminate",
        [ IsHomalgRingElement, IsHomalgRingElement ],
        
  function( poly, var )
    
    return Coefficients( poly, [ var ] );
    
end );

##
InstallMethod( Coefficients,
        "for a homalg ring element and a string",
        [ IsHomalgRingElement, IsString and IsStringRep ],
        
  function( poly, var_name )
    
    return Coefficients( poly, var_name / HomalgRing( poly ) );
    
end );

##
InstallMethod( Coefficients,
        "for a homalg ring element",
        [ IsHomalgRingElement ],
        
  function( poly )
    local R, indets, coeffs;
    
    R := HomalgRing( poly );
    
    if IsBound( poly!.Coefficients ) then
        return poly!.Coefficients;
    fi;
    
    if HasRelativeIndeterminatesOfPolynomialRing( R ) then
        indets := RelativeIndeterminatesOfPolynomialRing( R );
    elif HasIndeterminatesOfPolynomialRing( R ) then
        indets := IndeterminatesOfPolynomialRing( R );
    elif HasRelativeIndeterminateAntiCommutingVariablesOfExteriorRing( R ) then
        indets := RelativeIndeterminateAntiCommutingVariablesOfExteriorRing( R );
    elif HasIndeterminateAntiCommutingVariablesOfExteriorRing( R ) then
        indets := IndeterminateAntiCommutingVariablesOfExteriorRing( R );
    elif HasIndeterminateShiftsOfShiftAlgebra( R ) then
        indets := IndeterminateShiftsOfShiftAlgebra( R );
    elif HasIndeterminateShiftsOfPseudoDoubleShiftAlgebra( R ) then
        indets := IndeterminateShiftsOfPseudoDoubleShiftAlgebra( R );
    elif HasIndeterminateShiftsOfDoubleShiftAlgebra( R ) then
        indets := IndeterminateShiftsOfDoubleShiftAlgebra( R );
    elif HasIndeterminateShiftsOfBiasedDoubleShiftAlgebra( R ) then
        indets := IndeterminateShiftsOfBiasedDoubleShiftAlgebra( R );
    elif HasIsFieldForHomalg( R ) and IsFieldForHomalg( R ) then
        indets := [ ];
    else
        TryNextMethod( );
    fi;
    
    coeffs := Coefficients( poly, indets );
    
    poly!.Coefficients := coeffs;
    
    return coeffs;
    
end );

##
InstallMethod( Coefficients,
        "for a homalg matrix and a list of indeterminates",
        [ IsHomalgMatrix, IsList ],
        
  function( matrix, var )
    local R, RP, both, monomials, coeffs;
    
    R := HomalgRing( matrix );
    
    if IsZero( matrix ) then
        coeffs := HomalgZeroMatrix( 1, 0, R );
        coeffs!.monomials := MakeImmutable( [ ] );
        return coeffs;
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.CoefficientsMatrix) then
        both := RP!.CoefficientsMatrix( matrix, var ); ## the pair of external objects
        monomials := HomalgMatrix( both[1], R );
        monomials := EntriesOfHomalgMatrix( monomials );
        coeffs := HomalgMatrix( both[2], Length( monomials ), NumberRows( matrix ), R );
        coeffs!.monomials := MakeImmutable( monomials );
        return coeffs;
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called Coefficients ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( Coefficients,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( matrix )
    local R, indets, coeffs;
    
    R := HomalgRing( matrix );
    
    if IsBound( matrix!.Coefficients ) then
        return matrix!.Coefficients;
    fi;
    
    if HasRelativeIndeterminatesOfPolynomialRing( R ) then
        indets := RelativeIndeterminatesOfPolynomialRing( R );
    elif HasIndeterminatesOfPolynomialRing( R ) then
        indets := IndeterminatesOfPolynomialRing( R );
    elif HasRelativeIndeterminateAntiCommutingVariablesOfExteriorRing( R ) then
        indets := RelativeIndeterminateAntiCommutingVariablesOfExteriorRing( R );
    elif HasIndeterminateAntiCommutingVariablesOfExteriorRing( R ) then
        indets := IndeterminateAntiCommutingVariablesOfExteriorRing( R );
    elif HasIndeterminateShiftsOfShiftAlgebra( R ) then
        indets := IndeterminateShiftsOfShiftAlgebra( R );
    elif HasIndeterminateShiftsOfPseudoDoubleShiftAlgebra( R ) then
        indets := IndeterminateShiftsOfPseudoDoubleShiftAlgebra( R );
    elif HasIndeterminateShiftsOfDoubleShiftAlgebra( R ) then
        indets := IndeterminateShiftsOfDoubleShiftAlgebra( R );
    elif HasIndeterminateShiftsOfBiasedDoubleShiftAlgebra( R ) then
        indets := IndeterminateShiftsOfBiasedDoubleShiftAlgebra( R );
    elif HasIsFieldForHomalg( R ) and IsFieldForHomalg( R ) then
        indets := [ ];
    else
        TryNextMethod( );
    fi;
    
    coeffs := Coefficients( matrix, indets );
    
    matrix!.Coefficients := coeffs;
    
    return coeffs;
    
end );

##
InstallMethod( DecomposeInMonomials,
        "for a homalg ring element",
        [ IsHomalgRingElement ],
        
  function( poly )
    local coeffs, monoms;
    
    coeffs := Coefficients( poly );
    monoms := coeffs!.monomials;
    coeffs := EntriesOfHomalgMatrix( coeffs );
    
    return ListN( coeffs, monoms, {a,b} -> [ a, b ] );
    
end );

##  <#GAPDoc Label="Eval:HasEvalCoefficientsWithGivenMonomials">
##  <ManSection>
##    <Meth Arg="C" Name="Eval" Label="for matrices created with CoefficientsWithGivenMonomials"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      In case the matrix was created using
##      <Ref Func="CoefficientsWithGivenMonomials" Label="for two homalg matrices"/>
##      then the filter <C>HasEvalCoefficientsWithGivenMonomials</C> for <A>C</A> is set to true and
##      the <C>homalgTable</C> function <Ref Meth="CoefficientsWithGivenMonomials" Label="homalgTable entry"/>
##      will be used to set the attribute <C>Eval</C>.
##    <Listing Type="Code"><![CDATA[
InstallMethod( Eval,
        "for homalg matrices (HasEvalCoefficientsWithGivenMonomials)",
        [ IsHomalgMatrix and HasEvalCoefficientsWithGivenMonomials ],
        
  function( C )
    local R, RP, pair, M, monomials;
    
    R := HomalgRing( C );
    
    RP := homalgTable( R );

    pair := EvalCoefficientsWithGivenMonomials( C );

    M := pair[1];
    monomials := pair[2];
    
    if IsBound( RP!.CoefficientsWithGivenMonomials ) then
        
        return RP!.CoefficientsWithGivenMonomials( M, monomials );
        
    fi;

    Error( "could not find a procedure called CoefficientsWithGivenMonomials ",
            "in the homalgTable of the ring\n" );
    
end );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="CoefficientsWithGivenMonomials:homalgTable_entry">
##  <ManSection>
##    <Func Arg="M, monomials" Name="CoefficientsWithGivenMonomials" Label="homalgTable entry"/>
##    <Returns>the <C>Eval</C> value of a &homalg; matrix <A>C</A></Returns>
##    <Description>
##      Let <M>R :=</M> <C>HomalgRing</C><M>( <A>C</A> )</M> and <M>RP :=</M> <C>homalgTable</C><M>( R )</M>.
##      If the <C>homalgTable</C> component <M>RP</M>!.<C>CoefficientsWithGivenMonomials</C> is bound then
##      the method <Ref Meth="Eval" Label="for matrices created with CoefficientsWithGivenMonomials"/> returns
##      <M>RP</M>!.<C>CoefficientsWithGivenMonomials</C> applied to the content of the attribute
##      <C>EvalCoefficientsWithGivenMonomials</C><M>( <A>C</A> ) = [ <A>M</A>, <A>monomials</A> ]</M>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##
InstallMethod( CoefficientsOfUnivariatePolynomial,
        "for two homalg ring elements",
        [ IsHomalgRingElement, IsHomalgRingElement ],
        
  function( r, var )
    local R, RP, ext_obj;
    
    if IsZero( r ) then
        return HomalgZeroMatrix( 1, 0, R );
    fi;
    
    R := HomalgRing( r );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.CoefficientsOfUnivariatePolynomial) then
        ext_obj := RP!.CoefficientsOfUnivariatePolynomial( r, var );
        return HomalgMatrix( ext_obj, 1, "unknown_number_of_columns", R );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( CoefficientsOfUnivariatePolynomial,
        "for a homalg ring element and a string",
        [ IsHomalgRingElement, IsString ],
        
  function( r, var_name )
    
    return CoefficientsOfUnivariatePolynomial( r, var_name / HomalgRing( r ) );
    
end );

## for univariate polynomials over arbitrary base rings
InstallMethod( CoefficientsOfUnivariatePolynomial,
        "for a homalg ring element",
        [ IsHomalgRingElement ],
        
  function( r )
    local R, indets;
    
    R := HomalgRing( r );
    
    if HasRelativeIndeterminatesOfPolynomialRing( R ) then
        indets := RelativeIndeterminatesOfPolynomialRing( R );
    elif HasIndeterminatesOfPolynomialRing( R ) then
        indets := IndeterminatesOfPolynomialRing( R );
    fi;
    
    if not Length( indets ) = 1 then
        TryNextMethod( );
    fi;
    
    return CoefficientsOfUnivariatePolynomial( r, indets[1] );
    
end );

## for univariate polynomials over arbitrary base rings
InstallMethod( CoefficientOfUnivariatePolynomial,
        "for a homalg ring element and an integer",
        [ IsHomalgRingElement, IsInt ],
        
  function( r, j )
    local coeffs;
    
    coeffs := CoefficientsOfUnivariatePolynomial( r );
    coeffs := EntriesOfHomalgMatrix( coeffs );
    
    if j > Length( coeffs ) - 1 then
        return Zero( r );
    fi;
    
    return coeffs[j + 1];
    
end );

##
InstallMethod( LeadingCoefficient,
        "for lists of ring elements",
        [ IsHomalgRingElement, IsHomalgRingElement ],
        
  function( poly, var )
    
    return MatElm( Coefficients( poly, var ), 1, 1 );
    
end );

##
InstallMethod( LeadingCoefficient,
        "for a homalg ring element and a string",
        [ IsHomalgRingElement, IsString ],
        
  function( r, var_name )
    
    return LeadingCoefficient( r, var_name / HomalgRing( r ) );
    
end );

##
InstallMethod( LeadingCoefficient,
        "for a homalg ring element",
        [ IsHomalgRingElement ],
        
  function( poly )
    local lc;
    
    if IsBound( poly!.LeadingCoefficient ) then
        return poly!.LeadingCoefficient;
    fi;
    
    lc := MatElm( Coefficients( poly ), 1, 1 );
    
    poly!.LeadingCoefficient := lc;
    
    return lc;
    
end );

## FIXME: make this a fallback method
InstallMethod( LeadingMonomial,
        "for a homalg ring element",
        [ IsHomalgRingElement ],
        
  function( poly )
    local lm;
    
    if IsBound( poly!.LeadingMonomial ) then
        return poly!.LeadingMonomial;
    fi;
    
    if IsZero( poly ) then
        lm := poly;
    else
        lm := Coefficients( poly )!.monomials[1];
    fi;
    
    poly!.LeadingMonomial := lm;
    
    return lm;

end );

##
InstallMethod( IndicatorMatrixOfNonZeroEntries,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( mat )
    local R, RP, result, r, c, i, j;
    
    R := HomalgRing( mat );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.IndicatorMatrixOfNonZeroEntries) then
        return RP!.IndicatorMatrixOfNonZeroEntries( mat );
    fi;
    
    r := NumberRows( mat );
    c := NumberColumns( mat );
    
    result := List( [ 1 .. r ], a -> ListWithIdenticalEntries( c, 0 ) );
    
    for i in [ 1 .. r ] do
        for j in [ 1 .. c ] do
            if not IsZero( mat[ i, j ] ) then
                result[i][j] := 1;
            fi;
        od;
    od;
    
    return result;
    
end );

##
InstallMethod( Pullback,
        "for homalg rings",
        [ IsHomalgRingMap, IsHomalgMatrix ],
        
  function( phi, M )
    local R, S, T, r, c, RP;
    
    R := HomalgRing( M );
    
    S := Source( phi );
    
    T := Range( phi );
    
    if not IsIdenticalObj( S, R ) then
        
        if HasAmbientRing( R ) then
            R := AmbientRing( R );
            M := R * M;
        fi;
        
        if HasAmbientRing( S ) then
            S := AmbientRing( S );
        fi;
        
        if not IsIdenticalObj( S, R ) then
            Error( "the source ring of the ring map phi and the ring R of the matrix are not identical\n" );
        fi;
        
        if not IsBound( phi!.RingMapFromAmbientRing ) then
            phi!.RingMapFromAmbientRing := RingMap( ImagesOfRingMapAsColumnMatrix( phi ), S, T );
        fi;
        
        return Pullback( phi!.RingMapFromAmbientRing, M );
        
    elif HasAmbientRing( S ) then
        
        R := AmbientRing( R );
        S := AmbientRing( S );
        
        Assert( 0, IsIdenticalObj( R, S ) );
        
        M := R * M;
        
        if not IsBound( phi!.RingMapFromAmbientRing ) then
            phi!.RingMapFromAmbientRing := RingMap( ImagesOfRingMapAsColumnMatrix( phi ), S, T );
        fi;
        
        return Pullback( phi!.RingMapFromAmbientRing, M );
        
    fi;
    
    r := NumberRows( M );
    c := NumberColumns( M );
    
    if IsZero( M ) then
        
        return HomalgZeroMatrix( r, c, T );
        
    fi;
    
    if IsEmptyMatrix( ImagesOfRingMapAsColumnMatrix( phi ) ) then
        
        return T * M;
        
    fi;
    
    RP := homalgTable( T );
    
    if IsBound( RP!.Pullback ) then
        
        return HomalgMatrix( RP!.Pullback( phi, M ), NumberRows( M ), NumberColumns( M ), T );
        
    fi;
    
    if not IsHomalgInternalRingRep( T ) then
        Error( "could not find a procedure called Pullback ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( Pullback,
        "for homalg rings",
        [ IsHomalgRingMap, IsRingElement ],
        
  function( phi, r )
    
    r := HomalgMatrix( [ r ], 1, 1, Source( phi ) );
    
    r := Pullback( phi, r );
    
    return r[ 1, 1 ];
    
end );

####################################
#
# methods for operations (you probably don't urgently need to replace for an external CAS):
#
####################################

##
InstallMethod( \+,
        "for homalg ring elements",
        [ IsHomalgRingElement, IsHomalgRingElement ],
        
  function( r1, r2 )
    local R, RP;
    
    R := HomalgRing( r1 );
    
    if not HasRingElementConstructor( R ) then
        Error( "no ring element constructor found in the ring\n" );
    fi;
    
    if not IsIdenticalObj( R, HomalgRing( r2 ) ) then
        return Error( "the two elements are not in the same ring\n" );
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.Sum) then
        return RingElementConstructor( R )( RP!.Sum( r1, r2 ), R );
    elif IsBound(RP!.Minus) then
        return RingElementConstructor( R )( RP!.Minus( r1, RP!.Minus( Zero( R ), r2 ) ), R );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( \*,
        "for homalg ring elements",
        [ IsHomalgRingElement, IsHomalgRingElement ],
        
  function( r1, r2 )
    local R, RP;
    
    R := HomalgRing( r1 );
    
    if not HasRingElementConstructor( R ) then
        Error( "no ring element constructor found in the ring\n" );
    fi;
    
    if not IsIdenticalObj( R, HomalgRing( r2 ) ) then
        return Error( "the two elements are not in the same ring\n" );
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.Product) then
        return RingElementConstructor( R )( RP!.Product( r1, r2 ), R ) ;
    fi;
    
    #=====# the fallback method #=====#
    
    return MatElm( HomalgMatrix( [ r1 ], 1, 1, R ) * HomalgMatrix( [ r2 ], 1, 1, R ), 1, 1 );
    
end );

##
InstallMethod( Degree,
        "for homalg ring elements",
        [ IsHomalgRingElement ],
        
  function( r )
    local R, RP, deg;
    
    if IsBound( r!.Degree ) then
        return r!.Degree;
    fi;
    
    R := HomalgRing( r );
    
    ## do not delete this
    if HasRelativeIndeterminatesOfPolynomialRing( R ) then
        TryNextMethod( );
    fi;
    
    RP := homalgTable( R );
    
    if not IsBound(RP!.DegreeOfRingElement) then
        TryNextMethod( );
    fi;
    
    deg := RP!.DegreeOfRingElement( r, R );
    
    r!.Degree := deg;
    
    return deg;
    
end );

##
InstallMethod( Degree,
        "for homalg ring elements",
        [ IsHomalgRingElement ],
        
  function( r )
    local R, RP, coeffs, deg;
    
    if IsBound( r!.Degree ) then
        return r!.Degree;
    fi;
    
    if IsZero( r ) then
        return -1;
    fi;
    
    R := HomalgRing( r );
    
    if not HasRelativeIndeterminatesOfPolynomialRing( R ) then
        TryNextMethod( );
    fi;
    
    RP := homalgTable( R );
    
    if not ( IsBound(RP!.Coefficients) and IsBound( RP!.DegreeOfRingElement ) ) then
        TryNextMethod( );
    fi;
    
    coeffs := Coefficients( r );
    
    deg := RP!.DegreeOfRingElement( coeffs!.monomials[1], R );
    
    r!.Degree := deg;
    
    return deg;
    
end );

##
InstallMethod( MatrixOfSymbols,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( mat )
    local S, R, RP, symb;
    
    R := HomalgRing( mat );
    
    S := AssociatedGradedRing( R );
    
    if IsZero( mat ) then
        return HomalgZeroMatrix( NumberRows( mat ), NumberColumns( mat ), S );
    elif IsOne( mat ) then
        return HomalgIdentityMatrix( NumberRows( mat ), S );
    fi;
    
    RP := homalgTable( R );
    
    if not IsBound(RP!.MatrixOfSymbols) then
        Error( "could not find a procedure called MatrixOfSymbols ",
               "in the homalgTable of the ring\n" );
    fi;
    
    symb := RP!.MatrixOfSymbols( mat );
    
    symb := S * HomalgMatrix( symb, NumberRows( mat ), NumberColumns( mat ), R );
    
    ## TODO: add more properties and attributes to symb
    
    return symb;
    
end );

##
InstallMethod( GetRidOfRowsAndColumnsWithUnits,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local MM, R, RP, rr, cc, r, c, UI, VI, U, V, rows, columns,
          deleted_rows, deleted_columns, pos, i, j, e,
          column, column_range, row, row_range, IdU, IdV, u, v, U_M_V;
    
    if IsBound( M!.GetRidOfRowsAndColumnsWithUnits ) then
        return M!.GetRidOfRowsAndColumnsWithUnits;
    fi;
    
    MM := M;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    rr := NumberRows( M );
    cc := NumberColumns( M );
    
    r := rr;
    c := cc;
    
    UI := HomalgIdentityMatrix( rr, R );
    VI := HomalgIdentityMatrix( cc, R );
    
    U := UI;
    V := VI;
    
    if IsBound( RP!.GetRidOfRowsAndColumnsWithUnits ) then
        
        M := RP!.GetRidOfRowsAndColumnsWithUnits( M );
        
        rows := M[2];
        columns := M[3];
        
        deleted_rows := M[4];
        deleted_columns := M[5];
        
        M := M[1];
        
        Assert( 6, IsUnitFree( M ) );
        SetIsUnitFree( M, true );
        
    else
        
        rows := [ ];
        columns := [ ];
        
        deleted_rows := [ ];
        deleted_columns := [ ];
        
    fi;
    
    #=====# the fallback method #=====#
    
    pos := GetUnitPosition( M );
    
    SetIsUnitFree( M, pos = fail );
    
    while not IsUnitFree( M ) do
        
        i := pos[1]; j := pos[2];
        
        e := M[ i, j ];
        
        Assert( 6, IsUnit( e ) );
        Assert( 6, not IsZero( e ) );
        
        if IsHomalgRingElement( e ) then
            e!.IsUnit := true;
            SetIsZero( e, false );
        fi;
        
        if IsOne( e ) then
            e := HomalgIdentityMatrix( 1, R );
        else
            e := e^-1;
            Assert( 0, not e = fail );
            e := HomalgMatrix( [ e ], 1, 1, R );
            
            Assert( 6, not IsZero( e ) );
            SetIsZero( e, false );
            
        fi;
        
        Add( rows, i );
        Add( columns, j );
        
        column := CertainColumns( M, [ j ] );
        
        column_range := Concatenation( [ 1 .. j - 1 ], [ j + 1 .. c ] );
        
        M := CertainColumns( M, column_range ); c := c - 1;
        
        row := CertainRows( M, [ i ] );
        
        row_range := Concatenation( [ 1 .. i - 1 ], [ i + 1 .. r ] );
        
        column := CertainRows( column, row_range );
        
        M := CertainRows( M, row_range ); r := r - 1;
        
        ## the following line breaks the symmetry of the line redefining M,
        ## which could have been M := M - column * e * row;
        ## but since the adapted row will be reused in creating
        ## the trafo matrix V below, I decided to redefine the row
        ## for effeciency reasons
        
        row := e * row;
        
        M := M - column * row;
        
        column := column * e;
        
        Add( deleted_rows, -row );
        Add( deleted_columns, -column );
        
        pos := GetUnitPosition( M );
        
        SetIsUnitFree( M, pos = fail );
        
    od;
    
    r := rr;
    c := cc;
    
    for pos in [ 1 .. Length( rows ) ] do
        
        r := r - 1;
        c := c - 1;
        
        i := rows[pos]; j := columns[pos];
        
        IdU := HomalgIdentityMatrix( r, R );
        IdV := HomalgIdentityMatrix( c, R );
        
        u := CertainColumns( IdU, [ 1 .. i - 1 ] );
        u := UnionOfColumns( u, deleted_columns[pos] );
        u := UnionOfColumns( u, CertainColumns( IdU, [ i .. r ] ) );
        
        v := CertainRows( IdV, [ 1 .. j - 1 ] );
        v := UnionOfRows( v, deleted_rows[pos] );
        v := UnionOfRows( v, CertainRows( IdV, [ j .. c ] ) );
        
        U := u * U;
        V := V * v;
        
    od;
    
    ## now bring rows and columns to absolute positions
    
    rr := [ 1 .. rr ];
    cc := [ 1 .. cc ];
    
    Perform( rows, function( i ) Remove( rr, i ); end );
    Perform( columns, function( j ) Remove( cc, j ); end );
    
    UI := CertainColumns( UI, rr );
    VI := CertainRows( VI, cc );
    
    ## 1. Left/RightInverse is better than Left/RightInverseLazy here
    ##    as V and U are known to be a subidentity matrices
    ## 2. Caution:
    ##    (-) U * MM * V is NOT = M, in general, nor
    ##    (-) UI * M * VI is NOT = MM, in general, but
    ##    (+) U * MM and M generate the same column space
    ##    (+) MM * V and M generate the same row space
    ##    (+) UI * M generate column subspace of MM
    ##    (+) M * VI generate row subspace of MM
    
    Assert( 6, GenerateSameColumnModule( U * MM, M ) );
    Assert( 6, GenerateSameRowModule( MM * V, M ) );
    
    Assert( 6, IsZero( DecideZeroColumns( UI * M, BasisOfColumnModule( MM ) ) ) );
    Assert( 6, IsZero( DecideZeroRows( M * VI, BasisOfRowModule( MM ) ) ) );
    
    U_M_V := [ U, UI, M, VI, V ];
    
    MM!.GetRidOfRowsAndColumnsWithUnits := U_M_V;
    
    return U_M_V;
    
end );

##
## L = [ min_deg, max_deg, zt, coeff ]
## min_deg and max_deg determine the minimal and maximal degree of the element
## zt determines the percentage of the zero terms in the element
## The non-trivial coefficients belong to the interval [ 1 .. coeffs ]
##
InstallMethod( Random,
        "for a homalg ring and a list",
        [ IsHomalgRing, IsList ],
        
  function( R, L )
    local RP;
    
    RP := homalgTable( R );
    
    L := Concatenation( [ R ], L );
    
    if IsBound(RP!.RandomPol) then
        return RingElementConstructor( R )( CallFuncList( RP!.RandomPol, L ), R );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( Random,
        "for a homalg ring and an integer",
        [ IsHomalgRing, IsInt ],
        
  function( R, maxdeg )
    
    return Random( R, [ maxdeg ] );
    
end );

##
InstallMethod( Random,
        "for a homalg ring",
        [ IsHomalgRing ],
        
  function( R )
    
    return Random( R, [ 0, Random( [ 0, 1, 1, 1, 2, 2, 2, 3 ] ), 80, 50 ] );
    
end );

##
InstallMethod( Random,
        "for a homalg internal ring",
        [ IsHomalgRing and IsHomalgInternalRingRep ],
        
  function( R )
    
    return Random( R!.ring );
    
end );

##
InstallMethod( Random,
        "for a homalg ring",
        [ IsHomalgRing and IsRationalsForHomalg ],
        
  function( R )
    
    if IsHomalgInternalRingRep( R ) then
        TryNextMethod( );
    fi;
    
    return Random( R, 0 );
    
end );

##
InstallMethod( Random,
        "for a homalg ring",
        [ IsHomalgRing and IsIntegersForHomalg ],
        
  function( R )
    
    if IsHomalgInternalRingRep( R ) then
        TryNextMethod( );
    fi;
    
    return Random( R, 0 );
    
end );

##
InstallMethod( Value,
        "polynomial substitution",
        [ IsHomalgRingElement, IsList, IsList ],
        
  function( p, V, O )
    local lv, lo, R, i, RP, L;
    
    lv := Length( V );
    lo := Length( O );
    
    if not ( lv > 0 and lo = lv ) then
        Error( "Second and third parameters should be nonempty lists of same size\n" );
    fi;
    
    R := HomalgRing( p );
    
    Perform( [ 1 .. lo ],
      function( i )
        if not IsHomalgRingElement( O[ i ] ) then
            O[ i ] := O[ i ] / R;
        fi;
    end );
    
    if not ( ForAll( [ 1 .. lv ], i -> IsIdenticalObj( R, HomalgRing( V[ i ] ) ) ) and ForAll( [ 1 .. lo ], i -> IsIdenticalObj( R, HomalgRing( O[ i ] ) ) ) ) then
        Error( "All the elements of the list should be in same ring\n" );
    fi;
    
    if not ForAll( V, i -> i in Indeterminates( R ) ) then
        Error( "entries in the second parameter should be ring variables\n" );
    fi;
    
    if not ForAll( O, i -> IsHomalgRingElement( i ) ) then
        Error( "entries in the third parameter should be ring elements\n" );
    fi;
    
    RP := homalgTable( R );
    
    if not IsBound(RP!.Evaluate) then
        Error( "table entry Evaluate not found\n" );
    fi;
    
    L := [ ];
    
    for i in [ 1 .. lv ] do
        L[ 2*i-1 ] := V[ i ];
        L[ 2*i ] := O[ i ];
    od;
    
    return RingElementConstructor( R )( RP!.Evaluate( p, L ), R );
    
end );

##
InstallMethod( Value,
        "for a homalg matrix and two lists",
        [ IsHomalgMatrix, IsList, IsList ],
        
  function( M, V, O )
    local R, RP, r, c, L, lv, MM, i, j;
    
    R := HomalgRing( M );
    RP := homalgTable( R );
    
    r := NumberRows( M );
    c := NumberColumns( M );
    
    if IsBound( RP!.EvaluateMatrix ) then
        
        L := [ ];
        
        lv := Length( V );
        
        for i in [ 1 .. lv ] do
            L[ 2*i-1 ] := V[ i ];
            L[ 2*i ] := O[ i ];
        od;
        
        return HomalgMatrix( RP!.EvaluateMatrix( M, L ), r, c, R );
        
    fi;
    
    #=====# the fallback method #=====#
    
    MM := HomalgInitialMatrix( r, c, HomalgRing( M ) );
    
    for i in [ 1 .. r ] do
        for j in [ 1 .. c ] do
            MM[ i, j ] := Value( M[ i, j ], V, O );
        od;
    od;
    
    MakeImmutable( MM );
    
    return MM;
    
end );

##
InstallMethod( Value,
        "polynomial substitution",
        [ IsObject, IsHomalgRingElement, IsRingElement ],
        
  function( p, v, o )
    
    return Value( p, [ v ], [ o ] );
    
end );

##
InstallMethod( Value,
        "polynomial substitution",
        [ IsObject, IsHomalgRingElement ],
        
  function( p, v )
    
    return o -> Value( p, v, o );
    
end );

##
InstallMethod( Numerator,
        "for homalg ring element",
        [ IsHomalgRingElement ],
        
  function( p )
    local R, RP, l;
    
    R :=  HomalgRing( p );
    
    if IsBound( p!.Numerator ) then
        return p!.Numerator;
    fi;
    
    RP := homalgTable( R );
    
    if not IsBound( RP!.NumeratorAndDenominatorOfPolynomial ) then
        Error( "table entry for NumeratorAndDenominatorOfPolynomial not found\n" );
    fi;
    
    l := RP!.NumeratorAndDenominatorOfPolynomial( p );
    
    p!.Numerator := l[1];
    p!.Denominator := l[2];
    
    return l[1];
    
end );

##
InstallMethod( Denominator,
        "for homalg ring element",
        [ IsHomalgRingElement ],
        
  function( p )
    
    if not IsBound( p!.Denominator ) then
        ## this will trigger setting p!.Denominator
        Numerator( p );
    fi;
    
    return p!.Denominator;
    
end );

##
InstallMethod( Denominator,
        "for homalg matrices",
        [ IsHomalgMatrix ],
        
  function( M )
    
    if not IsBound( M!.Denominator ) then
        ## this will trigger setting M!.Denominator
        Numerator( M );
    fi;
    
    return M!.Denominator;
    
end );

##
InstallMethod( Value,
        "for a homalg matrix and two lists",
        [ IsHomalgMatrix, IsList, IsList ],
        
  function( M, V, O )
    local R, r, c, MM, i, j;
    
    R := HomalgRing( M );
    
    #=====# the fallback method #=====#
    
    r := NumberRows( M );
    c := NumberColumns( M );
    
    MM := HomalgInitialMatrix( r, c, HomalgRing( M ) );
    
    for i in [ 1 .. r ] do
        for j in [ 1 .. c ] do
            MM[ i, j ] := Value( M[ i, j ], V, O );
        od;
    od;
    
    MakeImmutable( MM );
    
    return MM;
    
end );

##
InstallMethod( MonomialMatrixWeighted,
        "for homalg rings",
        [ IsInt, IsHomalgRing, IsList ],
        
  function( d, R, weights )
    local dd, set_weights, RP, vars, mon;
    
    RP := homalgTable( R );
    
    if not Length( weights ) = Length( Indeterminates( R ) ) then
        Error( "there must be as many weights as indeterminates\n" );
    fi;
    
    set_weights := Set( weights );
    
    if set_weights = [1] or set_weights = [0,1] then
        dd := d;
    elif set_weights = [-1] or set_weights = [-1,0] then
        dd := -d;
    else
        Error( "Only weights -1, 0 or 1 are accepted. The weights -1 and 1 must not appear at once." );
    fi;
    
    if dd < 0 then
        return HomalgZeroMatrix( 0, 1, R );
    fi;
    
    vars := Indeterminates( R );

    if HasIsExteriorRing( R ) and IsExteriorRing( R ) and dd > Length( vars ) then
        return HomalgZeroMatrix( 0, 1, R );
    fi;
    
    if not ( set_weights = [ 1 ] or set_weights = [ -1 ] ) then
        
        ## the variables of weight 1 or -1
        vars := vars{Filtered( [ 1 .. Length( weights ) ], p -> weights[p] <> 0 )};
        
    fi;
    
    if IsBound(RP!.MonomialMatrix) then
        mon := RP!.MonomialMatrix( dd, vars, R );        ## the external object
        mon := HomalgMatrix( mon, R );
        SetNumberColumns( mon, 1 );
        if d = 0 then
            IsOne( mon );
        fi;
        
        return mon;
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called MonomialMatrix in the homalgTable of the external ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( MonomialMatrixWeighted,
        "for homalg rings",
        [ IsList, IsHomalgRing, IsList ],
        
  function( d, R, weights )
    local l, mon, w;
    
    if not Length( weights ) = Length( Indeterminates( R ) ) then
        Error( "there must be as many weights as indeterminates\n" );
    fi;
    
    l := Length( d );
    
    w := ListOfDegreesOfMultiGradedRing( l, R, weights );
    
    mon := List( [ 1 .. l ] , i -> MonomialMatrixWeighted( d[i], R, w[i] ) );
    
    return Iterated( mon, KroneckerMat );
    
end );

##
InstallMethod( ListOfDegreesOfMultiGradedRing,
        "for homalg rings",
        [ IsInt, IsHomalgRing, IsHomogeneousList ],
        
  function( l, R, weights )
    local indets, n, B, j, w, wlist, i, k;
    
    if l < 1 then
        Error( "the first argument must be a positiv integer\n" );
    fi;
    
    indets := Indeterminates( R );
    
    if not Length( weights ) = Length( indets ) then
        Error( "there must be as many weights as indeterminates\n" );
    fi;
    
    if IsList( weights[1] ) and Length( weights[1] ) = l then
        return List( [ 1 .. l ], i -> List( weights, w -> w[i] ) );
    fi;
    
    ## the rest handles the (improbable?) case of successive extensions
    ## without multiple weights
    
    if l = 1 then
        return [ weights ];
    fi;
    
    n := Length( weights );
    
    if not HasBaseRing( R ) then
        Error( "no 1. base ring found\n" );
    fi;
    
    B := BaseRing( R );
    j := Length( Indeterminates( B ) );
    
    w := Concatenation(
                 ListWithIdenticalEntries( j, 0 ),
                 ListWithIdenticalEntries( n - j, 1 )
                 );
    
    wlist := [ ListN( w, weights, \* ) ];
    
    for i in [ 2 .. l - 1 ] do
        
        if not HasBaseRing( B ) then
            Error( "no ", i, ". base ring found\n" );
        fi;
        
        B := BaseRing( B );
        k := Length( Indeterminates( B ) );
        
        w := Concatenation(
                     ListWithIdenticalEntries( k, 0 ),
                     ListWithIdenticalEntries( j - k, 1 ),
                     ListWithIdenticalEntries( n - j, 0 )
                     );
        
        Add( wlist, ListN( w, weights, \* ) );
        
        j := k;
        
    od;
    
    w := Concatenation(
                 ListWithIdenticalEntries( j, 1 ),
                 ListWithIdenticalEntries( n - j, 0 )
                 );
    
    Add( wlist, ListN( w, weights, \* ) );
    
    return wlist;
    
end );

##
InstallMethod( RandomMatrixBetweenGradedFreeLeftModulesWeighted,
        "for homalg rings",
        [ IsList, IsList, IsHomalgRing, IsList ],
        
  function( degreesS, degreesT, R, weights )
    local RP, r, c, rand, i, j, mon;
    
    RP := homalgTable( R );
    
    r := Length( degreesS );
    c := Length( degreesT );
    
    if degreesT = [ ] then
        return HomalgZeroMatrix( 0, c, R );
    elif degreesS = [ ] then
        return HomalgZeroMatrix( r, 0, R );
    fi;
    
    if IsBound(RP!.RandomMatrix) then
        rand := RP!.RandomMatrix( R, degreesT, degreesS, weights );      ## the external object
        rand := HomalgMatrix( rand, r, c, R );
        return rand;
    fi;
    
    #=====# begin of the core procedure #=====#
    
    rand := [ 1 .. r * c ];
    
    for i in [ 1 .. r ] do
        for j in [ 1 .. c ] do
            mon := MonomialMatrixWeighted( degreesS[i] - degreesT[j], R, weights );
            mon := ( R * HomalgMatrix( RandomMat( 1, NumberRows( mon ) ), HOMALG_MATRICES.ZZ ) ) * mon;
            mon := mon[ 1, 1 ];
            rand[ ( i - 1 ) * c + j ] := mon;
        od;
    od;
    
    return HomalgMatrix( rand, r, c, R );
    
end );

##
InstallMethod( RandomMatrixBetweenGradedFreeRightModulesWeighted,
        "for homalg rings",
        [ IsList, IsList, IsHomalgRing, IsList ],
        
  function( degreesT, degreesS, R, weights )
    local RP, r, c, rand, i, j, mon;
    
    RP := homalgTable( R );
    
    r := Length( degreesT );
    c := Length( degreesS );
    
    if degreesT = [ ] then
        return HomalgZeroMatrix( 0, c, R );
    elif degreesS = [ ] then
        return HomalgZeroMatrix( r, 0, R );
    fi;
    
    if IsBound(RP!.RandomMatrix) then
        rand := RP!.RandomMatrix( R, degreesT, degreesS, weights );      ## the external object
        rand := HomalgMatrix( rand, r, c, R );
        return rand;
    fi;
    
    #=====# begin of the core procedure #=====#
    
    rand := [ 1 .. r * c ];
    
    for i in [ 1 .. r ] do
        for j in [ 1 .. c ] do
            mon := MonomialMatrixWeighted( degreesS[j] - degreesT[i], R, weights );
            mon := ( R * HomalgMatrix( RandomMat( 1, NumberRows( mon ) ), HOMALG_MATRICES.ZZ ) ) * mon;
            mon := mon[ 1, 1 ];
            rand[ ( i - 1 ) * c + j ] := mon;
        od;
    od;
    
    return HomalgMatrix( rand, r, c, R );
    
end );

##
InstallMethod( RandomMatrix,
        "for three integers, a homalg ring, and a list",
        [ IsInt, IsInt, IsInt, IsHomalgRing, IsList ],
        
  function( r, c, d, R, weights )
    local degreesS, degreesT;
    
    degreesS := ListWithIdenticalEntries( r, d );
    degreesT := ListWithIdenticalEntries( c, 0 );

    return RandomMatrixBetweenGradedFreeLeftModulesWeighted( degreesS, degreesT, R, weights );
    
end );

##
InstallMethod( RandomMatrix,
        "for three integers and a homalg ring",
        [ IsInt, IsInt, IsInt, IsHomalgRing ],
        
  function( r, c, d, R )
    local weights;
    
    weights := ListWithIdenticalEntries( Length( Indeterminates( R ) ), 1 );

    return RandomMatrix( r, c, d, R, weights );
    
end );

##
## params = [ min_deg,max_deg,ze,zt,coeffs ]
##
## min_deg and max_deg determine the minimal and maximal degree of an entry in the matrix
## ze determines the percentage of the zero entries in the matrix (The default value is 50)
## zt determines the percentage of the zero terms in each entry in the matrix (The default value is 80)
## The non-trivial coefficients of each entry belong to the interval [ 1 .. coeffs ]  (The default value is 10)
##
InstallOtherMethod( RandomMatrix,
        "for two integers, a homalg ring and a list",
        [ IsInt, IsInt, IsHomalgRing, IsList ],
  function( r, c, R, params )
    local RP;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.RandomMat) then
        return HomalgMatrix( CallFuncList( RP!.RandomMat, Concatenation( [ R, r, c ], params ) ), r, c, R );
    else
        TryNextMethod();
    fi;
    
end );

##
InstallMethod( RandomMatrix,
        "for two integers and a homalg ring",
        [ IsInt, IsInt, IsHomalgRing ],
        
  function( r, c, R )
    local RP, params;
    
    # Some CAS are having a really hard time creating random empty matrices. Too many choices to make?...
    if r = 0 or c = 0 then
        
        return HomalgZeroMatrix( r, c, R );
        
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.RandomMat) then
        
        params := [ 0, Random( [ 1, 1, 1, 2, 2, 2, 3 ] ), 50, 80, 50 ];
        
        return RandomMatrix( r, c, R, params );
        
    fi;
    
    if not IsBound(RP!.RandomPol) then
        TryNextMethod( );
    fi;
    
    return HomalgMatrix( List( [ 1 .. r * c ], a -> Random( R ) ), r, c, R );
    
end );

##
InstallMethod( RandomMatrix,
        "for two integers and an internal homalg ring",
        [ IsInt, IsInt, IsHomalgInternalRingRep ],
        
  function( r, c, R )
    
    return HomalgMatrix( RandomMat( r, c, R!.ring ), r, c, R );
    
end );

##
InstallMethod( GeneralLinearCombination,
        "for a homalg ring, an integer, a list and an integer",
        [ IsHomalgRing, IsInt, IsList, IsInt ],
        
  function( R, bound, weights, n )
    local mat, m, s, B, A, r, i, indets;
    
    if n = 0 then
        return [ ];
    fi;
    
    mat := MonomialMatrixWeighted( 0, R, weights );
    
    for i in [ 1 .. bound ] do
        
        mat := UnionOfRows( mat, MonomialMatrixWeighted( i, R, weights ) );
        
    od;
    
    m := NumberRows( mat );
    
    # todo: better names for the bi: use the corresponding degree of the monomial
    s := List( [ 1 .. n ], i -> Concatenation( "b_", String( i ), "_0..", String( m - 1 ) ) );
    
    s := JoinStringsWithSeparator( s );
    
    if HasRelativeIndeterminatesOfPolynomialRing( R ) then
        indets := RelativeIndeterminatesOfPolynomialRing( R );
        B := BaseRing( R );
    elif HasIndeterminatesOfPolynomialRing( R ) then
        indets := IndeterminatesOfPolynomialRing( R );
        B := CoefficientsRing( R );
    elif HasRingRelations( R ) then
        B := CoefficientsRing( AmbientRing( R ) );
        indets := Indeterminates( AmbientRing( R ) );
        
    else
        indets := [ ];
        B := R;
    fi;
    
    B := ( B * s );
    
    A := B * indets;
    
    if HasRingRelations( R ) then
        A := A / ( A * RingRelations( R ) );
    fi;
    
    if HasRelativeIndeterminatesOfPolynomialRing( B ) then
        indets := RelativeIndeterminatesOfPolynomialRing( B );
    else
        indets := IndeterminatesOfPolynomialRing( B );
    fi;
    
    indets := List( indets, a -> a / A );
    
    indets := ListToListList( indets, n, m );
    
    indets := List( indets, l -> HomalgMatrix( l, 1, m, A ) );
    
    mat := A * mat;
    
    r := List( indets, i -> i * mat[ 1, 1 ] );
    
    return List( r, rr -> rr / A );
    
end );

##
InstallMethod( GetMonic,
        "for a homalg matrix and a positive integer",
        [ IsHomalgMatrix, IsPosInt ],
        
  function( M, i )
    local R, indets, l, B, newR, m, n, p, q, f, coeffs;
    
    R := HomalgRing( M );
    
    if HasRelativeIndeterminatesOfPolynomialRing( R ) then
        indets := RelativeIndeterminatesOfPolynomialRing( R );
        B := BaseRing( R );
    elif HasIndeterminatesOfPolynomialRing( R ) then
        indets := IndeterminatesOfPolynomialRing( R );
        B := CoefficientsRing( R );
    else
        Error( "the ring is not a polynomial ring\n" );
    fi;

    l := [ 1 .. Length( indets ) ];
    Remove( l, i );
    
    newR := ( B * indets{l} ) * [ indets[i] ];
    
    M := newR * M;
    
    m := NumberRows( M );
    n := NumberColumns( M );
    
    for p in [ 1 .. m ] do
        for q in [ 1 .. n ] do
            
            f := M[ p, q ];
            
            if IsMonic( f ) then
                return [ f, [ p, q ] ];
            fi;
            
        od;
    od;
    
    return fail;
    
end );

##
InstallMethod( GetMonic,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, indets, i, l;
    
    R := HomalgRing( M );
    
    if HasRelativeIndeterminatesOfPolynomialRing( R ) then
        indets := RelativeIndeterminatesOfPolynomialRing( R );
    elif HasIndeterminatesOfPolynomialRing( R ) then
        indets := IndeterminatesOfPolynomialRing( R );
    else
        Error( "the ring is not a polynomial ring\n" );
    fi;

    for i in Reversed( [ 1 .. Length( indets ) ] ) do
        
        l := GetMonic( M, i );
        
        if not l = fail then
            return [ l[1], l[2], i ];
        fi;
        
    od;
    
    return fail;

end );

##
InstallMethod( GetMonicUptoUnit,
        "for a homalg matrix and a positive integer",
        [ IsHomalgMatrix, IsPosInt ],
        
  function( M, i )
    local R, indets, l, B, newR, m, n, p, q, f, coeffs;
    
    R := HomalgRing( M );
    
    if HasRelativeIndeterminatesOfPolynomialRing( R ) then
        indets := RelativeIndeterminatesOfPolynomialRing( R );
        B := BaseRing( R );
    elif HasIndeterminatesOfPolynomialRing( R ) then
        indets := IndeterminatesOfPolynomialRing( R );
        B := CoefficientsRing( R );
    else
        Error( "the ring is not a polynomial ring\n" );
    fi;

    l := [ 1 .. Length( indets ) ];
    Remove( l, i );
    
    newR := ( B * indets{l} ) * [ indets[i] ];
    
    M := newR * M;
    
    m := NumberRows( M );
    n := NumberColumns( M );
    
    for p in [ 1 .. m ] do
        for q in [ 1 .. n ] do
            
            f := M[ p, q ];
            
            if IsMonicUptoUnit( f ) then
                return [ f, [ p, q ] ];
            fi;
            
        od;
    od;
    
    return fail;
    
end );

##
InstallMethod( GetMonicUptoUnit,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, indets, i, l;
    
    R := HomalgRing( M );
    
    if HasRelativeIndeterminatesOfPolynomialRing( R ) then
        indets := RelativeIndeterminatesOfPolynomialRing( R );
    elif HasIndeterminatesOfPolynomialRing( R ) then
        indets := IndeterminatesOfPolynomialRing( R );
    else
        Error( "the ring is not a polynomial ring\n" );
    fi;

    for i in Reversed( [ 1 .. Length( indets ) ] ) do
        
        l := GetMonicUptoUnit( M, i );
        
        if not l = fail then
            return [ l[1], l[2], i ];
        fi;
        
    od;
    
    return fail;

end );

##
InstallMethod( Diff,
        "for homalg matrices",
        [ IsHomalgMatrix, IsHomalgMatrix ],
        
  function( D, N )
    local R, RP, diff;
    
    R := HomalgRing( D );
    
    if not IsIdenticalObj( R, HomalgRing( N ) ) then
        Error( "the two matrices must be defined over identically the same ring\n" );
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.Diff) then
        diff := RP!.Diff( D, N );
        if not IsHomalgMatrix( diff ) then
            diff := HomalgMatrix( diff, NumberRows( D ) * NumberRows( N ), NumberColumns( D ) * NumberColumns( N ), R );
        fi;
        return diff;
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called Diff ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( Diff,
        "for two homalg ring elements",
        [ IsHomalgRingElement, IsHomalgRingElement ],
        
  function( x, r )
    local R;
    
    R := HomalgRing( r );
    
    x := HomalgMatrix( [ x ], 1, 1, R );
    r := HomalgMatrix( [ r ], 1, 1, R );
    
    return MatElm( Diff( x, r ), 1, 1 );
    
end );

##
InstallMethod( Diff,
        "for a homalg ring element",
        [ IsHomalgRingElement ],
        
  function( r )
    local R, var, x;
    
    R := HomalgRing( r );
    var := IndeterminatesOfPolynomialRing( R );
    x := var[Length( var )];
    
    return Diff( x, r );
    
end );

##
InstallMethod( TangentSpaceByEquationsAtPoint,
        "for two homalg matrices",
        [ IsHomalgMatrix, IsHomalgMatrix ],
        
  function( M, x )
    local R, var, n, k, Tx, map, i, xi, Ri, m;
    
    if not NumberColumns( M ) = 1 then
        Error( "the number of columns of the first argument M is not 1\n" );
    elif not NumberColumns( M ) = 1 then
        Error( "the number of columns of the second argument x is not 1\n" );
    fi;
    
    R := HomalgRing( M );
    
    var := IndeterminatesOfPolynomialRing( R );
    
    n := Length( var );
    
    if not n = NumberRows( x ) then
        Error( "the number of rows of the second argument x is not the number of indeterminates ", n, "\n" );
    fi;
    
    k := CoefficientsRing( R );
    
    if not IsIdenticalObj( HomalgRing( x ), k ) then
        Error( "the second argument is not a matrix over the coefficients ring ", k );
    fi;
    
    Tx := HomalgZeroMatrix( NumberRows( M ), 0, k );
    
    for i in [ 1 .. n ] do
        
        xi := String( var[i] );
        
        Ri := k * xi;
        
        xi := HomalgMatrix( [ xi / Ri ], 1, 1, Ri );
        
        map := UnionOfRows( Ri * CertainRows( x, [ 1 .. i - 1 ] ), xi, Ri * CertainRows( x, [ i + 1 .. n ] ) );
        
        map := RingMap( map, R, Ri );
        
        m := Diff( xi, Pullback( map, M ) );
        
        map := CertainRows( x, [ i ] );
        
        map := RingMap( map, Ri, k );
        
        Tx := UnionOfColumns( Tx, Pullback( map, m ) );
        
    od;
    
    return BasisOfRows( Tx );
    
end );

##
InstallMethod( TangentSpaceByEquationsAtPoint,
        "for a homalg matrix and a list",
        [ IsHomalgMatrix, IsList ],
        
  function( M, x )
    local R, k;
    
    R := HomalgRing( M );
    
    k := CoefficientsRing( R );
    
    x := HomalgMatrix( x, Length( x ), 1, k );
    
    return TangentSpaceByEquationsAtPoint( M, x );
    
end );

##
InstallMethod( NoetherNormalization,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, r, char, indets, l, k, K, m, pos_char, rand_mat, rand_inv;
    
    R := HomalgRing( M );
    r := CoefficientsRing( R );
    
    char := Characteristic( r );
    
    if char > 0 and not IsPrimeInt( char ) then
        TryNextMethod( );
    elif not HasIndeterminatesOfPolynomialRing( R ) then
        TryNextMethod( );
    fi;
    
    indets := Indeterminates( R );
    
    l := Length( indets );
    
    indets := HomalgMatrix( indets, 1, l, R );
    
    if char > 0 then
        if HasRationalParameters( r ) then
            k := HomalgRingOfIntegers( char );
        else
            k := HomalgRingOfIntegers( char, DegreeOverPrimeField( r ) );
        fi;
        K := k!.ring;
    else
        k := HOMALG_MATRICES.ZZ;
    fi;
    
    m := GetMonicUptoUnit( M );
    
    if not m = fail then
        return [ M, m, true, true ];
    fi;
    
    pos_char := char > 0;
    
    repeat
        
        if pos_char then
            rand_mat := Random( SL( l, K ) );
        else
            if l = 1 then
                rand_mat := [ [ 1 ] ];
            else
                rand_mat := RandomUnimodularMat( l );
            fi;
        fi;
        
        rand_inv := rand_mat^-1;
        
        rand_mat := HomalgMatrix( rand_mat, k );
        rand_inv := HomalgMatrix( rand_inv, k );
        
        rand_mat := R * rand_mat;
        rand_inv := R * rand_inv;
        
        SetLeftInverse( rand_mat, rand_inv );
        SetRightInverse( rand_mat, rand_inv );
        SetLeftInverse( rand_inv, rand_mat );
        SetRightInverse( rand_inv, rand_mat );
        
        rand_mat := indets * rand_mat;
        rand_inv := indets * rand_inv;
        
        rand_mat := RingMap( rand_mat, R, R );
        
        M := Pullback( rand_mat, M );
        
        m := GetMonicUptoUnit( M );
        
    until not m = fail;
    
    rand_inv := RingMap( rand_inv, R, R );
    
    SetIsIsomorphism( rand_mat, true );
    SetIsIsomorphism( rand_inv, true );
    
    return [ M, m, rand_mat, rand_inv ];
    
end );

##
InstallMethod( Inequalities,
        "for a homalg ring",
        [ IsHomalgRing ],
        
  function( R )
    local r, RP, J;
    
    r := R;
    
    RP := homalgTable( R );
    
    if not IsBound(RP!.Inequalities) then
        Error( "could not find a procedure called Inequalities in the homalgTable\n" );
    fi;
    
    J := RP!.Inequalities( R );
    
    J := DuplicateFreeList( J );
    
    if HasIsFieldForHomalg( R ) and IsFieldForHomalg( R ) then
        r := R;
    else
        r := CoefficientsRing( R );
    fi;
    
    r := AssociatedPolynomialRing( r );
    
    J := List( J, a -> a / r );
    
    if IsBound( R!.Inequalities ) then
        Append( J, R!.Inequalities );
    fi;
    
    J := DuplicateFreeList( J );
    
    R!.Inequalities := J;
    
    return J;
    
end );

##
InstallMethod( ClearDenominatorsRowWise,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, m, n, coeffs;
    
    if IsZero( M ) then
        return M;
    elif IsOne( M ) then
        return M;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    m := NumberRows( M );
    n := NumberColumns( M );
    
    if IsBound(RP!.ClearDenominatorsRowWise) then
        return HomalgMatrix( RP!.ClearDenominatorsRowWise( M ), m, n, R ); ## the external object
    fi;
    
    #=====# begin of the core procedure #=====#
    
    coeffs := EntriesOfHomalgMatrixAsListList( M );
    
    coeffs := List( coeffs, a -> Concatenation( List( a, b -> EntriesOfHomalgMatrix( Coefficients( b ) ) ) ) );
    
    coeffs := List( coeffs, a -> List( a, b -> DenominatorRat( Rat( String( b ) ) ) ) );
    
    coeffs := List( coeffs, Lcm );
    
    coeffs := HomalgDiagonalMatrix( List( coeffs, a -> a / R ) );
    
    return coeffs * M;
    
end );


##
InstallMethod( MaximalDegreePart,
        "for a homalg ring element",
        [ IsHomalgRingElement ],
        
  function( r )
    local R, RP, var, B, base, weights, d, coeffs, monoms, plist;
    
    if IsZero( r ) then
        return r;
    fi;
    
    R := HomalgRing( r );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.MaximalDegreePart) then
        if HasRelativeIndeterminatesOfPolynomialRing( R ) then
            var := RelativeIndeterminatesOfPolynomialRing( R );
            B := BaseRing( R );
            if HasIndeterminatesOfPolynomialRing( B ) then
                base := IndeterminatesOfPolynomialRing( B );
            else
                base := [ ];
            fi;
            weights := Concatenation( ListWithIdenticalEntries( Length( base ), 0 ), ListWithIdenticalEntries( Length( var ), 1 ) );
        else
            var := RelativeIndeterminatesOfPolynomialRing( R );
            weights := ListWithIdenticalEntries( Length( var ), 1 );
        fi;
        
        return RingElementConstructor( R )( RP!.MaximalDegreePart( r, weights ), R );
        
    fi;
    
    d := Degree( r );
    
    coeffs := Coefficients( r );
    
    monoms := coeffs!.monomials;
    
    coeffs := EntriesOfHomalgMatrix( coeffs );
    
    plist := Positions( List( monoms, Degree ), d );
    
    return coeffs{plist} * monoms{plist};
    
end );

##
InstallMethod( MaximalDegreePartOfColumnMatrix,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R;
    
    if not NumberColumns( M ) = 1 then
        Error( "the number of columns is not 1\n" );
    fi;
    
    if IsZero( M ) then
        return M;
    fi;
    
    R := HomalgRing( M );
    
    M := List( EntriesOfHomalgMatrix( M ), MaximalDegreePart );
    
    return HomalgMatrix( M, Length( M ), 1, R );
    
end );

##
InstallMethod( LeadingModule,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( mat )
    local R, RP, lead;
    
    ## we are in the left module convention, i.e., row convention;
    ## so ideals are passed as a one column matrix
    
    R := HomalgRing( mat );
    
    RP := homalgTable( R );
    
    if IsBound(RP!.LeadingModule) then
        lead := RP!.LeadingModule( mat );
    elif IsBound(RP!.LeadingIdeal) then
        if not NumberColumns( mat ) = 1 then
            Error( "the matrix of generators of the ideal should be a one column matrix\n" );
        fi;
        lead := RP!.LeadingIdeal( mat );
    else
        Error( "could not find a procedure called LeadingModule (or LeadingIdeal) ",
               "in the homalgTable of the ring\n" );
    fi;
    
    return HomalgMatrix( lead, NumberRows( mat ), NumberColumns( mat ), R );
    
end );

##
InstallGlobalFunction( VariableForHilbertPoincareSeries,
  function( arg )
    local s;
    
    if not IsBound( HOMALG_MATRICES.variable_for_Hilbert_Poincare_series ) then
        
        if Length( arg ) > 0 and IsString( arg[1] ) then
            s := arg[1];
        else
            s := "s";
        fi;
        
        s := Indeterminate( Rationals, s );
        
        HOMALG_MATRICES.variable_for_Hilbert_Poincare_series := s;
    fi;
    
    return HOMALG_MATRICES.variable_for_Hilbert_Poincare_series;
    
end );

##
InstallGlobalFunction( VariableForHilbertPolynomial,
  function( arg )
    local t;
    
    if not IsBound( HOMALG_MATRICES.variable_for_Hilbert_polynomial ) then
        
        if Length( arg ) > 0 and IsString( arg[1] ) then
            t := arg[1];
        else
            t := "t";
        fi;
        
        t := Indeterminate( Rationals, t );
        
        HOMALG_MATRICES.variable_for_Hilbert_polynomial := t;
    fi;
    
    return HOMALG_MATRICES.variable_for_Hilbert_polynomial;
    
end );

##
InstallGlobalFunction( CoefficientsOfLaurentPolynomialsWithRange,
  function( poly )
    local coeffs, ldeg;
    
    coeffs := CoefficientsOfLaurentPolynomial( poly );
    
    ldeg := coeffs[2];
    
    coeffs := coeffs[1];
    
    return [ coeffs, [ ldeg .. ldeg + Length( coeffs ) - 1 ] ];
    
end );

##
InstallGlobalFunction( SumCoefficientsOfLaurentPolynomials,
  function( arg )
    local s, sum;
    
    s := VariableForHilbertPolynomial( );
    
    sum := Sum( arg, a -> Sum( [ 1 .. Length( a[2] ) ], i -> a[1][i] * s^a[2][i] ) ) + 0 * s;
    
    return CoefficientsOfLaurentPolynomialsWithRange( sum );
    
end );

##
InstallMethod( CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries,
        "for a homalg matrix and two lists",
        [ IsHomalgMatrix, IsList, IsList ],
        
  function( M, weights, degrees )
    local c, save, R, RP, t, zero_cols, free, hilb_free, non_zero_cols, hilb, l, ldeg;
    
    c := String( [ weights, degrees ] );
    
    if IsBound( M!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) then
        save := M!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries;
        if IsBound( save.(c) ) then
            return save.(c);
        fi;
    else
        save := rec( );
        M!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries := save;
    fi;
    
    if NumberColumns( M ) <> Length( degrees ) then
        Error( "the number of columns must coincide with the number of degrees\n" );
    fi;
    
    R := HomalgRing( M );
    
    if Length( Indeterminates( R ) ) <> Length( weights ) then
        Error( "the number of indeterminates must coincide with the number of weights\n" );
    fi;
    
    ## take care of n x 0 matrices
    if NumberColumns( M ) = 0 then
        save.(c) := [ [ ], [ ] ];
        return save.(c);
    fi;
    
    RP := homalgTable( R );
    
    if IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) and
       Set( weights ) = [ 1 ] and Length( Set( degrees ) ) = 1 then
        
        t := Set( degrees )[ 1 ];
        
        ## the coefficients of the unreduced untwisted numerator
        ## differ from the twisted ones below by a shift by t
        hilb := CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries( M );
        
        if hilb = [ ] then
            ## the degenerate case
            hilb := [ [ ], [ ] ];
        else
            hilb := [ hilb, [ t .. Length( hilb ) - 1 + t ] ];
        fi;
        
        save.(c) := hilb;
        
        return save.(c);
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) then
        
        zero_cols := ZeroColumns( M );
        
        if zero_cols <> [ ] and
           not ( IsZero( M ) and NumberRows( M ) = 1 and NumberColumns( M ) = 1 ) then ## avoid infinite loops
            ## take care of matrices with zero columns, especially of 0 x n matrices
            
            free := HomalgZeroMatrix( 1, 1, R );
            
            hilb_free := CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries( free, weights, [ 0 * degrees[zero_cols[1]] ] );
            
            l := Length( hilb_free[1] );
            
            hilb_free := List( degrees{zero_cols}, d -> [ hilb_free[1], [ d .. d + l - 1 ] ] );
            
            hilb_free := CallFuncList( SumCoefficientsOfLaurentPolynomials, hilb_free );
            
            if IsZero( M ) then
                save.(c) := hilb_free;
                return save.(c);
            fi;
            
            non_zero_cols := NonZeroColumns( M );
            
            M := CertainColumns( M, non_zero_cols );
            
            degrees := degrees{non_zero_cols};
            
        else
            
            hilb_free := [ [ ], [ ] ];
            
        fi;
        
        hilb := RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries( M, weights, degrees );
        
        l := Length( hilb ) - 1;
        
        if l = 0 or l = -1 then
            ## the degenerate case
            hilb := [ [ ], [ ] ];
            
        else
            
            ldeg := hilb[l + 1];
            
            hilb := hilb{[ 1 .. l ]};
            
            t := PositionNonZero( hilb );
            
            hilb := hilb{[ t .. l ]};
            
            ldeg := ldeg + t - 1;
            
            hilb := [ hilb, [ ldeg .. l - t + ldeg ] ];
            
        fi;
        
        hilb := SumCoefficientsOfLaurentPolynomials( hilb, hilb_free );
        
        save.(c) := hilb;
        
        return save.(c);
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, free, r, hilb_free, hilb;
    
    ## take care of n x 0 matrices
    if NumberColumns( M ) = 0 then
        return [ ];
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) then
        
        free := ZeroColumns( M );
        
        if free <> [ ] and
           not ( IsZero( M ) and NumberRows( M ) = 1 and NumberColumns( M ) = 1 ) then ## avoid infinite loops
            ## take care of matrices with zero columns, especially of 0 x n matrices
            
            r := Length( free );
            
            if not IsBound( R!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries_of_free_rank_1 ) then
                free := HomalgZeroMatrix( 1, 1, R );
                R!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries_of_free_rank_1 :=
                  CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries( free );
            fi;
            
            hilb_free := r * R!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries_of_free_rank_1;
            
            if IsZero( M ) then
                return hilb_free;
            fi;
            
            M := CertainColumns( M, NonZeroColumns( M ) );
            
        else
            
            if not ( IsZero( M ) and NumberRows( M ) = 1 and NumberColumns( M ) = 1 ) then
                M := BasisOfRowModule( M );
            fi;
            
            hilb_free := 0;
            
        fi;
        
        return hilb_free + RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries( M );
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( CoefficientsOfNumeratorOfHilbertPoincareSeries,
        "for a rational function",
        [ IsRationalFunction ],
        
  function( series )
    local denom, ldeg, lowest_coeff, s, numer;
    
    if IsZero( series ) then
        return [ [ ], [ ] ];
    fi;
    
    denom := DenominatorOfRationalFunction( series );
    
    denom := CoefficientsOfUnivariatePolynomial( denom );
    
    ldeg := PositionNonZero( denom ) - 1;
    
    lowest_coeff := denom[ldeg + 1];
    
    if not lowest_coeff in [ 1, -1 ] then
        Error( "expected the lowest coefficient of the denominator of the Hilbert-Poincare series to be 1 or -1 but received ", lowest_coeff, "\n" );
    fi;
    
    s := IndeterminateOfUnivariateRationalFunction( series );
    
    numer := NumeratorOfRationalFunction( series ) / ( lowest_coeff * s^ldeg );
    
    return CoefficientsOfLaurentPolynomialsWithRange( numer );
    
end );

##
InstallMethod( CoefficientsOfNumeratorOfHilbertPoincareSeries,
        "for a rational function and the integer 0",
        [ IsRationalFunction, IsInt and IsZero ],
        
  function( series, i ) ## i = 0
    local coeffs;
    
    coeffs := CoefficientsOfNumeratorOfHilbertPoincareSeries( series );
    
    if coeffs[2] = [ ] then
        coeffs := coeffs[1];
    elif coeffs[2][1] = 0 then
        coeffs := coeffs[1];
    elif coeffs[2][1] > 0 then
        coeffs := Concatenation( ListWithIdenticalEntries( coeffs[2][1], 0 * coeffs[1][1] ), coeffs[1] );
    else
        Error( "expected CoefficientsOfNumeratorOfCoeffsertPoincareSeries to indicate a polynomial and not of a Laurent polynomial: ", coeffs );
    fi;
    
    return coeffs;
    
end );

##
InstallMethod( CoefficientsOfNumeratorOfHilbertPoincareSeries,
        "for a homalg matrix and two lists",
        [ IsHomalgMatrix, IsList, IsList ],
  ## the fallback method
  ReturnFail );

##
InstallMethod( CoefficientsOfNumeratorOfHilbertPoincareSeries,
        "for a homalg matrix and two lists",
        [ IsHomalgMatrix, IsList, IsList ],
        
  function( M, weights, degrees )
    local c, save, R, RP, t, free, s, hilb, d;
    
    c := String( [ weights, degrees ] );
    
    if IsBound( M!.CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries ) then
        save := M!.CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries;
        if IsBound( save.(c) ) then
            return save.(c);
        fi;
    else
        save := rec( );
        M!.CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries := save;
    fi;
    
    if NumberColumns( M ) <> Length( degrees ) then
        Error( "the number of columns must coincide with the number of degrees\n" );
    fi;
    
    R := HomalgRing( M );
    
    if Length( Indeterminates( R ) ) <> Length( weights ) then
        Error( "the number of indeterminates must coincide with the number of weights\n" );
    fi;
    
    ## take care of n x 0 matrices
    if NumberColumns( M ) = 0 then
        save.(c) := [ [ ], [ ] ];
        return save.(c);
    fi;
    
    RP := homalgTable( R );
    
    if ( IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) or
         IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries ) ) and
       Set( weights ) = [ 1 ] and Length( Set( degrees ) ) = 1 then
        
        t := Set( degrees )[ 1 ];
        
        ## the coefficients of the untwisted numerator
        ## differ from the twisted ones below by a shift by t
        hilb := CoefficientsOfNumeratorOfHilbertPoincareSeries( M );
        
        if hilb = [ ] then
            ## the degenerate case
            hilb := [ [ ], [ ] ];
        else
            hilb := [ hilb, [ t .. Length( hilb ) - 1 + t ] ];
        fi;
        
        save.(c) := hilb;
        
        return save.(c);
        
    elif IsBound( RP!.CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries ) then
        
        if IsZero( M ) then
            ## take care of zero matrices, especially of 0 x n matrices
            free := HomalgZeroMatrix( 1, NumberColumns( M ), R );
            hilb := RP!.CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries( free, weights, degrees );
        else
            hilb := RP!.CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries( M, weights, degrees );
        fi;
        
        save.(c) := hilb;
        
        return hilb;
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) then
        
        s := VariableForHilbertPoincareSeries( );
        
        hilb := HilbertPoincareSeries( M, weights, degrees, s );
        
        save.(c) := CoefficientsOfNumeratorOfHilbertPoincareSeries( hilb );
        
        return save.(c);
        
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( CoefficientsOfNumeratorOfHilbertPoincareSeries,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, free, hilb, lowest_coeff;
    
    ## take care of n x 0 matrices
    if NumberColumns( M ) = 0 then
        return [ ];
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries ) then
        
        if IsZero( M ) then
            ## take care of zero matrices, especially of 0 x n matrices
            free := HomalgZeroMatrix( 1, 1, R );
            hilb := RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries( free );
            return NumberColumns( M ) * hilb;
        else
            return RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries( M );
        fi;
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) then
        
        hilb := HilbertPoincareSeries( M );
        
        return CoefficientsOfNumeratorOfHilbertPoincareSeries( hilb, 0 );
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called CoefficientsOfNumeratorOfHilbertPoincareSeries ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
end );

##
InstallMethod( UnreducedNumeratorOfHilbertPoincareSeries,
        "for a homalg matrix, two lists, and a ring element",
        [ IsHomalgMatrix, IsList, IsList, IsRingElement ],
        
  function( M, weights, degrees, lambda )
    local R, RP, t, hilb, range;
    
    ## take care of n x 0 matrices
    if NumberColumns( M ) = 0 then
        return 0 * lambda;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) and
       Set( weights ) = [ 1 ] and Length( Set( degrees ) ) = 1 then
        
        t := Set( degrees )[ 1 ];
        
        ## the unreduced numerator of the untwisted Hilbert-Poincaré series
        ## differs from the twisted one by the factor lambda^t
        hilb := UnreducedNumeratorOfHilbertPoincareSeries( M );
        
        return lambda^t * hilb;
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) then
        
        hilb := CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries( M, weights, degrees );
        
        range := hilb[2];
        hilb := hilb[1];
        
        hilb := Sum( [ 1 .. Length( range ) ], i -> hilb[i] * lambda^range[i] );
        
        return hilb + 0 * lambda;
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( UnreducedNumeratorOfHilbertPoincareSeries,
        "for a homalg matrix and two lists",
        [ IsHomalgMatrix, IsList, IsList ],
        
  function( M, weights, degrees )
    
    return UnreducedNumeratorOfHilbertPoincareSeries( M, weights, degrees, VariableForHilbertPoincareSeries( ) );
    
end );

##
InstallMethod( UnreducedNumeratorOfHilbertPoincareSeries,
        "for a homalg matrix and a ring element",
        [ IsHomalgMatrix, IsRingElement ],
        
  function( M, lambda )
    local R, RP, hilb;
    
    ## take care of n x 0 matrices
    if NumberColumns( M ) = 0 then
        return 0 * lambda;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) then
        
        hilb := CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries( M );
        
        hilb := Sum( [ 0 .. Length( hilb ) - 1 ], k -> hilb[k+1] * lambda^k );
        
        return hilb + 0 * lambda;
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( UnreducedNumeratorOfHilbertPoincareSeries,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    
    return UnreducedNumeratorOfHilbertPoincareSeries( M, VariableForHilbertPoincareSeries( ) );
    
end );

##
InstallMethod( NumeratorOfHilbertPoincareSeries,
        "for a homalg matrix, two lists, and a ring element",
        [ IsHomalgMatrix, IsList, IsList, IsRingElement ],
        
  function( M, weights, degrees, lambda )
    local R, RP, t, hilb, range;
    
    ## take care of n x 0 matrices
    if NumberColumns( M ) = 0 then
        return 0 * lambda;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if ( IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) or
         IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries ) ) and
       Set( weights ) = [ 1 ] and Length( Set( degrees ) ) = 1 then
        
        t := Set( degrees )[ 1 ];
        
        ## the numerator of the untwisted Hilbert-Poincaré series
        ## differs from the twisted one by the factor lambda^t
        hilb := NumeratorOfHilbertPoincareSeries( M );
        
        return lambda^t * hilb;
        
    elif IsBound( RP!.CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries ) or
      IsBound( RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) then
        
        hilb := CoefficientsOfNumeratorOfHilbertPoincareSeries( M, weights, degrees );
        
        if hilb = [ [ ], [ ] ] then
            return 0 * lambda;
        fi;
        
        range := hilb[2];
        hilb := hilb[1];
        
        hilb := Sum( [ 1 .. Length( range ) ], i -> hilb[i] * lambda^range[i] );
        
        return hilb + 0 * lambda;
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( NumeratorOfHilbertPoincareSeries,
        "for a homalg matrix and two lists",
        [ IsHomalgMatrix, IsList, IsList ],
        
  function( M, weights, degrees )
    
    return NumeratorOfHilbertPoincareSeries( M, weights, degrees, VariableForHilbertPoincareSeries( ) );
    
end );

##
InstallMethod( NumeratorOfHilbertPoincareSeries,
        "for a homalg matrix and a ring element",
        [ IsHomalgMatrix, IsRingElement ],
        
  function( M, lambda )
    local R, RP, hilb, lowest_coeff;
    
    ## take care of n x 0 matrices
    if NumberColumns( M ) = 0 then
        return 0 * lambda;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries ) then
        
        hilb := CoefficientsOfNumeratorOfHilbertPoincareSeries( M );
        
        hilb := Sum( [ 0 .. Length( hilb ) - 1 ], k -> hilb[k+1] * lambda^k );
        
        return hilb + 0 * lambda;
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) then
        
        hilb := HilbertPoincareSeries( M );
        
        lowest_coeff := function( f );
            return First( CoefficientsOfUnivariatePolynomial( f ), a -> not IsZero( a ) );
        end;
        
        lowest_coeff := lowest_coeff( DenominatorOfRationalFunction( hilb ) );
        
        if not lowest_coeff in [ 1, -1 ] then
            Error( "expected the lowest coefficient of the denominator of the Hilbert-Poincare series to be 1 or -1 but received ", lowest_coeff, "\n" );
        fi;
        
        return NumeratorOfRationalFunction( hilb ) / lowest_coeff;
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called CoefficientsOfNumeratorOfHilbertPoincareSeries ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( NumeratorOfHilbertPoincareSeries,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    
    return NumeratorOfHilbertPoincareSeries( M, VariableForHilbertPoincareSeries( ) );
    
end );

##
InstallMethod( HilbertPoincareSeries,
        "for a homalg matrix, two lists, and a ring element",
        [ IsHomalgMatrix, IsList, IsList, IsRingElement ],
  ## the fallback method
  ReturnFail );

##
InstallMethod( HilbertPoincareSeries,
        "for a homalg matrix, two lists, and a ring element",
        [ IsHomalgMatrix, IsList, IsList, IsRingElement ],
        
  function( M, weights, degrees, lambda )
    local R, RP, t, hilb, denom, n, d;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if ( IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) or
         IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries ) ) and
       Set( weights ) = [ 1 ] and Length( Set( degrees ) ) = 1 then
        
        t := Set( degrees )[ 1 ];
        
        ## the untwisted Hilbert-Poincaré series
        ## differs from the twisted one by the factor lambda^t
        hilb := HilbertPoincareSeries( M, lambda );
        
        return lambda^t * hilb;
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) then
        
        hilb := UnreducedNumeratorOfHilbertPoincareSeries( M, weights, degrees, lambda );
        
        denom := Product( weights, i -> ( 1 - lambda^i ) );
        
        return hilb / denom;
        
    elif IsBound( RP!.CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries ) then
        
        hilb := NumeratorOfHilbertPoincareSeries( M, weights, degrees, lambda );
        
        ## for CASs which do not support Hilbert* for non-graded modules
        d := AffineDimension( M, weights, degrees );
        
        return hilb / ( 1 - lambda )^d;
        
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( HilbertPoincareSeries,
        "for a homalg matrix, two lists, and a string",
        [ IsHomalgMatrix, IsList, IsList, IsString ],
        
  function( M, weights, degrees, lambda )
    
    return HilbertPoincareSeries( M, weights, degrees, Indeterminate( Rationals, lambda ) );
    
end );

##
InstallMethod( HilbertPoincareSeries,
        "for a homalg matrix and two lists",
        [ IsHomalgMatrix, IsList, IsList ],
        
  function( M, weights, degrees )
    
    return HilbertPoincareSeries( M, weights, degrees, VariableForHilbertPoincareSeries( ) );
    
end );

##
InstallMethod( HilbertPoincareSeries,
        "for a homalg matrix and a ring element",
        [ IsHomalgMatrix, IsRingElement ],
        
  function( M, lambda )
    local R, RP, hilb, n, d, weights, degrees;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if HasIsDivisionRingForHomalg( R ) and IsDivisionRingForHomalg( R ) then
        
        return ( NumberColumns( M ) - RowRankOfMatrix( M ) ) * lambda^0;
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) then
        
        hilb := UnreducedNumeratorOfHilbertPoincareSeries( M, lambda );
        
        n := Length( Indeterminates( R ) );
        
        return  hilb / ( 1 - lambda )^n;
        
    elif IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries ) then
        
        hilb := NumeratorOfHilbertPoincareSeries( M, lambda );
        
        d := AffineDimension( M );
        
        return hilb / ( 1 - lambda )^d;
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) then
        
        weights := ListWithIdenticalEntries( Length( Indeterminates( R ) ), 1 );
        
        degrees := ListWithIdenticalEntries( NumberColumns( M ), 0 );
        
        return HilbertPoincareSeries( LeadingModule( M ), weights, degrees, lambda );
        
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( HilbertPoincareSeries,
        "for a homalg matrix and a string",
        [ IsHomalgMatrix, IsString ],
        
  function( M, lambda )
    
    return HilbertPoincareSeries( M, Indeterminate( Rationals, lambda ) );
    
end );

##
InstallMethod( HilbertPoincareSeries,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    
    return HilbertPoincareSeries( M, VariableForHilbertPoincareSeries( ) );
    
end );

##
InstallGlobalFunction( _Binomial,
  function( a, b )
    local factorial;
    
    if b = 0 then
        ## ensure that the result has the type of a
        return 1 + 0 * a;
    elif b = 1 then
        return a;
    fi;
    
    factorial := Product( [ 0 .. b - 1 ], i -> a - i ) / Factorial( b );
    
    return factorial;
    
end );

##
InstallMethod( HilbertPolynomial,
        "for a list, an integer, and a ring element",
        [ IsList, IsInt, IsRingElement ],
        
  function( coeffs, d, s )
    local range, hilb;
    
    if d <= 0 then
        return 0 * s;
    fi;
    
    if ForAll( coeffs, IsList ) and Length( coeffs ) = 2 then
        ## the case: coeffs = CoefficientsOfNumeratorOfHilbertPoincareSeries( M, weights, degrees );
        
        range := coeffs[2];
        coeffs := coeffs[1];
        
        hilb := Sum( [ 1 .. Length( range ) ], i -> coeffs[i] * _Binomial( d - 1 + s - range[i], d - 1 ) );
        
    else
        ## the case: coeffs = CoefficientsOfNumeratorOfHilbertPoincareSeries( M );
        
        hilb := Sum( [ 0 .. Length( coeffs ) - 1 ], k -> coeffs[k+1] * _Binomial( d - 1 + s - k, d - 1 ) );
        
    fi;
    
    return hilb + 0 * s;
    
end );

##
InstallMethod( DimensionOfHilbertPoincareSeries,
        "for a rational function",
        [ IsRationalFunction ],
        
  function( series )
    local denom, hdeg, ldeg;
    
    if IsZero( series ) then
        return HOMALG_MATRICES.DimensionOfZeroModules;
    fi;
    
    denom := DenominatorOfRationalFunction( series );
    
    hdeg := Degree( denom );
    
    denom := CoefficientsOfUnivariatePolynomial( denom );
    
    ldeg := PositionNonZero( denom ) - 1;
    
    return hdeg - ldeg;
    
end );

##
InstallMethod( HilbertPolynomial,
        "for a list and an integer",
        [ IsList, IsInt ],
        
  function( coeffs, d )
    local s;
    
    s := VariableForHilbertPolynomial( );
    
    return HilbertPolynomial( coeffs, d, s );
    
end );

##
InstallMethod( HilbertPolynomialOfHilbertPoincareSeries,
        "for a rational function",
        [ IsRationalFunction ],
        
  function( series )
    local coeffs, d;
    
    coeffs := CoefficientsOfNumeratorOfHilbertPoincareSeries( series );
    
    d := DimensionOfHilbertPoincareSeries( series );
    
    return HilbertPolynomial( coeffs, d );
    
end );

##
InstallMethod( HilbertPolynomial,
        "for a homalg matrix, two lists, and a ring element",
        [ IsHomalgMatrix, IsList, IsList, IsRingElement ],
  ## the fallback method
  ReturnFail );

##
InstallMethod( HilbertPolynomial,
        "for a homalg matrix, two lists, and a ring element",
        [ IsHomalgMatrix, IsList, IsList, IsRingElement ],
        
  function( M, weights, degrees, lambda )
    local R, RP, t, hilb;
    
    ## take care of n x 0 matrices
    if NumberColumns( M ) = 0 then
        return 0 * lambda;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if ( IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) or
         IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries ) ) and
       Set( weights ) = [ 1 ] and Length( Set( degrees ) ) = 1 then
        
        t := Set( degrees )[ 1 ];
        
        ## the untwisted Hilbert polynomial
        ## differs from the twisted one by a shift by t
        hilb := HilbertPolynomial( M, lambda );
        
        return Value( hilb, lambda - t );
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) or
      IsBound( RP!.CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries ) then
        
        hilb := HilbertPoincareSeries( M, weights, degrees, lambda );
        
        return HilbertPolynomialOfHilbertPoincareSeries( hilb );
        
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( HilbertPolynomial,
        "for a homalg matrix, two lists, and a string",
        [ IsHomalgMatrix, IsList, IsList, IsString ],
        
  function( M, weights, degrees, lambda )
    
    return HilbertPolynomial( M, weights, degrees, Indeterminate( Rationals, lambda ) );
    
end );

##
InstallMethod( HilbertPolynomial,
        "for a homalg matrix and two lists",
        [ IsHomalgMatrix, IsList, IsList ],
        
  function( M, weights, degrees )
    
    return HilbertPolynomial( M, weights, degrees, VariableForHilbertPolynomial( ) );
    
end );

##
InstallMethod( HilbertPolynomial,
        "for a homalg matrix and a ring element",
        [ IsHomalgMatrix, IsRingElement ],
        
  function( M, lambda )
    local R, RP, free, hilb, weights, degrees;
    
    ## take care of n x 0 matrices
    if NumberColumns( M ) = 0 then
        return 0 * lambda;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if HasIsDivisionRingForHomalg( R ) and IsDivisionRingForHomalg( R ) then
        
        return ( NumberColumns( M ) - RowRankOfMatrix( M ) ) * lambda^0;
        
    elif IsBound( RP!.CoefficientsOfHilbertPolynomial ) then
        
        if IsZero( M ) then
            ## take care of zero matrices, especially of 0 x n matrices
            free := HomalgZeroMatrix( 1, 1, R );
            hilb := RP!.CoefficientsOfHilbertPolynomial( free );
            hilb := NumberColumns( M ) * hilb;
        else
            hilb := RP!.CoefficientsOfHilbertPolynomial( M );
        fi;
        
        hilb := Sum( [ 0 .. Length( hilb ) - 1 ], k -> hilb[k+1] * lambda^k );
        
        return hilb + 0 * lambda;
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) or
      IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries ) then
        
        hilb := HilbertPoincareSeries( M, lambda );
        
        return HilbertPolynomialOfHilbertPoincareSeries( hilb );
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) then
        
        weights := ListWithIdenticalEntries( Length( Indeterminates( R ) ), 1 );
        
        degrees := ListWithIdenticalEntries( NumberColumns( M ), 0 );
        
        return HilbertPolynomial( LeadingModule( M ), weights, degrees, lambda );
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called CoefficientsOfHilbertPolynomial ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( HilbertPolynomial,
        "for a homalg matrix and a string",
        [ IsHomalgMatrix, IsString ],
        
  function( M, lambda )
    
    return HilbertPolynomial( M, Indeterminate( Rationals, lambda ) );
    
end );

##
InstallMethod( HilbertPolynomial,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    
    return HilbertPolynomial( M, VariableForHilbertPolynomial( ) );
    
end );

## for CASs which do not support Hilbert* for non-graded modules
InstallMethod( AffineDimension,
        "for a homalg matrix and two lists",
        [ IsHomalgMatrix, IsList, IsList ],
        
  function( M, weights, degrees )
    local R, RP, free, hilb, d;
    
    if HasAffineDimension( M ) then
        return AffineDimension( M );
    fi;
    
    R := HomalgRing( M );
    
    if NumberColumns( M ) = 0 then
        ## take care of n x 0 matrices
        return HOMALG_MATRICES.DimensionOfZeroModules;
    elif ZeroColumns( M ) <> [ ] then
        ## take care of matrices with zero columns, especially of 0 x n matrices
        if HasKrullDimension( R ) then
            return KrullDimension( R ); ## this is not a mistake
        elif not ( IsZero( M ) and NumberRows( M ) = 1 and NumberColumns( M ) = 1 ) then ## avoid infinite loops
            free := HomalgZeroMatrix( 1, 1, R );
            return AffineDimension( free, weights, degrees );
        fi;
    elif HasIsIntegersForHomalg( R ) and IsIntegersForHomalg( R ) then
        
        M := BasisOfRowModule( M );
        
        if IsZero( DecideZero( HomalgIdentityMatrix( NumberColumns( M ), R ), M ) ) then
            return HOMALG_MATRICES.DimensionOfZeroModules;
        elif NumberColumns( M ) - NumberRows( M ) = 0 then
            return 0;
        fi;
        
        return 1;
    fi;
    
    RP := homalgTable( R );
    
    if IsBound( RP!.AffineDimension ) then
        
        return AffineDimension( M );
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) then
        
        ## the Hilbert polynomial, as a projective invariant,
        ## cannot distinguish between empty and zero dimensional *affine* sets;
        ## they are both empty as projective sets
        hilb := HilbertPoincareSeries( M, weights, degrees );
        
        d := DimensionOfHilbertPoincareSeries( hilb );
        
        SetAffineDimension( M, d );
        
        return d;
        
    fi;
    
    ## before giving up
    return AffineDimension( M );
    
end );

##
InstallMethod( AffineDimension,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, free, hilb, d;
    
    R := HomalgRing( M );
    
    if NumberColumns( M ) = 0 then
        ## take care of n x 0 matrices
        return HOMALG_MATRICES.DimensionOfZeroModules;
    elif ZeroColumns( M ) <> [ ] then
        ## take care of matrices with zero columns, especially of 0 x n matrices
        if HasKrullDimension( R ) then
            return KrullDimension( R ); ## this is not a mistake
        elif not ( IsZero( M ) and NumberRows( M ) = 1 and NumberColumns( M ) = 1 ) then ## avoid infinite loops
            free := HomalgZeroMatrix( 1, 1, R );
            return AffineDimension( free );
        fi;
    elif ( HasIsFieldForHomalg( R ) and IsFieldForHomalg( R ) ) or
      ( HasIsIntegersForHomalg( R ) and IsIntegersForHomalg( R ) ) then
        
        M := BasisOfRowModule( M );
        
        if IsZero( DecideZero( HomalgIdentityMatrix( NumberColumns( M ), R ), M ) ) then
            return HOMALG_MATRICES.DimensionOfZeroModules;
        elif NumberColumns( M ) - NumberRows( M ) = 0 then
            return 0;
        fi;
        
        return 1;
    fi;
    
    RP := homalgTable( R );
    
    if IsBound( RP!.AffineDimension ) then
        
        if not IsZero( M ) then
            M := BasisOfRowModule( M );
        fi;
        
        d := RP!.AffineDimension( M );
        
        if d < 0 then
            d := HOMALG_MATRICES.DimensionOfZeroModules;
        fi;
        
        return d;
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) then
        
        ## the Hilbert polynomial, as a projective invariant,
        ## cannot distinguish between empty and zero dimensional *affine* sets;
        ## they are both empty as projective sets
        hilb := HilbertPoincareSeries( M );
        
        if IsZero( hilb ) then
            d := HOMALG_MATRICES.DimensionOfZeroModules;
        else
            d := Degree( DenominatorOfRationalFunction( hilb ) );
        fi;
        
        return d;
        
    elif IsBound( RP!.AffineDimensionOfIdeal ) and NumberColumns( M ) = 1 then
        
        d := RP!.AffineDimensionOfIdeal( M );
        
        if d < 0 then
            d := HOMALG_MATRICES.DimensionOfZeroModules;
        fi;
        
        return d;
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called AffineDimension ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( AffineDegree,
        "for a homalg matrix and two lists",
        [ IsHomalgMatrix, IsList, IsList ],
        
  function( M, weights, degrees )
    local R, k, char, RP, hilb;
    
    R := HomalgRing( M );
    
    if HasCoefficientsRing( R ) then
        k := CoefficientsRing( R );
        
        if HasIsIntegersForHomalg( k ) and IsIntegersForHomalg( k ) then
            char := Eliminate( BasisOfRows( M ) );
            if not IsIdenticalObj( k, HomalgRing( char ) ) then
                char := Eliminate( char );
            fi;
            if not IsZero( char ) then
                char := EntriesOfHomalgMatrix( char );
                char := List( char, a -> EvalString( String( a ) ) );
                char := Gcd( char );
                if not IsPrime( char ) then
                    Error( "AffineDegree over a mixed characteristic, here ", char, ", is not supported yet\n" );
                fi;
                k := HomalgRingOfIntegersInUnderlyingCAS( char, k );
                R := k * List( Indeterminates( R ), String );
                M := R * M;
                M := BasisOfRows( M );
            fi;
        fi;
    fi;
    
    RP := homalgTable( R );
    
    if ( IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) or
         IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries ) ) and
       Set( weights ) = [ 1 ] and Length( Set( degrees ) ) = 1 then
        
        ## the coefficients of the untwisted numerator
        ## differ from the twisted ones below by a shift
        hilb := CoefficientsOfNumeratorOfHilbertPoincareSeries( M );
        
        return Sum( hilb );
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) or
      IsBound( RP!.CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries ) then
        
        hilb := CoefficientsOfNumeratorOfHilbertPoincareSeries( M, weights, degrees );
        
        return Sum( hilb[1] );
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( AffineDegree,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, k, char, RP, hilb, weights, degrees;
    
    ## take care of n x 0 matrices
    if NumberColumns( M ) = 0 then
        return 0;
    fi;
    
    R := HomalgRing( M );
    
    if HasCoefficientsRing( R ) then
        k := CoefficientsRing( R );
        
        if HasIsIntegersForHomalg( k ) and IsIntegersForHomalg( k ) then
            char := Eliminate( BasisOfRows( M ) );
            if not IsIdenticalObj( k, HomalgRing( char ) ) then
                char := Eliminate( char );
            fi;
            if not IsZero( char ) then
                char := EntriesOfHomalgMatrix( char );
                char := List( char, a -> EvalString( String( a ) ) );
                char := Gcd( char );
                if not IsPrime( char ) then
                    Error( "AffineDegree over a mixed characteristic, here ", char, ", is not supported yet\n" );
                fi;
                k := HomalgRingOfIntegersInUnderlyingCAS( char, k );
                R := k * List( Indeterminates( R ), String );
                M := R * M;
                M := BasisOfRows( M );
            fi;
        fi;
    fi;
    
    RP := homalgTable( R );
    
    if IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) or
       IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries )  then
        
        hilb := CoefficientsOfNumeratorOfHilbertPoincareSeries( M );
        
        return Sum( hilb );

    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) then
        
        weights := ListWithIdenticalEntries( Length( Indeterminates( R ) ), 1 );
        
        degrees := ListWithIdenticalEntries( NumberColumns( M ), 0 );
        
        return AffineDegree( LeadingModule( M ), weights, degrees );
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called CoefficientsOfNumeratorOfHilbertPoincareSeries ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( ProjectiveDegree,
        "for a homalg matrix and two lists",
        [ IsHomalgMatrix, IsList, IsList ],
        
  function( M, weights, degrees )
    local R, RP, hilb;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if ( IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) or
         IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries ) ) and
       Set( weights ) = [ 1 ] and Length( Set( degrees ) ) = 1 then
        
        hilb := HilbertPolynomial( M );
        
        if IsZero( hilb ) then
            return 0;
        fi;
        
        return LeadingCoefficient( hilb ) * Factorial( Degree( hilb ) );
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) or
      IsBound( RP!.CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries ) then
        
        hilb := HilbertPolynomial( M, weights, degrees );
        
        if IsZero( hilb ) then
            return 0;
        fi;
        
        return LeadingCoefficient( hilb ) * Factorial( Degree( hilb ) );
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( ProjectiveDegree,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, hilb;
    
    ## take care of n x 0 matrices
    if NumberColumns( M ) = 0 then
        return 0;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) or
      IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries )  then
        
        hilb := HilbertPolynomial( M );
        
        if IsZero( hilb ) then
            hilb := 0;
        else
            hilb := LeadingCoefficient( hilb ) * Factorial( Degree( hilb ) );
        fi;
        
        return hilb;
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called CoefficientsOfNumeratorOfHilbertPoincareSeries ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( ConstantTermOfHilbertPolynomial,
        "for a homalg matrix and two lists",
        [ IsHomalgMatrix, IsList, IsList ],
        
  function( M, weights, degrees )
    local d, R, RP, t, hilb, range;
    
    ## take care of n x 0 matrices
    if NumberColumns( M ) = 0 then
        return 0;
    fi;
    
    ## for CASs which do not support Hilbert* for non-graded modules
    d := AffineDimension( M, weights, degrees );
    
    if d <= 0 then
        return 0;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if ( IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) or
         IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries ) ) and
       Set( weights ) = [ 1 ] and Length( Set( degrees ) ) = 1 then
        
        t := Set( degrees )[ 1 ];
        
        ## the coefficients of the untwisted numerator
        ## differ from the twisted ones below by a shift by t
        hilb := CoefficientsOfNumeratorOfHilbertPoincareSeries( M );
        
        return Sum( [ 0 .. Length( hilb ) - 1 ], k -> hilb[k+1] * Binomial( d - 1 -t - k, d - 1 ) );
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ) or
      IsBound( RP!.CoefficientsOfNumeratorOfWeightedHilbertPoincareSeries ) then
        
        hilb := CoefficientsOfNumeratorOfHilbertPoincareSeries( M, weights, degrees );
        
        range := hilb[2];
        hilb := hilb[1];
        
        return Sum( [ 1 .. Length( range ) ], i -> hilb[i] * Binomial( d - 1 - range[i], d - 1 ) );
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called CoefficientsOfUnreducedNumeratorOfWeightedHilbertPoincareSeries ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( ConstantTermOfHilbertPolynomial,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP, d, hilb;
    
    if NumberColumns( M ) = 0 then
        ## take care of n x 0 matrices
        return 0;
    elif IsZero( M ) then
        ## take care of zero matrices, especially of 0 x n matrices
        return NumberColumns( M );
    fi;
    
    d := AffineDimension( M );
    
    if d <= 0 then
        return 0;
    fi;
    
    R := HomalgRing( M );
    
    RP := homalgTable( R );
    
    if IsBound( RP!.ConstantTermOfHilbertPolynomial ) then
        
        return RP!.ConstantTermOfHilbertPolynomial( M );
        
    elif IsBound( RP!.CoefficientsOfUnreducedNumeratorOfHilbertPoincareSeries ) or
      IsBound( RP!.CoefficientsOfNumeratorOfHilbertPoincareSeries ) then
        
        hilb := CoefficientsOfNumeratorOfHilbertPoincareSeries( M );
        
        return Sum( [ 0 .. Length( hilb ) - 1 ], k -> hilb[k+1] * Binomial( d - 1 - k, d - 1 ) );
        
    fi;
    
    if not IsHomalgInternalRingRep( R ) then
        Error( "could not find a procedure called ConstantTermOfHilbertPolynomial ",
               "in the homalgTable of the non-internal ring\n" );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( DataOfHilbertFunction,
        "for a rational function",
        [ IsRationalFunction ],
        
  function( HP )
    local t, H, numer, denom, range, ldeg, hdeg, s, power, q, F, l, i;
    
    t := VariableForHilbertPolynomial( );
    
    if IsZero( HP ) then
        
        H := 0 * t;
        
        ## it is ugly that we need this
        SetIndeterminateOfUnivariateRationalFunction( H, t );
        
        ## checking this property sets it
        Assert( 0, IsUnivariatePolynomial( H ) );
        
        return [ [ [ ], [ ] ], H ];
        
    fi;
    
    numer := NumeratorOfRationalFunction( HP );
    denom := DenominatorOfRationalFunction( HP );
    
    denom!.IndeterminateNumberOfUnivariateRationalFunction := IndeterminateNumberOfUnivariateRationalFunction( numer );
    
    range := CoefficientsOfNumeratorOfHilbertPoincareSeries( HP )[2];
    
    ldeg := range[1];
    
    hdeg := range[Length( range )];
    
    s := IndeterminateOfUnivariateRationalFunction( HP );
    
    power := Minimum( 0, ldeg );
    
    denom := denom * s^power;
    
    ## set the property IsUnivariatePolynomial by testing it
    Assert( 0, IsUnivariatePolynomial( numer ) );
    Assert( 0, IsUnivariatePolynomial( denom ) );
    
    q := s^(hdeg - power);
    numer := EuclideanRemainder( numer, q );
    denom := EuclideanRemainder( denom, q );
    
    F := numer * GcdRepresentation( denom, q )[1];
    
    F := EuclideanRemainder( F, q ) * s^power;
    
    F := CoefficientsOfLaurentPolynomialsWithRange( F );
    
    Assert( 0, IsSubset( range, F[2] ) );
    
    range := F[2];
    
    l := Length( range );
    
    H := HilbertPolynomialOfHilbertPoincareSeries( HP );
    
    while l > 0 do
        
        if Value( H, range[l] ) <> F[1][l] then
            break;
        fi;
        
        l := l - 1;
        
        range := F[2]{[ 1 .. l ]};
        
        F := [ F[1]{[ 1 .. l ]}, range ];
        
    od;
    
    if l = 0 then
        return [ [ [ 0 ], [ ldeg - 1 ] ], H ];
    fi;
    
    return [ F, H ];
    
end );

##
InstallMethod( HilbertFunction,
        "for a rational function",
        [ IsRationalFunction ],
        
  function( HP )
    local data, H, l, ldeg, indeg;
    
    data := DataOfHilbertFunction( HP );
    
    H := data[2];
    
    data := data[1];
    
    l := Length( data[2] );
    
    if l = 0 then
        
        Assert( 0, IsZero( H ) );
        
        return t -> 0;
        
    fi;
    
    ldeg := data[2][1];
    indeg := data[2][l];
    
    data := data[1];
    
    return
      function( t )
        
        if t < ldeg then
            return 0;
        elif t <= indeg then
            if not IsInt( t ) then
                Error( "only able to evaluate integers in the interval [ ldeg, indeg ], but received ", t, "\n" );
            fi;
            return data[t - ldeg + 1];
        fi;
        
        return Value( H, t );
        
    end;
    
end );

##
InstallMethod( IndexOfRegularity,
        "for a rational function",
        [ IsRationalFunction ],
        
  function( HP )
    local range;
    
    if IsZero( HP ) then
        Error( "GAP does not support -infinity yet\n" );
    fi;
    
    range := DataOfHilbertFunction( HP )[1][2];
    
    return range[Length( range )] + 1;
    
end );

##
InstallMethod( IsPrimeModule,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( M )
    local R, RP;
    
    R := HomalgRing( M );
    
    if IsZero( M ) and HasIsIntegralDomain( R ) and IsIntegralDomain( R ) then
        return true;
    fi;
    
    RP := homalgTable( R );
    
    if IsBound( RP!.IsPrime ) then
        return RP!.IsPrime( M );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( IntersectWithSubalgebra,
        "for a homalg matrix and a list",
        [ IsHomalgMatrix, IsList ],
        
  function( I, var )
    local R, indets, J, S;
    
    R := HomalgRing( I );
    
    if not ( HasIsFreePolynomialRing( R ) and IsFreePolynomialRing( R ) ) then
        TryNextMethod( );
    fi;
    
    indets := Indeterminates( R );
    
    if not IsSubset( indets, var ) then
        Error( "expecting the second argument ", var,
               " to be a subset of the set of indeterminates ", indets, "\n" );
    fi;
    
    J := Eliminate( I, Difference( indets, var ) );
    
    S := CoefficientsRing( R ) * var;
    
    return S * J;
    
end );

##
InstallMethod( MaximalIndependentSet,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( I )
    local R, indets, d, RP, i, combinations, u;
    
    R := HomalgRing( I );
    
    indets := Indeterminates( R );
    
    if IsZero( I ) then
        return indets;
    fi;
    
    I := BasisOfRowModule( I );
    
    d := AffineDimension( I );
    
    if d = 0 then
        return [ ];
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.MaximalIndependentSet) then
        indets := RP!.MaximalIndependentSet( I );
        Assert( 0, Length( indets ) = d );
        return indets;
    fi;
    
    ## the fallback method
    
    combinations := IteratorOfCombinations( indets, d );
    
    for u in combinations do
        if IsZero( IntersectWithSubalgebra( I, u ) ) then
            return u;
        fi;
    od;
    
    Error( "oh, no maximal independent set found, this is a bug!\n" );
    
end );

## for ideals with affine entries
InstallMethod( MaximalIndependentSet,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( I )
    local R, indets, d, left;
    
    R := HomalgRing( I );
    
    indets := Indeterminates( R );
    
    if IsZero( I ) then
        return indets;
    fi;
    
    d := AffineDimension( I );
    
    if d = 0 then
        return [ ];
    fi;
    
    I := BasisOfRowModule( I );
    
    if not ForAll( EntriesOfHomalgMatrix( I ), a -> Degree( a ) = 1 ) then
        TryNextMethod( );
    fi;
    
    I := LeadingModule( I );
    
    return Difference( indets, EntriesOfHomalgMatrix( I ) );
    
end );

##
InstallMethod( AMaximalIdealContaining,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( I )
    local R, A, one, indets, m, v, l, n_is_one, n, a, k, d;
    
    R := HomalgRing( I );
    
    if not HasCoefficientsRing( R ) then
        TryNextMethod( );
    fi;
    
    A := CoefficientsRing( R );
    
    if not ( HasIsFieldForHomalg( A ) and IsFieldForHomalg( A ) ) then
        TryNextMethod( );
    fi;
    
    one := HomalgIdentityMatrix( 1, R );
    
    I := BasisOfRowModule( I );
    
    if IsZero( DecideZeroRows( one, I ) ) then
        Error( "expected a matrix not reducing one to zero\n" );
    fi;
    
    indets := Indeterminates( R );
    
    if IsZero( I ) then
        return HomalgMatrix( indets, Length( indets ), 1, R );
    fi;
    
    m := I;
    
    while AffineDimension( m ) > 0 do
        
        v := MaximalIndependentSet( m );
        
        n_is_one := true;
        
        while true do
            
            ## the fiber over the origin of the subspace L corresponding
            ## to the maximal independent set v
            n := UnionOfRows( m, HomalgMatrix( v, Length( v ), 1, R ) );
            
            n := BasisOfRowModule( n );
            
            ## if the fiber is not empty then break the while loop
            if not IsZero( DecideZeroRows( one, n ) ) then
                n_is_one := false;
                break;
            fi;
            
            l := Length( v );
            
            ## try fibers over bigger coordinate subspaces of L containing the origin
            if l > 1 then
                Remove( v, l );
            else
                break;
            fi;
            
        od;
        
        if n_is_one then
            
            v := v[1];
            
            a := One( R );
            k := 1;
            
            while true do
                
                n := UnionOfRows( m, HomalgMatrix( [ v^k + a ], 1, 1, R ) );
                
                n := BasisOfRowModule( n );
                
                if not IsZero( DecideZeroRows( one, n ) ) then
                    break;
                fi;
                
                a := a + 1;
                
                if IsZero( a ) then
                    k := k + 1;
                fi;
                
            od;
            
        fi;
        
        m := n;
        
    od;
    
    m := RadicalDecompositionOp( m );
    
    d := List( m, AffineDegree );
    
    d := Minimum( d );
    
    m := First( m, p -> AffineDegree( p ) = d );
    
    Assert( 4, AffineDimension( m ) = 0 );
    
    SetAffineDimension( m, 0 );
    
    return m;
    
end );

##
InstallMethod( AMaximalIdealContaining,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( I )
    local R, zz, one, indets, S, gens, gens0, lcm, p, Fp;
    
    R := HomalgRing( I );
    
    if not HasCoefficientsRing( R ) then
        TryNextMethod( );
    fi;
    
    zz := CoefficientsRing( R );
    
    if not ( HasIsIntegersForHomalg( zz ) and IsIntegersForHomalg( zz ) ) then
        TryNextMethod( );
    fi;
    
    indets := Indeterminates( R );
    
    S := zz * indets;
    
    I := S * I;
    
    one := HomalgIdentityMatrix( 1, S );
    
    I := BasisOfRowModule( I );
    
    if IsZero( DecideZeroRows( one, I ) ) then
        Error( "expected a matrix not reducing one to zero\n" );
    fi;
    
    if IsZero( I ) then
        return UnionOfRows( HomalgMatrix( "[2]", 1, 1, R ), HomalgMatrix( indets, Length( indets ), 1, R ) );
    fi;
    
    gens := EntriesOfHomalgMatrix( S * I );
    
    gens0 := Filtered( gens, g -> Degree( g ) = 0 );
    
    if not gens0 = [ ] then
        
        gens0 := List( List( gens0, String ), EvalString );
        gens0 := Gcd( gens0 );
        
        p := PrimeDivisors( gens0 )[1];
        
    else
        
        lcm := Iterated( List( gens, LeadingCoefficient ), LcmOp );
        lcm := Int( String( lcm ) );
        
        p := 2;
        
        while IsInt( lcm / p ) do
            p := NextPrimeInt( p );
        od;
        
    fi;
    
    Assert( 4, not ( p / S ) in I );
    
    Fp := HomalgRingOfIntegersInUnderlyingCAS( p, zz );
    S := Fp * indets;
    I := S * I;
    
    p := HomalgMatrix( [ p ], 1, 1, R );
    
    return UnionOfRows( p, R * AMaximalIdealContaining( I ) );
    
end );

##
InstallMethod( AMaximalIdealContaining,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( I )
    local R;
    
    R := HomalgRing( I );
    
    if not ( HasIsFieldForHomalg( R ) and IsFieldForHomalg( R ) ) then
        TryNextMethod( );
    fi;
    
    I := BasisOfRowModule( I );
    
    if not IsZero( I ) then
        Error( "expected a matrix not reducing one to zero\n" );
    fi;
    
    return I;
    
end );

##
InstallMethod( AMaximalIdealContaining,
        "for a homalg matrix",
        [ IsHomalgMatrix ],
        
  function( I )
    local R, one;
    
    R := HomalgRing( I );
    
    if not ( HasIsIntegersForHomalg( R ) and IsIntegersForHomalg( R ) ) then
        TryNextMethod( );
    fi;
    
    one := HomalgIdentityMatrix( 1, R );
    
    I := BasisOfRowModule( I );
    
    if IsZero( DecideZeroRows( one, I ) ) then
        Error( "expected a matrix not reducing one to zero\n" );
    fi;
    
    if IsZero( I ) then
        return HomalgMatrix( [ 2 ], 1, 1, R );
    fi;
    
    if not NumberRows( I ) = 1  then
        Error( "Hermite normal form failed to produce the cyclic generator ",
               "of the principal ideal\n" );
    fi;
    
    I := I[ 1, 1 ];
    I := Int( String( I ) );
    
    return HomalgMatrix( [ PrimeDivisors( I ){[1]} ], 1, 1, R );
    
end );

##
InstallMethod( IsolateIndeterminate,
        "for a homalg ring element",
        [ IsHomalgRingElement ],
        
  function( r )
    local R, indets, l, coeffs, monoms, t, degrees, pos, i, c, a;
    
    R := HomalgRing( r );
    
    indets := Indeterminates( R );
    
    l := Length( indets );
    
    if l = [ ] then
        return fail;
    fi;
    
    coeffs := Coefficients( r );
    monoms := coeffs!.monomials;
    
    t := Length( monoms );
    
    degrees := List( monoms, Degree );
    
    if not 1 in degrees then
        return fail;
    fi;
    
    pos := Positions( degrees, 1 );
    
    for i in pos do
        
        c := coeffs[ i, 1 ];
        
        if not IsUnit( c ) then
            continue;
        fi;
        
        a := monoms[i];
        
        if not ForAll( Concatenation( [ 1 .. i - 1 ], [ i + 1 .. t ] ), j -> not IsZero( DecideZero( monoms[j], a ) ) ) then
            continue;
        fi;
        
        return [ Position( indets, a ), a - ( r / c ) ];
        
    od;
    
    return fail;
    
end );

##
InstallOtherMethod( Saturate,
        "for two homalg matrices",
        [ IsHomalgMatrix, IsHomalgMatrix ],
        
  function( mat, r )
    local mat_old;
    
    if not NumberColumns( mat ) = NumberColumns( r ) then
        Error( "the matrices mat and r must have the same number of columns\n" );
    elif not NumberRows( r ) = NumberColumns( r ) then
        Error( "the matrix r is not a square matrix\n" );
    fi;
    
    repeat
        mat_old := mat;
        mat := SyzygiesOfRows( r, mat );
    until IsZero( DecideZeroRows( mat, mat_old ) );
    
    return mat;
    
end );

##
InstallMethod( Saturate,
        "for a homalg matrix and a homalg ring element",
        [ IsHomalgMatrix, IsRingElement ],
        
  function( mat, r )
    
    r := HomalgMatrix( [ r ], 1, 1, HomalgRing( mat ) );
    
    return Saturate( mat, r );
    
end );

##
InstallMethod( RingMapOntoRewrittenResidueClassRing,
        "for a homalg ring",
        [ IsHomalgRing ],
        
  function( R )
    local id, I, A, k, char, indets, matrix, images, zero_rows, S, pi, ker;
    
    id := RingMap( R );
    
    if not HasAmbientRing( R ) then
        return id;
    fi;
    
    ## R = A / I
    I := BasisOfRows( MatrixOfRelations( R ) );
    
    A := AmbientRing( R );
    
    k := CoefficientsRing( A );
    
    if HasIsIntegersForHomalg( k ) and IsIntegersForHomalg( k ) then
        char := Eliminate( I );
        if not IsIdenticalObj( k, HomalgRing( char ) ) then
            char := Eliminate( char );
        fi;
        if not IsZero( char ) then
            char := EntriesOfHomalgMatrix( char );
            char := List( char, a -> EvalString( String( a ) ) );
            char := Gcd( char );
            if IsPrime( char ) then
                k := HomalgRingOfIntegersInUnderlyingCAS( char, k );
            fi;
        fi;
    fi;
    
    indets := Indeterminates( A );
    
    matrix := HomalgMatrix( indets, Length( indets ), 1, A );
    
    ## search for the standard indeterminates with respect to I: NF_I(x_i) = x_i
    images := DecideZero( matrix, I );
    
    ## the positions of the standard indeterminates with respect to I
    zero_rows := ZeroRows( matrix - images );
    
    ## create the standard subring S of A, i.e., the subring generated by the standard indeterminates
    if IsZero( DecideZeroRows( HomalgIdentityMatrix( 1, A ), I ) ) then
        S := k / One( k );
    elif Length( indets ) = Length( zero_rows ) and
       IsIdenticalObj( k, CoefficientsRing( A ) ) then
        S := A;
    else
        indets := indets{zero_rows};
        S := k * List( indets, String );
    fi;
    
    if not zero_rows = [ ] then
        ## define the surjective ring morphism pi: S ->> R = A / I
        pi := RingMap( indets, S, R );
        ## compute the kernel ker( pi )
        ker := GeneratorsOfKernelOfRingMap( pi );
        ## define the residue class ring S / ker( pi ), isomorphic to R = A / I
        S := S / ker;
    fi;
    
    images := S * images;
    
    ## define the surjective ring morphism A ->> S / ker
    pi := RingMap( images, A, S );
    
    SetIsMorphism( pi, true );
    SetIsEpimorphism( pi, true );
    
    return pi;
    
end );

##
InstallMethod( RingMapOntoSimplifiedOnceResidueClassRing,
        "for a homalg ring",
        [ IsHomalgRing ],
        
  function( R )
    local id, I, i, img, A, indets, new_indets, S, map, epi;
    
    id := RingMap( R );
    
    if not HasAmbientRing( R ) then
        return id;
    fi;
    
    ## R = A / I
    A := AmbientRing( R );
    
    I := MatrixOfRelations( R );
    
    ## [ y_1, ..., y_{i-1}, y_i, y_{i+1}, ..., y_s ]
    indets := ShallowCopy( Indeterminates( A ) );
    
    if IsEmpty( indets ) and IsZero( DecideZeroRows( HomalgIdentityMatrix( 1, A ), I ) ) then
        S := CoefficientsRing( A ) / 1;
        SetIsZero( S, true );
        epi := RingMap( ListWithIdenticalEntries( Length( indets ), Zero( S ) ), A, S );
        SetIsMorphism( epi, true );
        SetIsEpimorphism( epi, true );
        return epi;
    fi;
    
    for i in [ 1 .. NumberRows( I ) ] do
        ## [ j, f/u ] where (u y_j - f) ∈ GB(I)
        img := IsolateIndeterminate( I[ i, 1 ] );
        if not img = fail then
            break;
        fi;
    od;
    
    if img = fail then
        epi := RingMap( List( indets, a -> a / R ), A, R );
        SetIsMorphism( epi, true );
        SetIsEpimorphism( epi, true );
        return epi;
    fi;
    
    new_indets := List( indets, String );
    
    ## [ y_1, ..., y_{i-1}, y_{i+1}, ..., y_s ]
    Remove( new_indets, img[1] );
    
    ## k[y_1, ..., y_{i-1}, y_{i+1}, ..., y_s]
    S := CoefficientsRing( A ) * new_indets;
    
    ## [ y_1, ..., y_{i-1}, f/u y_{i+1}, ..., y_s ]
    indets[img[1]] := img[2];
    
    indets := List( indets, a -> a / S );
    
    map := RingMap( indets, A, S );
    
    ## replace y_i -> f/u
    I := Pullback( map, I );
    
    I := BasisOfRows( I );
    
    S := S / I;
    
    indets := List( indets, a -> a / S );
    
    epi := RingMap( indets, A, S );
    
    SetIsMorphism( epi, true );
    SetIsEpimorphism( epi, true );
    
    return epi;
    
end );

##
InstallMethod( RingMapOntoSimplifiedResidueClassRing,
        "for a homalg ring",
        [ IsHomalgRing ],
        
  function( R )
    local pi, psi;
    
    # R = A / I
    pi := RingMapOntoRewrittenResidueClassRing( R ); # replace pi: A -> R = A / I by pi: A -> R_1 := A_1 / I_1
    
    while true do
        
        ## construct the surjective morphism psi: A_i -> A_{i+1} / I_{i+1} =: R_{i+1}
        psi := RingMapOntoSimplifiedOnceResidueClassRing( Range( pi ) );
        
        if ( HasIsZero( Range( psi ) ) and IsZero( Range( psi ) ) ) or
           Length( Indeterminates( Source( psi ) ) ) = Length( Indeterminates( Range( psi ) ) ) then
            break;
        fi;
        
        ## compose A -pi-> A_i / I_i -psi-> A_{i+1} / I_{i+1},
        ## where we understand the above psi as the isomorphism psi: A_i / I_i -psi-> A_{i+1} / I_{i+1}
        pi := PreCompose( pi, psi );
        
    od;
    
    return pi;
    
end );

##
InstallMethod( RingMapOntoSimplifiedOnceResidueClassRingUsingLinearEquations,
        "for a homalg ring",
        [ IsHomalgRing ],
        
  function( R )
    local id, I, L, A, S, pi, P, J, T, psi, epi;
    
    id := RingMap( R );
    
    if not HasAmbientRing( R ) then
        return id;
    elif HasIsZero( R ) and IsZero( R ) then
        return id;
    fi;
    
    ## R = A / I
    I := MatrixOfRelations( R );
    
    L := Filtered( EntriesOfHomalgMatrix( I ), e -> Degree( e ) <= 1 );
    
    A := AmbientRing( R );
    
    L := HomalgMatrix( L, Length( L ), 1, A );
    
    L := BasisOfRows( L );
    
    if IsZero( L ) then
        return id;
    fi;
    
    S := A / L;
    
    pi := RingMapOntoSimplifiedResidueClassRing( S );
    
    P := Range( pi );
    
    Assert( 0, ( HasIsZero( P ) and IsZero( P ) ) or not HasAmbientRing( P ) );
    
    J := Pullback( pi, I );
    
    J := CertainRows( J, NonZeroRows( J ) );
    
    T := P / J;
    
    psi := RingMap( List( Indeterminates( P ), a -> a / T ), P, T );
    
    epi := PreCompose( pi, psi );
    
    SetIsMorphism( epi, true );
    SetIsEpimorphism( epi, true );
    
    return epi;
    
end );

##
InstallMethod( RingMapOntoSimplifiedResidueClassRingUsingLinearEquations,
        "for a homalg ring",
        [ IsHomalgRing ],
        
  function( R )
    local id, pi, psi;
    
    id := RingMap( R );
    
    if not HasAmbientRing( R ) then
        return id;
    fi;
    
    # R = A / I
    pi := RingMap( Indeterminates( R ), AmbientRing( R ), R );
    
    SetIsMorphism( pi, true );
    SetIsEpimorphism( pi, true );
    
    while true do
        
        ## construct the surjective morphism psi: A_i -> A_{i+1} / I_{i+1} =: R_{i+1}
        psi := RingMapOntoSimplifiedOnceResidueClassRingUsingLinearEquations( Range( pi ) );
        
        if ( HasIsZero( Range( psi ) ) and IsZero( Range( psi ) ) ) or
           Length( Indeterminates( Source( psi ) ) ) = Length( Indeterminates( Range( psi ) ) ) then
            break;
        fi;
        
        ## compose A -pi-> A_i / I_i -psi-> A_{i+1} / I_{i+1},
        ## where we understand the above psi as the isomorphism psi: A_i / I_i -psi-> A_{i+1} / I_{i+1}
        pi := PreCompose( pi, psi );
        
    od;
    
    return pi;
    
end );

[Dauer der Verarbeitung: 0.204 Sekunden, vorverarbeitet 2026-04-29]