CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

| Download

GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it

Views: 418346
#############################################################################
##
##  BasicFunctors.gi            Modules package              Mohamed Barakat
##
##  Copyright 2007-2010 Mohamed Barakat, RWTH Aachen
##
##  Implementation stuff for some tool functors.
##
#############################################################################

####################################
#
# install global functions/variables:
#
####################################

##
## Cokernel
##

##
InstallGlobalFunction( _Functor_Cokernel_OnModules,	### defines: Cokernel(Epi)
  function( phi )
    local R, T, p, rel, gen, coker, id, epi, gen_iso, img_emb, emb;
    
    if HasCokernelEpi( phi ) then
        return Range( CokernelEpi( phi ) );
    fi;
    
    R := HomalgRing( phi );
    
    T := Range( phi );
    
    ## this is probably obsolete but clarifies our idea:
    p := PositionOfTheDefaultSetOfGenerators( T );  ## avoid future possible side effects of the following command(s)
    
    rel := UnionOfRelations( phi );
    
    gen := GeneratorsOfModule( T );
    
    gen := UnionOfRelations( gen, rel * gen );
    
    coker := Presentation( gen, rel );
    
    ## the identity matrix is the matrix of the natural epimorphism
    ## w.r.t. the p-th set of relations of T and the first set of relations of coker:
    id := HomalgIdentityMatrix( NrGenerators( gen ), R );
    
    ## the natural epimorphism:
    epi := HomalgMap( id, [ T, p ], [ coker, 1 ] );
    
    ## for graded modules we need to know on which presentation the cokernel was computed
    epi!.DefaultPresentationOfCokernelEpi := [ p, 1 ];
    
    ## even if IsMorphism( phi ) = false, in this data structure IsMorphism( epi ) = true
    SetIsMorphism( epi, true );
    
    ## we cannot check this assertion, since
    ## checking it would cause an infinite loop
    SetIsEpimorphism( epi, true );
    
    ## set the attribute CokernelEpi (specific for Cokernel):
    SetCokernelEpi( phi, epi );
    
    ## the generalized inverse of the natural epimorphism
    ## (cf. [Bar, Cor. 4.8])
    gen_iso := HomalgMap( id, [ coker, 1 ], [ T, p ] );
    
    ## set the morphism aid map
    SetMorphismAid( gen_iso, phi );
    
    ## set the generalized inverse of the natural epimorphism
    SetInverseOfGeneralizedMorphismWithFullDomain( epi, gen_iso );
    
    ## we cannot check this assertion, since
    ## checking it would cause an infinite loop
    SetIsGeneralizedIsomorphism( gen_iso, true );
    
    #=====# end of the core procedure #=====#
    
    ## abelian category: [HS, Prop. II.9.6]
    if HasImageObjectEmb( phi ) then
        img_emb := ImageObjectEmb( phi );
        SetKernelEmb( epi, img_emb );
        if not HasCokernelEpi( img_emb ) then
            SetCokernelEpi( img_emb, epi );
        fi;
    elif HasIsMonomorphism( phi ) and IsMonomorphism( phi ) then
        SetKernelEmb( epi, phi );
    fi;
    
    ## this is in general NOT a morphism,
    ## BUT it is one modulo the image of phi in T, and then even a monomorphism:
    ## this is enough for us since we will always view it this way (cf. [BR08, 3.1.1,(2), 3.1.2] )
    emb := HomalgMap( id, [ coker, 1 ], [ T, p ] );
    SetMorphismAid( emb, phi );
    
    ## we cannot check this assertion, since
    ## checking it would cause an infinite loop
    SetIsGeneralizedIsomorphism( emb, true );
    
    ## save the natural embedding in the cokernel (thanks GAP):
    coker!.NaturalGeneralizedEmbedding := emb;
    
    return coker;
    
end );

##  <#GAPDoc Label="functor_Cokernel:code">
##      <Listing Type="Code"><![CDATA[
InstallValue( functor_Cokernel_for_fp_modules,
        CreateHomalgFunctor(
                [ "name", "Cokernel" ],
                [ "category", HOMALG_MODULES.category ],
                [ "operation", "Cokernel" ],
                [ "natural_transformation", "CokernelEpi" ],
                [ "special", true ],
                [ "number_of_arguments", 1 ],
                [ "1", [ [ "covariant" ],
                        [ IsMapOfFinitelyGeneratedModulesRep,
                          [ IsHomalgChainMorphism, IsImageSquare ] ] ] ],
                [ "OnObjects", _Functor_Cokernel_OnModules ]
                )
        );
##  ]]></Listing>
##  <#/GAPDoc>

functor_Cokernel_for_fp_modules!.ContainerForWeakPointersOnComputedBasicMorphisms := true;

##
## ImageObject
##

InstallGlobalFunction( _Functor_ImageObject_OnModules,	### defines: ImageObject(Emb)
  function( phi )
    local T, p, img, emb, coker_epi, img_submodule;
    
    if HasImageObjectEmb( phi ) then
        return Source( ImageObjectEmb( phi ) );
    fi;
    
    T := Range( phi );
    
    ## this is probably obsolete but clarifies our idea:
    p := PositionOfTheDefaultSetOfGenerators( T );  ## avoid future possible side effects of the following command(s)
    
    ## the image module
    img := MatrixOfMap( phi ) / T;
    
    ## emb is the matrix of the natural embedding
    ## w.r.t. the first set of relations of img and the p-th set of relations of T
    emb := MatrixOfGenerators( img, 1 );
    
    emb := HomalgMap( emb, [ img, 1 ], [ T, p ] );
    
    ## check assertion
    Assert( 5, IsMonomorphism( emb ) );
    SetIsMonomorphism( emb, true );
    
    INSTALL_TODO_LIST_ENTRIES_FOR_MORPHISMS_AND_IMAGE_EMBEDDINGS( phi, emb );
    
    ## set the attribute ImageObjectEmb (specific for ImageObject):
    ## (since ImageObjectEmb is listed below as a natural transformation
    ##  for the functor ImageObject, a method will be automatically installed
    ##  by InstallFunctor to fetch it by first invoking the main operation ImageObject)
    SetImageObjectEmb( phi, emb );
    
    #=====# end of the core procedure #=====#
    
    ## abelian category: [HS, Prop. II.9.6]
    if HasCokernelEpi( phi ) then
        coker_epi := CokernelEpi( phi );
        SetCokernelEpi( emb, coker_epi );
        if not HasKernelEmb( coker_epi ) then
            SetKernelEmb( coker_epi, emb );
        fi;
    fi;
    
    ## at last define the image submodule
    img_submodule := ImageSubobject( phi );
    
    SetUnderlyingSubobject( img, img_submodule );
    SetEmbeddingInSuperObject( img_submodule, emb );
    
    MatchPropertiesAndAttributesOfSubobjectAndUnderlyingObject( img_submodule, img );
    
    ## save the natural embedding in the image (thanks GAP):
    img!.NaturalGeneralizedEmbedding := emb;
    
    return img;
    
end );

##  <#GAPDoc Label="functor_ImageObject:code">
##      <Listing Type="Code"><![CDATA[
InstallValue( functor_ImageObject_for_fp_modules,
        CreateHomalgFunctor(
                [ "name", "ImageObject for modules" ],
                [ "category", HOMALG_MODULES.category ],
                [ "operation", "ImageObject" ],
                [ "natural_transformation", "ImageObjectEmb" ],
                [ "number_of_arguments", 1 ],
                [ "1", [ [ "covariant" ],
                        [ IsMapOfFinitelyGeneratedModulesRep and
                          AdmissibleInputForHomalgFunctors ] ] ],
                [ "OnObjects", _Functor_ImageObject_OnModules ]
                )
        );
##  ]]></Listing>
##  <#/GAPDoc>

functor_ImageObject_for_fp_modules!.ContainerForWeakPointersOnComputedBasicMorphisms := true;

##
## Hom
##

InstallGlobalFunction( _Functor_Hom_OnModules,		### defines: Hom (object part)
  function( M, N )
    local s, t, dM, dN, P1, l0, l1, _l0, matM, matN, R, HP0N, HP1N, r, c, idN,
          alpha, hom, gen, proc_to_readjust_generators, proc_to_normalize_generators, p;
    
    CheckIfTheyLieInTheSameCategory( M, N );
    
    s := PositionOfTheDefaultSetOfGenerators( M );
    t := PositionOfTheDefaultSetOfGenerators( N );
    
    dM := PresentationMorphism( M );
    dN := PresentationMorphism( N );
    
    P1 := Source( dM );
    
    l0 := NrGenerators( M );
    l1 := NrGenerators( P1 );
    
    _l0 := NrGenerators( N );
    
    matM := MatrixOfMap( dM );
    matN := MatrixOfMap( dN );
    
    R := HomalgRing( M );
    
    if l0 = 0 then
        HP0N := HomalgZeroMatrix( 0, 0, R );
    else
        HP0N := DiagMat( ListWithIdenticalEntries( l0, Involution( matN ) ) );
    fi;
    
    if l1 = 0 then
        HP1N := HomalgZeroMatrix( 0, 0, R );
    else
        HP1N := DiagMat( ListWithIdenticalEntries( l1, Involution( matN ) ) );
    fi;
    
    if IsHomalgLeftObjectOrMorphismOfLeftObjects( M ) then
        r := l0;
        c := _l0;
        
        proc_to_normalize_generators :=
          function( mat, M_with_s, N_with_t )
            local M, s, N, t, mor, mat_old;
            
            ## for better readability of the code:
            M := M_with_s[1];
            s := M_with_s[2];
            
            N := N_with_t[1];
            t := N_with_t[2];
            
            ## we assume mat to be a matrix of a morphism
            ## w.r.t. the CURRENT generators of source and target:
            mor := HomalgMap( mat, M, N );
            
            mat_old := MatrixOfMap( mor, s, t );
            
            return ConvertMatrixToColumn( mat_old );
        end;
        
        proc_to_readjust_generators :=
          function( gen, M_with_s, N_with_t )
            local c, r, mat_old, mor;
            
            ## M_with_s = [ M, s ]
            ## N_with_t = [ N, t ]
            
            r := CallFuncList( NrGenerators, M_with_s );
            c := CallFuncList( NrGenerators, N_with_t );
            
            mat_old := ConvertColumnToMatrix( gen, r, c );
            
            ## the matrix of the morphism will be displayed
            ## w.r.t. the CURRENT generators of source and target
            mor := HomalgMap( mat_old, M_with_s, N_with_t );
            
            ## check assertion
            Assert( 3, IsMorphism( mor ) );
            
            SetIsMorphism( mor, true );
            
            return mor;
        end;
        
        HP0N := RightPresentation( HP0N );
        HP1N := RightPresentation( HP1N );
        
    else
        r := _l0;
        c := l0;
        
        proc_to_normalize_generators :=
          function( mat, M_with_s, N_with_t )
            local M, s, N, t, mor, mat_old;
            
            ## for better readability of the code:
            M := M_with_s[1];
            s := M_with_s[2];
            
            N := N_with_t[1];
            t := N_with_t[2];
            
            ## we assume mat to be a matrix of a morphism
            ## w.r.t. the CURRENT generators of source and target:
            mor := HomalgMap( mat, M, N );
            
            mat_old := MatrixOfMap( mor, s, t );
            
            return ConvertMatrixToRow( mat_old );
        end;
        
        proc_to_readjust_generators :=
          function( gen, M_with_s, N_with_t )
            local c, r, mat_old, mor;
            
            ## M_with_s = [ M, s ]
            ## N_with_t = [ N, t ]
            
            c := CallFuncList( NrGenerators, M_with_s );
            r := CallFuncList( NrGenerators, N_with_t );
            
            mat_old := ConvertRowToMatrix( gen, r, c );
            
            ## the matrix of the morphism will be displayed
            ## w.r.t. the CURRENT generators of source and target
            mor := HomalgMap( mat_old, M_with_s, N_with_t );
            
            ## check assertion
            Assert( 3, IsMorphism( mor ) );
            
            SetIsMorphism( mor, true );
            
            return mor;
        end;
        
        HP0N := LeftPresentation( HP0N );
        HP1N := LeftPresentation( HP1N );
        
    fi;
    
    idN := HomalgIdentityMatrix( _l0, R );
    
    alpha := KroneckerMat( matM, idN );
    
    alpha := HomalgMap( alpha, HP0N, HP1N );
    
    SetIsMorphism( alpha, true );
    
    hom := Kernel( alpha );
    
    #=====# end of the core procedure #=====#
    
    SetProcedureToNormalizeGenerators( hom, [ proc_to_normalize_generators, [ M, s ], [ N, t ] ] );
    SetProcedureToReadjustGenerators( hom, [ proc_to_readjust_generators, [ M, s, ], [ N, t ] ] );
    
    return hom;
    
end );

##
InstallGlobalFunction( _Functor_Hom_OnMaps,	### defines: Hom (morphism part)
  function( F_source, F_target, arg_before_pos, phi, arg_behind_pos )
    local R, L, idL, hull_phi, covariant, emb_source, emb_target, mor;
    
    R := HomalgRing( phi );
    
    if arg_before_pos = [ ] and Length( arg_behind_pos ) = 1 then
        ## Hom( phi, L )
        
        L := arg_behind_pos[1];
        
        idL := HomalgIdentityMatrix( NrGenerators( L ), R );
        
        hull_phi := KroneckerMat( MatrixOfMap( phi ), idL );
        
        covariant := false;
        
    elif Length( arg_before_pos ) = 1 and arg_behind_pos = [ ] then
        ## Hom( L, phi )
        
        L := arg_before_pos[1];
        
        idL := HomalgIdentityMatrix( NrGenerators( L ), R );
        
        hull_phi := Involution( KroneckerMat( idL, MatrixOfMap( phi ) ) );
        
        covariant := true;
        
    else
        Error( "wrong input\n" );
    fi;
    
    emb_source := NaturalGeneralizedEmbedding( F_source );
    emb_target := NaturalGeneralizedEmbedding( F_target );
    
    hull_phi := HomalgMap( hull_phi, Range( emb_source ), Range( emb_target ) );
    
    SetIsMorphism( hull_phi, true );
    
    mor := CompleteImageSquare( emb_source, hull_phi, emb_target );
    
    ## HasIsIsomorphism( phi ) and IsIsomorphism( phi ), resp.
    ## HasIsMorphism( phi ) and IsMorphism( phi ), and
    ## UpdateObjectsByMorphism( mor )
    ## will be taken care of in FunctorMap
    
    if covariant then
        ## Hom( L, - )
        
        if HasIsMonomorphism( phi ) and IsMonomorphism( phi ) then
            
            ## check assertion
            Assert( 3, IsMonomorphism( mor ) );
            
            SetIsMonomorphism( mor, true );
            
        fi;
        
        if HasIsEpimorphism( phi ) and IsEpimorphism( phi ) and
          HasIsProjective( L ) and IsProjective( L ) then
            
            ## check assertion
            Assert( 3, IsEpimorphism( mor ) );
            
            SetIsEpimorphism( mor, true );
            
        fi;
        
    else
        ## Hom( -, L )
        
        if HasIsEpimorphism( phi ) and IsEpimorphism( phi ) then
            
            ## check assertion
            Assert( 3, IsMonomorphism( mor ) );
            
            SetIsMonomorphism( mor, true );

        fi;
        
        if HasIsMonomorphism( phi ) and IsMonomorphism( phi ) and
          HasIsInjective( L ) and IsInjective( L ) then
            
            ## check assertion
            Assert( 3, IsEpimorphism( mor ) );
            
            SetIsEpimorphism( mor, true );
            
        fi;
        
    fi;
    
    return mor;
    
end );

##  <#GAPDoc Label="Functor_Hom:code">
##      <Listing Type="Code"><![CDATA[
InstallValue( Functor_Hom_for_fp_modules,
        CreateHomalgFunctor(
                [ "name", "Hom" ],
                [ "category", HOMALG_MODULES.category ],
                [ "operation", "Hom" ],
                [ "number_of_arguments", 2 ],
                [ "1", [ [ "contravariant", "right adjoint", "distinguished" ] ] ],
                [ "2", [ [ "covariant", "left exact" ] ] ],
                [ "OnObjects", _Functor_Hom_OnModules ],
                [ "OnMorphisms", _Functor_Hom_OnMaps ],
                [ "MorphismConstructor", HOMALG_MODULES.category.MorphismConstructor ]
                )
        );
##  ]]></Listing>
##  <#/GAPDoc>

Functor_Hom_for_fp_modules!.ContainerForWeakPointersOnComputedBasicObjects := true;

Functor_Hom_for_fp_modules!.ContainerForWeakPointersOnComputedBasicMorphisms := true;

##
InstallMethod( NatTrIdToHomHom_R,
        "for homalg modules",
        [ IsFinitelyPresentedModuleRep ],
        
  function( M )
    local HM, iota, HHM, bas, epsilon;
    
    HM := Hom( M );
    
    iota := MatrixOfGenerators( HM );
    
    HHM := Hom( HM );
    
    bas := MatrixOfGenerators( HHM );
    
    if IsHomalgLeftObjectOrMorphismOfLeftObjects( M ) then
        epsilon := RightDivide( iota, bas );
    else
        epsilon := LeftDivide( iota, bas );
    fi;
    
    epsilon := HomalgMap( epsilon, M, HHM );
    
    SetPropertiesIfKernelIsTorsionObject( epsilon );
    
    return epsilon;
    
end );

##
InstallMethod( LeftDualizingFunctor,
        "for homalg rings",
        [ IsHomalgRing, IsString ],
        
  function( R, name )
    
    return InsertObjectInMultiFunctor( Functor_Hom_for_fp_modules, 2, 1 * R, name );
    
end );

##
InstallMethod( LeftDualizingFunctor,
        "for homalg rings",
        [ IsHomalgRing ],
        
  function( R )
    
    if not IsBound( R!.Functor_R_Hom ) then
        if IsBound( R!.creation_number ) then
            R!.Functor_R_Hom := LeftDualizingFunctor( R, Concatenation( "R", String( R!.creation_number ), "_Hom" ) );
        else
            Error( "the homalg ring doesn't have a creation number\n" );
        fi;
    fi;
    
    return R!.Functor_R_Hom;
    
end );

##
InstallMethod( RightDualizingFunctor,
        "for homalg rings",
        [ IsHomalgRing, IsString ],
        
  function( R, name )
    
    return InsertObjectInMultiFunctor( Functor_Hom_for_fp_modules, 2, R * 1, name );
    
end );

##
InstallMethod( RightDualizingFunctor,
        "for homalg rings",
        [ IsHomalgRing ],
        
  function( R )
    
    if not IsBound( R!.Functor_Hom_R ) then
        if IsBound( R!.creation_number ) then
            R!.Functor_Hom_R := RightDualizingFunctor( R, Concatenation( "Hom_R", String( R!.creation_number ) ) );
        else
            Error( "the homalg ring doesn't have a creation number\n" );
        fi;
    fi;
    
    return R!.Functor_Hom_R;
    
end );

##
InstallMethod( Dualize,
        "for homalg modules or submodules",
        [ IsFinitelyPresentedModuleOrSubmoduleRep ],
        
  Hom );

##
InstallMethod( Dualize,
        "for homalg module maps",
        [ IsMapOfFinitelyGeneratedModulesRep ],
        
  Hom );

##
## TensorProduct
##

InstallGlobalFunction( _Functor_TensorProduct_OnModules,		### defines: TensorProduct (object part)
  function( M, N )
    local R, rl, l0, _l0, matM, matN, idM, idN, MN,
          F, gen, proc_to_readjust_generators, proc_to_normalize_generators, p;
    
    R := HomalgRing( M );
    
    ## do not use CheckIfTheyLieInTheSameCategory here
    if not IsIdenticalObj( R, HomalgRing( N ) ) then
        Error( "the rings of the source and target modules are not identical\n" );
    fi;
    
    if IsHomalgRightObjectOrMorphismOfRightObjects( M ) then
        if IsHomalgLeftObjectOrMorphismOfLeftObjects( N ) then
            rl := [ true, true ];
        else
            rl := [ true, false ];
        fi;
    else
        if IsHomalgLeftObjectOrMorphismOfLeftObjects( N ) then
            rl := [ false, true ];
        else
            rl := [ false, false ];
        fi;
    fi;
    
    l0 := NrGenerators( M );
    _l0 := NrGenerators( N );
    
    matM := MatrixOfMap( PresentationMorphism( M ) );
    matN := MatrixOfMap( PresentationMorphism( N ) );
    
    if rl = [ true, true ] or rl = [ false, false ] then
        matM := Involution( matM );	## the first module follows the second
    fi;
    
    idM := HomalgIdentityMatrix( l0, R );
    idN := HomalgIdentityMatrix( _l0, R );
    
    matM := KroneckerMat( matM, idN );
    matN := KroneckerMat( idM, matN );
    
    ## the result has the parity of the second module
    if rl[2] then
        MN := UnionOfRows( matM, matN );
        F := HomalgFreeLeftModule( NrGenerators( M ) * NrGenerators( N ), R );
    else
        MN := UnionOfColumns( matM, matN );
        F := HomalgFreeRightModule( NrGenerators( M ) * NrGenerators( N ), R );
    fi;
    
    MN := HomalgMap( MN, "free", F );
    
    return Cokernel( MN );
    
end );

##
InstallGlobalFunction( _Functor_TensorProduct_OnMaps,	### defines: TensorProduct (morphism part)
  function( F_source, F_target, arg_before_pos, phi, arg_behind_pos )
    local R, L, M_or_mor, N_or_mor, rl, idL, hull_phi,
          emb_source, emb_target, mor;
    
    R := HomalgRing( phi );
    
    if arg_before_pos = [ ] and Length( arg_behind_pos ) = 1 then
        ## phi \tensor L
        
        L := arg_behind_pos[1];
        
        M_or_mor := phi;
        N_or_mor := L;
        
    elif Length( arg_before_pos ) = 1 and arg_behind_pos = [ ] then
        ##  L \tensor phi
        
        L := arg_before_pos[1];
        
        M_or_mor := L;
        N_or_mor := phi;
        
    else
        Error( "wrong input\n" );
    fi;
    
    ## do not use CheckIfTheyLieInTheSameCategory here
    if not IsIdenticalObj( R, HomalgRing( L ) ) then
        Error( "the module and the morphism are not defined over identically the same ring\n" );
    fi;
    
    ## decide whether the output module is left/right depending on the input
    if IsHomalgRightObjectOrMorphismOfRightObjects( M_or_mor ) then
        if IsHomalgLeftObjectOrMorphismOfLeftObjects( N_or_mor ) then
            rl := [ true, true ];
        else
            rl := [ true, false ];
        fi;
    else
        if IsHomalgLeftObjectOrMorphismOfLeftObjects( N_or_mor ) then
            rl := [ false, true ];
        else
            rl := [ false, false ];
        fi;
    fi;
    
    if IsMapOfFinitelyGeneratedModulesRep( M_or_mor )
       and IsFinitelyPresentedModuleRep( N_or_mor ) then
        
        phi := M_or_mor;
        L := N_or_mor;
        
        idL := HomalgIdentityMatrix( NrGenerators( L ), R );
        
        if rl = [ true, true ] or rl = [ false, false ] then
            phi := Involution( MatrixOfMap( phi ) );	## the first module follows the second
        else
            phi := MatrixOfMap( phi );
        fi;
        
        hull_phi := KroneckerMat( phi, idL );
        
    elif IsMapOfFinitelyGeneratedModulesRep( N_or_mor )
      and IsFinitelyPresentedModuleRep( M_or_mor ) then
        
        phi := N_or_mor;
        L := M_or_mor;
        
        idL := HomalgIdentityMatrix( NrGenerators( L ), R );
        
        hull_phi := KroneckerMat( idL, MatrixOfMap( phi ) );
        
    fi;
    
    emb_source := NaturalGeneralizedEmbedding( F_source );
    emb_target := NaturalGeneralizedEmbedding( F_target );
    
    hull_phi := HomalgMap( hull_phi, Range( emb_source ), Range( emb_target ) );
    
    SetIsMorphism( hull_phi, true );
    
    mor := CompleteImageSquare( emb_source, hull_phi, emb_target );
    
    ## HasIsIsomorphism( phi ) and IsIsomorphism( phi ), resp.
    ## HasIsMorphism( phi ) and IsMorphism( phi ), and
    ## UpdateObjectsByMorphism( mor )
    ## will be taken care of in FunctorMap
    
    if HasIsEpimorphism( phi ) and IsEpimorphism( phi ) then
        
        ## check assertion
        Assert( 3, IsEpimorphism( mor ) );
        
        SetIsEpimorphism( mor, true );
        
    fi;
    
    if HasIsMonomorphism( phi ) and IsMonomorphism( phi ) and
      HasIsProjective( L ) and IsProjective( L ) then
        
        ## check assertion
        Assert( 3, IsMonomorphism( mor ) );
        
        SetIsMonomorphism( mor, true );
        
    fi;
    
    return mor;
    
end );

##  <#GAPDoc Label="Functor_TensorProduct:code">
##      <Listing Type="Code"><![CDATA[
InstallValue( Functor_TensorProduct_for_fp_modules,
        CreateHomalgFunctor(
                [ "name", "TensorProduct" ],
                [ "category", HOMALG_MODULES.category ],
                [ "operation", "TensorProductOp" ],
                [ "number_of_arguments", 2 ],
                [ "1", [ [ "covariant", "left adjoint", "distinguished" ] ] ],
                [ "2", [ [ "covariant", "left adjoint" ] ] ],
                [ "OnObjects", _Functor_TensorProduct_OnModules ],
                [ "OnMorphisms", _Functor_TensorProduct_OnMaps ],
                [ "MorphismConstructor", HOMALG_MODULES.category.MorphismConstructor ]
                )
        );
##  ]]></Listing>
##  <#/GAPDoc>

Functor_TensorProduct_for_fp_modules!.ContainerForWeakPointersOnComputedBasicObjects := true;

Functor_TensorProduct_for_fp_modules!.ContainerForWeakPointersOnComputedBasicMorphisms := true;

##
## BaseChange
##

##
InstallGlobalFunction( _functor_BaseChange_OnModules,		### defines: BaseChange (object part)
  function( _R, M )
    local R, S, lift, mat, left, distinguished, N;
    
    R := HomalgRing( _R );
    
    if IsIdenticalObj( HomalgRing( M ), R ) then
        return M;
    fi;
    
    S := HomalgRing( M );
    
    lift := HasRingRelations( S ) and IsIdenticalObj( R, AmbientRing( S ) );
    
    mat := MatrixOfRelations( M );
    
    left := IsHomalgLeftObjectOrMorphismOfLeftObjects( M );
    distinguished := IsBound( M!.distinguished ) and M!.distinguished = true;
    
    distinguished := distinguished and not lift;
    
    if not distinguished then
        if lift then
            if left then
                mat := UnionOfRows( mat );
            else
                mat := UnionOfColumns( mat );
            fi;
        else
            mat := R * mat;
            if HasRingRelations( R ) then
                if left then
                    mat := GetRidOfObsoleteRows( mat );
                else
                    mat := GetRidOfObsoleteColumns( mat );
                fi;
            fi;
        fi;
    fi;
    
    if left then
        if distinguished then
            if HasIsZero( M ) and IsZero( M ) then
                N := 0 * R;
            else
                N := 1 * R;
            fi;
        else
            N := LeftPresentation( mat );
        fi;
    else
        if distinguished then
            if HasIsZero( M ) and IsZero( M ) then
                N := R * 0;
            else
                N := R * 1;
            fi;
        else
            N := RightPresentation( mat );
        fi;
    fi;
    
    return N;
    
end );

##
InstallOtherMethod( BaseChange,
        "for homalg maps",
        [ IsHomalgRing, IsMapOfFinitelyGeneratedModulesRep ], 1001,
        
  function( R, phi )
    
    return HomalgMap( R * MatrixOfMap( phi ), R * Source( phi ), R * Range( phi ) );
    
end );

##
InstallOtherMethod( BaseChange,
        "for homalg maps",
        [ IsHomalgModule, IsMapOfFinitelyGeneratedModulesRep ], 1001,
        
  function( _R, phi )
    local R;
    
    return BaseChange( HomalgRing( _R ), phi );
    
end );

InstallValue( functor_BaseChange_for_fp_modules,
        CreateHomalgFunctor(
                [ "name", "BaseChange" ],
                [ "category", HOMALG_MODULES.category ],
                [ "operation", "BaseChange" ],
                [ "number_of_arguments", 2 ],
                [ "1", [ [ "covariant" ] ] ],
                [ "2", [ [ "covariant" ] ] ],
                [ "OnObjects", _functor_BaseChange_OnModules ]
                )
        );

functor_BaseChange_for_fp_modules!.ContainerForWeakPointersOnComputedBasicObjects := true;

#functor_BaseChange_for_fp_modules!.ContainerForWeakPointersOnComputedBasicMorphisms :=
#  ContainerForWeakPointers( TheTypeContainerForWeakPointersOnComputedValuesOfFunctor );

####################################
#
# methods for operations & attributes:
#
####################################

##
## Cokernel( phi ) and CokernelEpi( phi )
##

##  <#GAPDoc Label="functor_Cokernel">
##  <ManSection>
##    <Var Name="functor_Cokernel"/>
##    <Description>
##      The functor that associates to a map its cokernel.
##      <#Include Label="functor_Cokernel:code">
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Cokernel">
##  <ManSection>
##    <Oper Arg="phi" Name="Cokernel"/>
##    <Description>
##      The following example also makes use of the natural transformation <C>CokernelEpi</C>.
##      <Example><![CDATA[
##  gap> ZZ := HomalgRingOfIntegers( );
##  Z
##  gap> M := HomalgMatrix( "[ 2, 3, 4,   5, 6, 7 ]", 2, 3, ZZ );;
##  gap> M := LeftPresentation( M );
##  <A non-torsion left module presented by 2 relations for 3 generators>
##  gap> N := HomalgMatrix( "[ 2, 3, 4, 5,   6, 7, 8, 9 ]", 2, 4, ZZ );;
##  gap> N := LeftPresentation( N );
##  <A non-torsion left module presented by 2 relations for 4 generators>
##  gap> mat := HomalgMatrix( "[ \
##  > 1, 0, -3, -6, \
##  > 0, 1,  6, 11, \
##  > 1, 0, -3, -6  \
##  > ]", 3, 4, ZZ );;
##  gap> phi := HomalgMap( mat, M, N );;
##  gap> IsMorphism( phi );
##  true
##  gap> phi;
##  <A homomorphism of left modules>
##  gap> coker := Cokernel( phi );
##  <A left module presented by 5 relations for 4 generators>
##  gap> ByASmallerPresentation( coker );
##  <A rank 1 left module presented by 1 relation for 2 generators>
##  gap> Display( coker );
##  Z/< 8 > + Z^(1 x 1)
##  gap> nu := CokernelEpi( phi );
##  <An epimorphism of left modules>
##  gap> Display( nu );
##  [ [  -5,   0 ],
##    [  -6,   1 ],
##    [   1,  -2 ],
##    [   0,   1 ] ]
##  
##  the map is currently represented by the above 4 x 2 matrix
##  gap> DefectOfExactness( phi, nu );
##  <A zero left module>
##  gap> ByASmallerPresentation( nu );
##  <A non-zero epimorphism of left modules>
##  gap> Display( nu );
##  [ [   2,   0 ],
##    [   1,  -2 ],
##    [   0,   1 ] ]
##  
##  the map is currently represented by the above 3 x 2 matrix
##  gap> PreInverse( nu );
##  false
##  ]]></Example>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallFunctor( functor_Cokernel_for_fp_modules );

##
## ImageObject( phi ) and ImageObjectEmb( phi )
##

##  <#GAPDoc Label="functor_ImageObject">
##  <ManSection>
##    <Var Name="functor_ImageObject"/>
##    <Description>
##      The functor that associates to a map its image.
##      <#Include Label="functor_ImageObject:code">
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="ImageObject">
##  <ManSection>
##    <Oper Arg="phi" Name="ImageObject"/>
##    <Description>
##      The following example also makes use of the natural transformations <C>ImageObjectEpi</C>
##      and <C>ImageObjectEmb</C>.
##      <Example><![CDATA[
##  gap> ZZ := HomalgRingOfIntegers( );
##  Z
##  gap> M := HomalgMatrix( "[ 2, 3, 4,   5, 6, 7 ]", 2, 3, ZZ );;
##  gap> M := LeftPresentation( M );
##  <A non-torsion left module presented by 2 relations for 3 generators>
##  gap> N := HomalgMatrix( "[ 2, 3, 4, 5,   6, 7, 8, 9 ]", 2, 4, ZZ );;
##  gap> N := LeftPresentation( N );
##  <A non-torsion left module presented by 2 relations for 4 generators>
##  gap> mat := HomalgMatrix( "[ \
##  > 1, 0, -3, -6, \
##  > 0, 1,  6, 11, \
##  > 1, 0, -3, -6  \
##  > ]", 3, 4, ZZ );;
##  gap> phi := HomalgMap( mat, M, N );;
##  gap> IsMorphism( phi );
##  true
##  gap> phi;
##  <A homomorphism of left modules>
##  gap> im := ImageObject( phi );
##  <A left module presented by yet unknown relations for 3 generators>
##  gap> ByASmallerPresentation( im );
##  <A free left module of rank 1 on a free generator>
##  gap> pi := ImageObjectEpi( phi );
##  <A non-zero split epimorphism of left modules>
##  gap> epsilon := ImageObjectEmb( phi );
##  <A monomorphism of left modules>
##  gap> phi = pi * epsilon;
##  true
##  ]]></Example>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallFunctorOnObjects( functor_ImageObject_for_fp_modules );

##
## Kernel( phi ) and KernelEmb( phi )
##

##  <#GAPDoc Label="Kernel:map">
##  <ManSection>
##    <Oper Arg="phi" Name="Kernel" Label="for maps"/>
##    <Description>
##      The following example also makes use of the natural transformation <C>KernelEmb</C>.
##      <Example><![CDATA[
##  gap> ZZ := HomalgRingOfIntegers( );
##  Z
##  gap> M := HomalgMatrix( "[ 2, 3, 4,   5, 6, 7 ]", 2, 3, ZZ );;
##  gap> M := LeftPresentation( M );
##  <A non-torsion left module presented by 2 relations for 3 generators>
##  gap> N := HomalgMatrix( "[ 2, 3, 4, 5,   6, 7, 8, 9 ]", 2, 4, ZZ );;
##  gap> N := LeftPresentation( N );
##  <A non-torsion left module presented by 2 relations for 4 generators>
##  gap> mat := HomalgMatrix( "[ \
##  > 1, 0, -3, -6, \
##  > 0, 1,  6, 11, \
##  > 1, 0, -3, -6  \
##  > ]", 3, 4, ZZ );;
##  gap> phi := HomalgMap( mat, M, N );;
##  gap> IsMorphism( phi );
##  true
##  gap> phi;
##  <A homomorphism of left modules>
##  gap> ker := Kernel( phi );
##  <A cyclic left module presented by yet unknown relations for a cyclic generato\
##  r>
##  gap> Display( ker );
##  Z/< -3 >
##  gap> ByASmallerPresentation( last );
##  <A cyclic torsion left module presented by 1 relation for a cyclic generator>
##  gap> Display( ker );
##  Z/< 3 >
##  gap> iota := KernelEmb( phi );
##  <A monomorphism of left modules>
##  gap> Display( iota );
##  [ [  0,  2,  4 ] ]
##  
##  the map is currently represented by the above 1 x 3 matrix
##  gap> DefectOfExactness( iota, phi );
##  <A zero left module>
##  gap> ByASmallerPresentation( iota );
##  <A non-zero monomorphism of left modules>
##  gap> Display( iota );
##  [ [  2,  0 ] ]
##  
##  the map is currently represented by the above 1 x 2 matrix
##  gap> PostInverse( iota );
##  fail
##  ]]></Example>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##
## DefectOfExactness( cpx_post_pre )
##

##  <#GAPDoc Label="DefectOfExactness">
##  <ManSection>
##    <Oper Arg="phi, psi" Name="DefectOfExactness"/>
##    <Description>
##      We follow the associative convention for applying maps.
##      For left modules <A>phi</A> is applied first and from the right.
##      For right modules <A>psi</A> is applied first and from the left.
##      <P/>
##      The following example also makes use of the natural transformation <C>KernelEmb</C>.
##      <Example><![CDATA[
##  gap> ZZ := HomalgRingOfIntegers( );
##  Z
##  gap> M := HomalgMatrix( "[ 2, 3, 4, 0,   5, 6, 7, 0 ]", 2, 4, ZZ );;
##  gap> M := LeftPresentation( M );
##  <A non-torsion left module presented by 2 relations for 4 generators>
##  gap> N := HomalgMatrix( "[ 2, 3, 4, 5,   6, 7, 8, 9 ]", 2, 4, ZZ );;
##  gap> N := LeftPresentation( N );
##  <A non-torsion left module presented by 2 relations for 4 generators>
##  gap> mat := HomalgMatrix( "[ \
##  > 1, 3,  3,  3, \
##  > 0, 3, 10, 17, \
##  > 1, 3,  3,  3, \
##  > 0, 0,  0,  0  \
##  > ]", 4, 4, ZZ );;
##  gap> phi := HomalgMap( mat, M, N );;
##  gap> IsMorphism( phi );
##  true
##  gap> phi;
##  <A homomorphism of left modules>
##  gap> iota := KernelEmb( phi );
##  <A monomorphism of left modules>
##  gap> DefectOfExactness( iota, phi );
##  <A zero left module>
##  gap> hom_iota := Hom( iota );	## a shorthand for Hom( iota, ZZ );
##  <A homomorphism of right modules>
##  gap> hom_phi := Hom( phi );	## a shorthand for Hom( phi, ZZ );
##  <A homomorphism of right modules>
##  gap> DefectOfExactness( hom_iota, hom_phi );
##  <A cyclic right module on a cyclic generator satisfying yet unknown relations>
##  gap> ByASmallerPresentation( last );
##  <A cyclic torsion right module on a cyclic generator satisfying 1 relation>
##  gap> Display( last );
##  Z/< 2 >
##  ]]></Example>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##
## Hom( M, N )
##

##  <#GAPDoc Label="Functor_Hom">
##  <ManSection>
##    <Var Name="Functor_Hom"/>
##    <Description>
##      The bifunctor <C>Hom</C>.
##      <#Include Label="Functor_Hom:code">
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Hom">
##  <ManSection>
##    <Oper Arg="o1,o2" Name="Hom"/>
##    <Description>
##      <A>o1</A> resp. <A>o2</A> could be a module, a map, a complex (of modules or of again of complexes),
##      or a chain morphism.
##      <P/>
##      Each generator of a module of homomorphisms is displayed as a matrix of appropriate dimensions.
##      <Example><![CDATA[
##  gap> ZZ := HomalgRingOfIntegers( );
##  Z
##  gap> M := HomalgMatrix( "[ 2, 3, 4,   5, 6, 7 ]", 2, 3, ZZ );;
##  gap> M := LeftPresentation( M );
##  <A non-torsion left module presented by 2 relations for 3 generators>
##  gap> N := HomalgMatrix( "[ 2, 3, 4, 5,   6, 7, 8, 9 ]", 2, 4, ZZ );;
##  gap> N := LeftPresentation( N );
##  <A non-torsion left module presented by 2 relations for 4 generators>
##  gap> mat := HomalgMatrix( "[ \
##  > 1, 0, -3, -6, \
##  > 0, 1,  6, 11, \
##  > 1, 0, -3, -6  \
##  > ]", 3, 4, ZZ );;
##  gap> phi := HomalgMap( mat, M, N );;
##  gap> IsMorphism( phi );
##  true
##  gap> phi;
##  <A homomorphism of left modules>
##  gap> psi := Hom( phi, M );
##  <A homomorphism of right modules>
##  gap> ByASmallerPresentation( psi );
##  <A non-zero homomorphism of right modules>
##  gap> Display( psi );
##  [ [   1,   1,   0,   1 ],
##    [   2,   2,   0,   0 ],
##    [   0,   0,   6,  10 ] ]
##  
##  the map is currently represented by the above 3 x 4 matrix
##  gap> homNM := Source( psi );
##  <A rank 2 right module on 4 generators satisfying 2 relations>
##  gap> IsIdenticalObj( homNM, Hom( N, M ) );	## the caching at work
##  true
##  gap> homMM := Range( psi );
##  <A rank 1 right module on 3 generators satisfying 2 relations>
##  gap> IsIdenticalObj( homMM, Hom( M, M ) );	## the caching at work
##  true
##  gap> Display( homNM );
##  Z/< 3 > + Z/< 3 > + Z^(2 x 1)
##  gap> Display( homMM );
##  Z/< 3 > + Z/< 3 > + Z^(1 x 1)
##  gap> IsMonomorphism( psi );
##  false
##  gap> IsEpimorphism( psi );
##  false
##  gap> GeneratorsOfModule( homMM );
##  <A set of 3 generators of a homalg right module>
##  gap> Display( last );
##  [ [  0,  0,  0 ],
##    [  0,  1,  2 ],
##    [  0,  0,  0 ] ]
##  
##  the map is currently represented by the above 3 x 3 matrix
##  
##  [ [  0,  2,  4 ],
##    [  0,  0,  0 ],
##    [  0,  2,  4 ] ]
##  
##  the map is currently represented by the above 3 x 3 matrix
##  
##  [ [   0,   1,   3 ],
##    [   0,   0,  -2 ],
##    [   0,   1,   3 ] ]
##  
##  the map is currently represented by the above 3 x 3 matrix
##  
##  a set of 3 generators given by the the above matrices
##  gap> GeneratorsOfModule( homNM );
##  <A set of 4 generators of a homalg right module>
##  gap> Display( last );
##  [ [  0,  1,  2 ],
##    [  0,  1,  2 ],
##    [  0,  1,  2 ],
##    [  0,  0,  0 ] ]
##  
##  the map is currently represented by the above 4 x 3 matrix
##  
##  [ [  0,  1,  2 ],
##    [  0,  0,  0 ],
##    [  0,  0,  0 ],
##    [  0,  2,  4 ] ]
##  
##  the map is currently represented by the above 4 x 3 matrix
##  
##  [ [   0,   0,  -3 ],
##    [   0,   0,   7 ],
##    [   0,   0,  -5 ],
##    [   0,   0,   1 ] ]
##  
##  the map is currently represented by the above 4 x 3 matrix
##  
##  [ [   0,   1,  -3 ],
##    [   0,   0,  12 ],
##    [   0,   0,  -9 ],
##    [   0,   2,   6 ] ]
##  
##  the map is currently represented by the above 4 x 3 matrix
##  
##  a set of 4 generators given by the the above matrices
##  ]]></Example>
##      If for example the source <M>N</M> gets a new presentation, you will see the effect on the generators:
##      <Example><![CDATA[
##  gap> ByASmallerPresentation( N );
##  <A rank 2 left module presented by 1 relation for 3 generators>
##  gap> GeneratorsOfModule( homNM );
##  <A set of 4 generators of a homalg right module>
##  gap> Display( last );
##  [ [  0,  3,  6 ],
##    [  0,  1,  2 ],
##    [  0,  0,  0 ] ]
##  
##  the map is currently represented by the above 3 x 3 matrix
##  
##  [ [   0,   9,  18 ],
##    [   0,   0,   0 ],
##    [   0,   2,   4 ] ]
##  
##  the map is currently represented by the above 3 x 3 matrix
##  
##  [ [   0,   0,   0 ],
##    [   0,   0,  -5 ],
##    [   0,   0,   1 ] ]
##  
##  the map is currently represented by the above 3 x 3 matrix
##  
##  [ [   0,   9,  18 ],
##    [   0,   0,  -9 ],
##    [   0,   2,   6 ] ]
##  
##  the map is currently represented by the above 3 x 3 matrix
##  
##  a set of 4 generators given by the the above matrices
##  ]]></Example>
##      Now we compute a certain natural filtration on <C>Hom</C><M>(M,M)</M>:
##      <Example><![CDATA[
##  gap> dM := Resolution( M );
##  <A non-zero right acyclic complex containing a single morphism of left modules\
##   at degrees [ 0 .. 1 ]>
##  gap> hMM := Hom( dM, dM );
##  <A non-zero acyclic cocomplex containing a single morphism of right complexes \
##  at degrees [ 0 .. 1 ]>
##  gap> BMM := HomalgBicomplex( hMM );
##  <A non-zero bicocomplex containing right modules at bidegrees [ 0 .. 1 ]x
##  [ -1 .. 0 ]>
##  gap> II_E := SecondSpectralSequenceWithFiltration( BMM );
##  <A stable cohomological spectral sequence with sheets at levels 
##  [ 0 .. 2 ] each consisting of right modules at bidegrees [ -1 .. 0 ]x
##  [ 0 .. 1 ]>
##  gap> Display( II_E );
##  The associated transposed spectral sequence:
##  
##  a cohomological spectral sequence at bidegrees
##  [ [ 0 .. 1 ], [ -1 .. 0 ] ]
##  ---------
##  Level 0:
##  
##   * *
##   * *
##  ---------
##  Level 1:
##  
##   * *
##   . .
##  ---------
##  Level 2:
##  
##   s s
##   . .
##  
##  Now the spectral sequence of the bicomplex:
##  
##  a cohomological spectral sequence at bidegrees
##  [ [ -1 .. 0 ], [ 0 .. 1 ] ]
##  ---------
##  Level 0:
##  
##   * *
##   * *
##  ---------
##  Level 1:
##  
##   * *
##   * *
##  ---------
##  Level 2:
##  
##   s s
##   . s
##  gap> filt := FiltrationBySpectralSequence( II_E );
##  <A descending filtration with degrees [ -1 .. 0 ] and graded parts:
##    
##  -1:	<A non-zero cyclic torsion right module on a cyclic generator satisfying
##       yet unknown relations>
##     0:	<A rank 1 right module on 3 generators satisfying 2 relations>
##  of
##  <A right module on 4 generators satisfying yet unknown relations>>
##  gap> ByASmallerPresentation( filt );
##  <A descending filtration with degrees [ -1 .. 0 ] and graded parts:
##    
##  -1:	<A non-zero cyclic torsion right module on a cyclic generator satisfying 1\
##   relation>
##     0:	<A rank 1 right module on 2 generators satisfying 1 relation>
##  of
##  <A rank 1 right module on 3 generators satisfying 2 relations>>
##  gap> Display( filt );
##  Degree -1:
##  
##  Z/< 3 >
##  ----------
##  Degree 0:
##  
##  Z/< 3 > + Z^(1 x 1)
##  gap> Display( homMM );
##  Z/< 3 > + Z/< 3 > + Z^(1 x 1)
##  ]]></Example>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallFunctor( Functor_Hom_for_fp_modules );

##
## TensorProduct( M, N )	( M * N )
##

##  <#GAPDoc Label="Functor_TensorProduct">
##  <ManSection>
##    <Var Name="Functor_TensorProduct"/>
##    <Description>
##      The tensor product bifunctor.
##      <#Include Label="Functor_TensorProduct:code">
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="TensorProduct">
##  <ManSection>
##    <Oper Arg="o1,o2" Name="TensorProduct"/>
##    <Oper Arg="o1,o2" Name="\*" Label="TensorProduct"/>
##    <Description>
##      <A>o1</A> resp. <A>o2</A> could be a module, a map, a complex (of modules or of again of complexes),
##      or a chain morphism.
##      <P/>
##      The symbol <C>*</C> is a shorthand for several operations associated with the functor <C>Functor_TensorProduct_for_fp_modules</C>
##      installed under the name <C>TensorProduct</C>.
##      <Example><![CDATA[
##  gap> ZZ := HomalgRingOfIntegers( );
##  Z
##  gap> M := HomalgMatrix( "[ 2, 3, 4,   5, 6, 7 ]", 2, 3, ZZ );
##  <A 2 x 3 matrix over an internal ring>
##  gap> M := LeftPresentation( M );
##  <A non-torsion left module presented by 2 relations for 3 generators>
##  gap> N := HomalgMatrix( "[ 2, 3, 4, 5,   6, 7, 8, 9 ]", 2, 4, ZZ );
##  <A 2 x 4 matrix over an internal ring>
##  gap> N := LeftPresentation( N );
##  <A non-torsion left module presented by 2 relations for 4 generators>
##  gap> mat := HomalgMatrix( "[ \
##  > 1, 0, -3, -6, \
##  > 0, 1,  6, 11, \
##  > 1, 0, -3, -6  \
##  > ]", 3, 4, ZZ );
##  <A 3 x 4 matrix over an internal ring>
##  gap> phi := HomalgMap( mat, M, N );
##  <A "homomorphism" of left modules>
##  gap> IsMorphism( phi );
##  true
##  gap> phi;
##  <A homomorphism of left modules>
##  gap> L := Hom( ZZ, M );
##  <A rank 1 right module on 3 generators satisfying yet unknown relations>
##  gap> ByASmallerPresentation( L );
##  <A rank 1 right module on 2 generators satisfying 1 relation>
##  gap> Display( L );
##  Z/< 3 > + Z^(1 x 1)
##  gap> L;
##  <A rank 1 right module on 2 generators satisfying 1 relation>
##  gap> psi := phi * L;
##  <A homomorphism of right modules>
##  gap> ByASmallerPresentation( psi );
##  <A non-zero homomorphism of right modules>
##  gap> Display( psi );
##  [ [   0,   0,   1,   1 ],
##    [   0,   0,   8,   1 ],
##    [   0,   0,   0,  -2 ],
##    [   0,   0,   0,   2 ] ]
##  
##  the map is currently represented by the above 4 x 4 matrix
##  gap> ML := Source( psi );
##  <A rank 1 right module on 4 generators satisfying 3 relations>
##  gap> IsIdenticalObj( ML, M * L );	## the caching at work
##  true
##  gap> NL := Range( psi );
##  <A rank 2 right module on 4 generators satisfying 2 relations>
##  gap> IsIdenticalObj( NL, N * L );	## the caching at work
##  true
##  gap> Display( ML );
##  Z/< 3 > + Z/< 3 > + Z/< 3 > + Z^(1 x 1)
##  gap> Display( NL );
##  Z/< 3 > + Z/< 12 > + Z^(2 x 1)
##  ]]></Example>
##      Now we compute a certain natural filtration on the tensor product <M>M</M><C>*</C><M>L</M>:
##      <Example><![CDATA[
##  gap> P := Resolution( M );
##  <A non-zero right acyclic complex containing a single morphism of left modules\
##   at degrees [ 0 .. 1 ]>
##  gap> GP := Hom( P );
##  <A non-zero acyclic cocomplex containing a single morphism of right modules at\
##   degrees [ 0 .. 1 ]>
##  gap> CE := Resolution( GP );
##  <An acyclic cocomplex containing a single morphism of right complexes at degre\
##  es [ 0 .. 1 ]>
##  gap> FCE := Hom( CE, L );
##  <A non-zero acyclic complex containing a single morphism of left cocomplexes a\
##  t degrees [ 0 .. 1 ]>
##  gap> BC := HomalgBicomplex( FCE );
##  <A non-zero bicomplex containing left modules at bidegrees [ 0 .. 1 ]x
##  [ -1 .. 0 ]>
##  gap> II_E := SecondSpectralSequenceWithFiltration( BC );
##  <A stable homological spectral sequence with sheets at levels 
##  [ 0 .. 2 ] each consisting of left modules at bidegrees [ -1 .. 0 ]x
##  [ 0 .. 1 ]>
##  gap> Display( II_E );
##  The associated transposed spectral sequence:
##  
##  a homological spectral sequence at bidegrees
##  [ [ 0 .. 1 ], [ -1 .. 0 ] ]
##  ---------
##  Level 0:
##  
##   * *
##   * *
##  ---------
##  Level 1:
##  
##   * *
##   . .
##  ---------
##  Level 2:
##  
##   s s
##   . .
##  
##  Now the spectral sequence of the bicomplex:
##  
##  a homological spectral sequence at bidegrees
##  [ [ -1 .. 0 ], [ 0 .. 1 ] ]
##  ---------
##  Level 0:
##  
##   * *
##   * *
##  ---------
##  Level 1:
##  
##   * *
##   . s
##  ---------
##  Level 2:
##  
##   s s
##   . s
##  gap> filt := FiltrationBySpectralSequence( II_E );
##  <An ascending filtration with degrees [ -1 .. 0 ] and graded parts:
##     0:	<A rank 1 left module presented by 1 relation for 2 generators>
##    -1:	<A non-zero left module presented by 2 relations for 2 generators>
##  of
##  <A non-zero left module presented by 10 relations for 6 generators>>
##  gap> ByASmallerPresentation( filt );
##  <An ascending filtration with degrees [ -1 .. 0 ] and graded parts:
##     0:	<A rank 1 left module presented by 1 relation for 2 generators>
##    -1:	<A non-zero torsion left module presented by 2 relations
##               for 2 generators>
##  of
##  <A rank 1 left module presented by 3 relations for 4 generators>>
##  gap> Display( filt );
##  Degree 0:
##  
##  Z/< 3 > + Z^(1 x 1)
##  ----------
##  Degree -1:
##  
##  Z/< 3 > + Z/< 3 >
##  gap> Display( ML );
##  Z/< 3 > + Z/< 3 > + Z/< 3 > + Z^(1 x 1)
##  ]]></Example>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallFunctor( Functor_TensorProduct_for_fp_modules );

## for convenience
InstallOtherMethod( \*,
        "for homalg modules",
        [ IsStructureObjectOrObjectOrMorphism, IsFinitelyPresentedModuleRep ],
        
  function( M, N )
    
    return TensorProduct( M, N );
    
end );

## for convenience
InstallOtherMethod( \*,
        "for homalg modules",
        [ IsFinitelyPresentedModuleRep, IsStructureObjectOrObjectOrMorphism ],
        
  function( M, N )
    
    return TensorProduct( M, N );
    
end );

## for convenience
InstallOtherMethod( \*,
        "for homalg modules",
        [ IsHomalgComplex, IsHomalgComplex ],
        
  function( M, N )
    
    return TensorProduct( M, N );
    
end );

##  <#GAPDoc Label="\*:ModuleBaseChange">
##  <ManSection>
##    <Oper Arg="R, M" Name="\*" Label="transfer a module over a different ring"/>
##    <Oper Arg="M, R" Name="\*" Label="transfer a module over a different ring (right)"/>
##    <Returns>a &homalg; module</Returns>
##    <Description>
##      Transfers the <M>S</M>-module <A>M</A> over the &homalg; ring <A>R</A>. This works only in three cases:
##      <Enum>
##        <Item><M>S</M> is a subring of <A>R</A>.</Item>
##        <Item><A>R</A> is a residue class ring of <M>S</M> constructed using <C>/</C>.</Item>
##        <Item><A>R</A> is a subring of <M>S</M> and the entries of the current matrix of <M>S</M>-relations of <A>M</A>
##          lie in <A>R</A>.</Item>
##      </Enum>
##      CAUTION: So it is not suited for general base change.
##      <Example><![CDATA[
##  gap> ZZ := HomalgRingOfIntegers( );
##  Z
##  gap> Display( ZZ );
##  <An internal ring>
##  gap> Z4 := ZZ / 4;
##  Z/( 4 )
##  gap> Display( Z4 );
##  <A residue class ring>
##  gap> M := HomalgDiagonalMatrix( [ 2 .. 4 ], ZZ );
##  <An unevaluated diagonal 3 x 3 matrix over an internal ring>
##  gap> M := LeftPresentation( M );
##  <A torsion left module presented by 3 relations for 3 generators>
##  gap> Display( M );
##  Z/< 2 > + Z/< 3 > + Z/< 4 >
##  gap> M;
##  <A torsion left module presented by 3 relations for 3 generators>
##  gap> N := Z4 * M; ## or N := M * Z4;
##  <A non-torsion left module presented by 2 relations for 3 generators>
##  gap> ByASmallerPresentation( N );
##  <A non-torsion left module presented by 1 relation for 2 generators>
##  gap> Display( N );
##  Z/( 4 )/< |[ 2 ]| > + Z/( 4 )^(1 x 1)
##  gap> N;
##  <A non-torsion left module presented by 1 relation for 2 generators>
##  ]]></Example>
##      <Example><![CDATA[
##  gap> ZZ := HomalgRingOfIntegers( );
##  Z
##  gap> M := HomalgMatrix( "[ \
##  > 2, 3, 4, \
##  > 5, 6, 7  \
##  > ]", 2, 3, ZZ );
##  <A 2 x 3 matrix over an internal ring>
##  gap> M := LeftPresentation( M );
##  <A non-torsion left module presented by 2 relations for 3 generators>
##  gap> Z4 := ZZ / 4;
##  Z/( 4 )
##  gap> Display( Z4 );
##  <A residue class ring>
##  gap> M4 := Z4 * M;
##  <A non-torsion left module presented by 2 relations for 3 generators>
##  gap> Display( M4 );
##  [ [  2,  3,  4 ],
##    [  5,  6,  7 ] ]
##  
##  modulo [ 4 ]
##  
##  Cokernel of the map
##  
##  Z/( 4 )^(1x2) --> Z/( 4 )^(1x3),
##  
##  currently represented by the above matrix
##  gap> d := Resolution( 2, M4 );
##  <A right acyclic complex containing 2 morphisms of left modules at degrees 
##  [ 0 .. 2 ]>
##  gap> dd := Hom( d, Z4 );
##  <A cocomplex containing 2 morphisms of right modules at degrees [ 0 .. 2 ]>
##  gap> DD := Resolution( 2, dd );
##  <A cocomplex containing 2 morphisms of right complexes at degrees [ 0 .. 2 ]>
##  gap> D := Hom( DD, Z4 );
##  <A complex containing 2 morphisms of left cocomplexes at degrees [ 0 .. 2 ]>
##  gap> C := ZZ * D;
##  <A "complex" containing 2 morphisms of left cocomplexes at degrees [ 0 .. 2 ]>
##  gap> LowestDegreeObject( C );
##  <A "cocomplex" containing 2 morphisms of left modules at degrees [ 0 .. 2 ]>
##  gap> Display( last );
##  -------------------------
##  at cohomology degree: 2
##  0
##  ------------^------------
##  (an empty 1 x 0 matrix)
##  
##  the map is currently represented by the above 1 x 0 matrix
##  -------------------------
##  at cohomology degree: 1
##  Z/< 4 > 
##  ------------^------------
##  [ [  0 ],
##    [  1 ],
##    [  2 ],
##    [  1 ] ]
##  
##  the map is currently represented by the above 4 x 1 matrix
##  -------------------------
##  at cohomology degree: 0
##  Z/< 4 > + Z/< 4 > + Z/< 4 > + Z/< 4 > 
##  -------------------------
##  ]]></Example>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallFunctor( functor_BaseChange_for_fp_modules );

##
## Ext( c, M, N )
##

##  <#GAPDoc Label="Functor_Ext">
##  <ManSection>
##    <Var Name="Functor_Ext"/>
##    <Description>
##      The bifunctor <C>Ext</C>.
##      <P/>
##      <#Include Label="Functor_Ext:code">
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Functor_Ext:code">
##    Below is the only <E>specific</E> line of code used to define <C>Functor_Ext_for_fp_modules</C>
##    and all the different operations <C>Ext</C> in &homalg;.
##      <Listing Type="Code"><![CDATA[
RightSatelliteOfCofunctor( Functor_Hom_for_fp_modules, "Ext" );
##  ]]></Listing>
##  <#/GAPDoc>

##  <#GAPDoc Label="RightSatelliteOfCofunctor:example">
##    <#Include Label="Functor_Ext:code">
##  <#/GAPDoc>

##  <#GAPDoc Label="Ext">
##  <ManSection>
##    <Oper Arg="[c,]o1,o2[,str]" Name="Ext"/>
##    <Description>
##      Compute the <A>c</A>-th extension object of <A>o1</A> with <A>o2</A> where <A>c</A> is a nonnegative integer
##      and <A>o1</A> resp. <A>o2</A> could be a module, a map, a complex (of modules or of again of complexes),
##      or a chain morphism. If <A>str</A>=<Q>a</Q> then the (cohomologically) graded object
##      <M>Ext^i(</M><A>o1</A>,<A>o2</A><M>)</M> for <M>0 \leq i \leq</M><A>c</A> is computed.
##      If neither <A>c</A> nor <A>str</A> is specified then the cohomologically graded object
##      <M>Ext^i(</M><A>o1</A>,<A>o2</A><M>)</M> for <M>0 \leq i \leq d</M> is computed,
##      where <M>d</M> is the length of the internally computed free resolution of <A>o1</A>.
##      <P/>
##      Each generator of a module of extensions is displayed as a matrix of appropriate dimensions.
##      <Example><![CDATA[
##  gap> ZZ := HomalgRingOfIntegers( );
##  Z
##  gap> M := HomalgMatrix( "[ 2, 3, 4,   5, 6, 7 ]", 2, 3, ZZ );;
##  gap> M := LeftPresentation( M );
##  <A non-torsion left module presented by 2 relations for 3 generators>
##  gap> N := TorsionObject( M );
##  <A cyclic torsion left module presented by yet unknown relations for a cyclic \
##  generator>
##  gap> iota := TorsionObjectEmb( M );
##  <A monomorphism of left modules>
##  gap> psi := Ext( 1, iota, N );
##  <A homomorphism of right modules>
##  gap> ByASmallerPresentation( psi );
##  <A non-zero homomorphism of right modules>
##  gap> Display( psi );
##  [ [  2 ] ]
##  
##  the map is currently represented by the above 1 x 1 matrix
##  gap> extNN := Range( psi );
##  <A non-zero cyclic torsion right module on a cyclic generator satisfying 1 rel\
##  ation>
##  gap> IsIdenticalObj( extNN, Ext( 1, N, N ) );	## the caching at work
##  true
##  gap> extMN := Source( psi );
##  <A non-zero cyclic torsion right module on a cyclic generator satisfying 1 rel\
##  ation>
##  gap> IsIdenticalObj( extMN, Ext( 1, M, N ) );	## the caching at work
##  true
##  gap> Display( extNN );
##  Z/< 3 >
##  gap> Display( extMN );
##  Z/< 3 >
##  ]]></Example>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##

##
## Tor( c, M, N )
##

##  <#GAPDoc Label="Functor_Tor">
##  <ManSection>
##    <Var Name="Functor_Tor"/>
##    <Description>
##      The bifunctor <C>Tor</C>.
##      <P/>
##      <#Include Label="Functor_Tor:code">
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Functor_Tor:code">
##    Below is the only <E>specific</E> line of code used to define <C>Functor_Tor_for_fp_modules</C>
##    and all the different operations <C>Tor</C> in &homalg;.
##      <Listing Type="Code"><![CDATA[
LeftSatelliteOfFunctor( Functor_TensorProduct_for_fp_modules, "Tor" );
##  ]]></Listing>
##  <#/GAPDoc>

##  <#GAPDoc Label="LeftSatelliteOfFunctor:example">
##    <#Include Label="Functor_Tor:code">
##  <#/GAPDoc>

##  <#GAPDoc Label="Tor">
##  <ManSection>
##    <Oper Arg="[c,]o1,o2[,str]" Name="Tor"/>
##    <Description>
##      Compute the <A>c</A>-th torsion object of <A>o1</A> with <A>o2</A> where <A>c</A> is a nonnegative integer
##      and <A>o1</A> resp. <A>o2</A> could be a module, a map, a complex (of modules or of again of complexes),
##      or a chain morphism. If <A>str</A>=<Q>a</Q> then the (cohomologically) graded object
##      <M>Tor_i(</M><A>o1</A>,<A>o2</A><M>)</M> for <M>0 \leq i \leq</M><A>c</A> is computed.
##      If neither <A>c</A> nor <A>str</A> is specified then the cohomologically graded object
##      <M>Tor_i(</M><A>o1</A>,<A>o2</A><M>)</M> for <M>0 \leq i \leq d</M> is computed,
##      where <M>d</M> is the length of the internally computed free resolution of <A>o1</A>.
##      <Example><![CDATA[
##  gap> ZZ := HomalgRingOfIntegers( );
##  Z
##  gap> M := HomalgMatrix( "[ 2, 3, 4,   5, 6, 7 ]", 2, 3, ZZ );;
##  gap> M := LeftPresentation( M );
##  <A non-torsion left module presented by 2 relations for 3 generators>
##  gap> N := TorsionObject( M );
##  <A cyclic torsion left module presented by yet unknown relations for a cyclic \
##  generator>
##  gap> iota := TorsionObjectEmb( M );
##  <A monomorphism of left modules>
##  gap> psi := Tor( 1, iota, N );
##  <A homomorphism of left modules>
##  gap> ByASmallerPresentation( psi );
##  <A non-zero homomorphism of left modules>
##  gap> Display( psi );
##  [ [  1 ] ]
##  
##  the map is currently represented by the above 1 x 1 matrix
##  gap> torNN := Source( psi );
##  <A non-zero cyclic torsion left module presented by 1 relation for a cyclic ge\
##  nerator>
##  gap> IsIdenticalObj( torNN, Tor( 1, N, N ) );	## the caching at work
##  true
##  gap> torMN := Range( psi );
##  <A non-zero cyclic torsion left module presented by 1 relation for a cyclic ge\
##  nerator>
##  gap> IsIdenticalObj( torMN, Tor( 1, M, N ) );	## the caching at work
##  true
##  gap> Display( torNN );
##  Z/< 3 >
##  gap> Display( torMN );
##  Z/< 3 >
##  ]]></Example>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##

##
## RHom( c, M, N )
##

##  <#GAPDoc Label="Functor_RHom">
##  <ManSection>
##    <Var Name="Functor_RHom"/>
##    <Description>
##      The bifunctor <C>RHom</C>.
##      <P/>
##      <#Include Label="Functor_RHom:code">
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Functor_RHom:code">
##    Below is the only <E>specific</E> line of code used to define <C>Functor_RHom_for_fp_modules</C>
##    and all the different operations <C>RHom</C> in &homalg;.
##      <Listing Type="Code"><![CDATA[
RightDerivedCofunctor( Functor_Hom_for_fp_modules );
##  ]]></Listing>
##  <#/GAPDoc>

##  <#GAPDoc Label="RightDerivedCofunctor:example">
##    <#Include Label="Functor_RHom:code">
##  <#/GAPDoc>

##  <#GAPDoc Label="RHom">
##  <ManSection>
##    <Oper Arg="[c,]o1,o2[,str]" Name="RHom"/>
##    <Description>
##      Compute the <A>c</A>-th extension object of <A>o1</A> with <A>o2</A> where <A>c</A> is a nonnegative integer
##      and <A>o1</A> resp. <A>o2</A> could be a module, a map, a complex (of modules or of again of complexes),
##      or a chain morphism. The string <A>str</A> may take different values:
##      <List>
##        <Item>If <A>str</A>=<Q>a</Q> then <M>R^i Hom(</M><A>o1</A>,<A>o2</A><M>)</M> for <M>0 \leq i \leq</M><A>c</A>
##          is computed.</Item>
##        <Item>If <A>str</A>=<Q>c</Q> then the <A>c</A>-th connecting homomorphism with respect to
##          the short exact sequence <A>o1</A> is computed.</Item>
##        <Item>If <A>str</A>=<Q>t</Q> then the exact triangle upto cohomological degree <A>c</A> with respect to
##          the short exact sequence <A>o1</A> is computed.</Item>
##      </List>
##      If neither <A>c</A> nor <A>str</A> is specified then the cohomologically graded object
##      <M>R^i Hom(</M><A>o1</A>,<A>o2</A><M>)</M> for <M>0 \leq i \leq d</M> is computed,
##      where <M>d</M> is the length of the internally computed free resolution of <A>o1</A>.
##      <P/>
##      Each generator of a module of derived homomorphisms is displayed as a matrix of appropriate dimensions.
##      <#Include Label="RHom_Z">
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##

##
## LTensorProduct( c, M, N )
##

##  <#GAPDoc Label="Functor_LTensorProduct">
##  <ManSection>
##    <Var Name="Functor_LTensorProduct"/>
##    <Description>
##      The bifunctor <C>LTensorProduct</C>.
##      <P/>
##      <#Include Label="Functor_LTensorProduct:code">
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Functor_LTensorProduct:code">
##    Below is the only <E>specific</E> line of code used to define <C>Functor_LTensorProduct_for_fp_modules</C>
##    and all the different operations <C>LTensorProduct</C> in &homalg;.
##      <Listing Type="Code"><![CDATA[
LeftDerivedFunctor( Functor_TensorProduct_for_fp_modules );
##  ]]></Listing>
##  <#/GAPDoc>

##  <#GAPDoc Label="LeftDerivedFunctor:example">
##    <#Include Label="Functor_LTensorProduct:code">
##  <#/GAPDoc>

##  <#GAPDoc Label="LTensorProduct">
##  <ManSection>
##    <Oper Arg="[c,]o1,o2[,str]" Name="LTensorProduct"/>
##    <Description>
##      Compute the <A>c</A>-th torsion object of <A>o1</A> with <A>o2</A> where <A>c</A> is a nonnegative integer
##      and <A>o1</A> resp. <A>o2</A> could be a module, a map, a complex (of modules or of again of complexes),
##      or a chain morphism. The string <A>str</A> may take different values:
##      <List>
##        <Item>If <A>str</A>=<Q>a</Q> then <M>L_i TensorProduct(</M><A>o1</A>,<A>o2</A><M>)</M> for <M>0 \leq i \leq</M><A>c</A>
##          is computed.</Item>
##        <Item>If <A>str</A>=<Q>c</Q> then the <A>c</A>-th connecting homomorphism with respect to
##          the short exact sequence <A>o1</A> is computed.</Item>
##        <Item>If <A>str</A>=<Q>t</Q> then the exact triangle upto cohomological degree <A>c</A> with respect to
##          the short exact sequence <A>o1</A> is computed.</Item>
##      </List>
##      If neither <A>c</A> nor <A>str</A> is specified then the cohomologically graded object
##      <M>L_i TensorProduct(</M><A>o1</A>,<A>o2</A><M>)</M> for <M>0 \leq i \leq d</M> is computed,
##      where <M>d</M> is the length of the internally computed free resolution of <A>o1</A>.
##      <P/>
##      Each generator of a module of derived homomorphisms is displayed as a matrix of appropriate dimensions.
##      <#Include Label="LTensorProduct_Z">
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##

##
## HomHom( M, K, N ) = Hom( Hom( M, K ), N )
##

##  <#GAPDoc Label="Functor_HomHom">
##  <ManSection>
##    <Var Name="Functor_HomHom"/>
##    <Description>
##      The bifunctor <C>HomHom</C>.
##      <P/>
##      <#Include Label="Functor_HomHom:code">
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Functor_HomHom:code">
##    Below is the only <E>specific</E> line of code used to define <C>Functor_HomHom_for_fp_modules</C>
##    and all the different operations <C>HomHom</C> in &homalg;.
##      <Listing Type="Code"><![CDATA[
Functor_Hom_for_fp_modules * Functor_Hom_for_fp_modules;
##  ]]></Listing>
##  <#/GAPDoc>

##  <#GAPDoc Label="ComposeFunctors:example">
##    <#Include Label="Functor_HomHom:code">
##  <#/GAPDoc>

##
## LHomHom( M, K, N ) = L(Hom( Hom( -, K ), N ))( M )
##

##  <#GAPDoc Label="Functor_LHomHom">
##  <ManSection>
##    <Var Name="Functor_LHomHom"/>
##    <Description>
##      The bifunctor <C>LHomHom</C>.
##      <P/>
##      <#Include Label="Functor_LHomHom:code">
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="Functor_LHomHom:code">
##    Below is the only <E>specific</E> line of code used to define <C>Functor_LHomHom_for_fp_modules</C>
##    and all the different operations <C>LHomHom</C> in &homalg;.
##      <Listing Type="Code"><![CDATA[
LeftDerivedFunctor( Functor_HomHom_for_fp_modules );
##  ]]></Listing>
##  <#/GAPDoc>

##  <#GAPDoc Label="LeftDerivedFunctor:example2">
##    <#Include Label="Functor_LHomHom:code">
##  <#/GAPDoc>