#############################################################################
##
##  BasicFunctors.gi            Graded Modules package
##
##  Copyright 2007-2010, Mohamed Barakat, University of Kaiserslautern
##                       Markus Lange-Hegermann, RWTH Aachen
##
##  Implementation stuff for some graded tool functors.
##
#############################################################################

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

##
## Cokernel
##

##
InstallGlobalFunction( _Functor_Cokernel_OnGradedModules,	### defines: Cokernel(Epi)
  function( phi )
    local S, R, U_phi, epi, positions, p2, p, coker_U_phi, coker, gen_iso, img_emb, emb;
    
    if HasCokernelEpi( phi ) then
      return Range( CokernelEpi( phi ) );
    fi;
    
    S := HomalgRing( phi );
    R := UnderlyingNonGradedRing( S );

    U_phi := UnderlyingMorphism( phi ) ;
    
    epi := CokernelEpi( U_phi );
    
    if IsBound( epi!.DefaultPresentationOfCokernelEpi ) then
        positions := epi!.DefaultPresentationOfCokernelEpi;
    else
        positions := [ 1, 1 ];
    fi;
    
    p2 := PositionOfTheDefaultPresentation( Range( U_phi ) );
    SetPositionOfTheDefaultPresentation( Range( U_phi ), positions[1] );
    
    coker_U_phi := Cokernel( U_phi );
    
    # The cokernel get the same degrees as the range of phi,
    # since the epimorphism on the cokernel is induced by the indentity matrix.
    # By choice of the first presentation we ensure, that we can use this trick.
    p := PositionOfTheDefaultPresentation( coker_U_phi );
    SetPositionOfTheDefaultPresentation( coker_U_phi, positions[2] );
    epi := GradedMap( epi, Range( phi ), DegreesOfGenerators( Range( phi ) ), HomalgRing( phi ) );
    
    if HasIsMorphism( phi ) and IsMorphism( phi ) then
        Assert( 4, IsMorphism( epi ) );
        SetIsMorphism( epi, true );
    fi;
    
    SetPositionOfTheDefaultPresentation( coker_U_phi, p );
    SetPositionOfTheDefaultPresentation( Range( U_phi ), p2 );
    
    ## set the attribute CokernelEpi (specific for Cokernel):
    SetCokernelEpi( phi, epi );

    coker := Range( epi );
    
    ## the generalized inverse of the natural epimorphism
    ## (cf. [Bar, Cor. 4.8])
    gen_iso := GradedMap( InverseOfGeneralizedMorphismWithFullDomain( UnderlyingMorphism( epi ) ), coker, Range( phi ), S );
    
    ## 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 );
    
    if HasIsMonomorphism( phi ) and IsMonomorphism( phi ) and
       HasIsModuleOfGlobalSectionsTruncatedAtCertainDegree( Source( phi ) ) and 
       IsInt( IsModuleOfGlobalSectionsTruncatedAtCertainDegree( Source( phi ) ) ) and
       HasIsModuleOfGlobalSectionsTruncatedAtCertainDegree( Range( phi ) ) and 
       IsInt( IsModuleOfGlobalSectionsTruncatedAtCertainDegree( Range( phi ) ) ) and
       IsModuleOfGlobalSectionsTruncatedAtCertainDegree( Source( phi ) ) = IsModuleOfGlobalSectionsTruncatedAtCertainDegree( Range( phi ) ) then
        SetTrivialArtinianSubmodule( coker, IsInt( IsModuleOfGlobalSectionsTruncatedAtCertainDegree( Source( phi ) ) ) );
    fi;
    
    #=====# 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 := GradedMap( NaturalGeneralizedEmbedding( UnderlyingModule( coker ) ), coker, Range( phi ) );
    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_ForGradedModules,
        CreateHomalgFunctor(
                [ "name", "Cokernel" ],
                [ "category", HOMALG_GRADED_MODULES.category ],
                [ "operation", "Cokernel" ],
                [ "natural_transformation", "CokernelEpi" ],
                [ "special", true ],
                [ "number_of_arguments", 1 ],
                [ "1", [ [ "covariant" ],
                        [ IsMapOfGradedModulesRep,
                          [ IsHomalgChainMorphism, IsImageSquare ] ] ] ],
                [ "OnObjects", _Functor_Cokernel_OnGradedModules ]
                )
        );
##  ]]></Listing>
##  <#/GAPDoc>

functor_Cokernel_ForGradedModules!.ContainerForWeakPointersOnComputedBasicMorphisms := true;

##
## ImageObject
##

InstallGlobalFunction( _Functor_ImageObject_OnGradedModules,	### defines: ImageObject(Emb)
  function( phi )
    local S, emb, img, coker_epi, img_submodule;
    
    S := HomalgRing( phi );
    
    if HasImageObjectEmb( phi ) then
        return Source( ImageObjectEmb( phi ) );
    fi;
    
    emb := GradedMap( ImageObjectEmb( UnderlyingMorphism( phi ) ), "create", Range( phi ), HomalgRing( phi ) );
    
    if HasIsMorphism( phi ) and IsMorphism( phi ) then
        Assert( 4, IsMorphism( emb ) );
        SetIsMorphism( emb, true );
    fi;
    
    ## 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 );
    
    if HasIsEpimorphism( phi ) and IsEpimorphism( phi ) then
        SetIsIsomorphism( emb, true );
        UpdateObjectsByMorphism( emb );
    elif HasIsMorphism( phi ) and IsMorphism( phi ) then
        SetIsMonomorphism( emb, true );
        ## TODO: This should be stored anyway, in case phi becomes known to be a morphism.
    fi;
    
    ## get the image module from its embedding
    img := Source( 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_ForGradedModules,
        CreateHomalgFunctor(
                [ "name", "ImageObject" ],
                [ "category", HOMALG_GRADED_MODULES.category ],
                [ "operation", "ImageObject" ],
                [ "natural_transformation", "ImageObjectEmb" ],
                [ "number_of_arguments", 1 ],
                [ "1", [ [ "covariant" ],
                        [ IsMapOfGradedModulesRep ] ] ],
                [ "OnObjects", _Functor_ImageObject_OnGradedModules ]
                )
        );
##  ]]></Listing>
##  <#/GAPDoc>

functor_ImageObject_ForGradedModules!.ContainerForWeakPointersOnComputedBasicMorphisms := true;

##
## Kernel
##

# not needed, homalg calls KernelSubobject, which is implemented in LIGrHOM

##
## GradedHom
##

InstallGlobalFunction( _Functor_GradedHom_OnGradedModules,		### defines: GradedHom (object part)
  function( M, N )
    local S, hom, emb, degHP0N, p, HP0N;
    
    S := HomalgRing( M );
    
    CheckIfTheyLieInTheSameCategory( M, N );
    
    hom := Hom( UnderlyingModule( M ), UnderlyingModule( N ) );
    hom := UnderlyingSubobject( hom );
    emb := EmbeddingInSuperObject( hom );
    ## This highly depends on the internal structure of _Functor_Hom_OnModules and is not intrinsic
    ## HP0N is the source of alpha (the map of which hom is the kernel)
    degHP0N := Concatenation( List( DegreesOfGenerators( M ), m -> -m + DegreesOfGenerators( N ) ) );
    
    p := PositionOfTheDefaultPresentation( Range( emb ) );
    SetPositionOfTheDefaultPresentation( Range( emb ), 1 );
    
    HP0N := GradedModule( Range( emb ), degHP0N, S );
    
    SetPositionOfTheDefaultPresentation( HP0N, p );
    
    emb := GradedMap( emb, "create", HP0N, S );
    
    Assert( 4, IsMorphism( emb ) );
    SetIsMorphism( emb, true );
    
    hom := Source( emb );
    Assert( 6, IsIdenticalObj( Hom( UnderlyingModule( M ), UnderlyingModule( N ) ), UnderlyingModule( hom ) ) );
    
    hom!.NaturalGeneralizedEmbedding := emb;
    
    # we can not set ModuleOfGlobalSections, because negative degrees would have to be truncated
    
    return hom;
    
end );

##
InstallGlobalFunction( _Functor_GradedHom_OnGradedMaps,     ### defines: GradedHom (morphism part)
  function( F_source, F_target, arg_before_pos, phi, arg_behind_pos )
    local psi;

    if arg_before_pos = [ ] and Length( arg_behind_pos ) = 1 then
        
        psi := GradedMap( Hom( UnderlyingMorphism( phi ), UnderlyingModule( arg_behind_pos[1] ) ), F_source, F_target );
        
    elif Length( arg_before_pos ) = 1 and arg_behind_pos = [ ] then
        
        psi := GradedMap( Hom( UnderlyingModule( arg_before_pos[1] ), UnderlyingMorphism( phi ) ), F_source, F_target );
        
    else
        Error( "wrong input\n" );
    fi;
    
    if HasIsMorphism( phi ) and IsMorphism( phi ) then
        Assert( 4, IsMorphism( psi ) );
        SetIsMorphism( psi, true );
    fi;
    
    return psi;
     
end );

##  <#GAPDoc Label="Functor_Hom:code">
##      <Listing Type="Code"><![CDATA[
InstallValue( Functor_GradedHom_ForGradedModules,
        CreateHomalgFunctor(
                [ "name", "GradedHom" ],
                [ "category", HOMALG_GRADED_MODULES.category ],
                [ "operation", "GradedHom" ],
                [ "number_of_arguments", 2 ],
                [ "1", [ [ "contravariant", "right adjoint", "distinguished" ], HOMALG_GRADED_MODULES.FunctorOn ] ],
                [ "2", [ [ "covariant", "left exact" ], HOMALG_GRADED_MODULES.FunctorOn ] ],
                [ "OnObjects", _Functor_GradedHom_OnGradedModules ],
                [ "OnMorphisms", _Functor_GradedHom_OnGradedMaps ],
                [ "MorphismConstructor", HOMALG_GRADED_MODULES.category.MorphismConstructor ]
                )
        );
##  ]]></Listing>
##  <#/GAPDoc>

Functor_GradedHom_ForGradedModules!.ContainerForWeakPointersOnComputedBasicObjects := true;

Functor_GradedHom_ForGradedModules!.ContainerForWeakPointersOnComputedBasicMorphisms := true;

##
InstallMethod( NatTrIdToHomHom_R,
        "for homalg modules",
        [ IsGradedModuleRep ],
        
  function( M )
    local HHM, nat, epsilon;
    
    HHM := GradedHom( GradedHom( M ) );
    
    nat := NatTrIdToHomHom_R( UnderlyingModule( M ) );
    
    epsilon := GradedMap( nat, M, HHM );
    
    Assert( 4, IsMorphism( epsilon ) );
    SetIsMorphism( epsilon, true );
    
    SetPropertiesIfKernelIsTorsionObject( epsilon );
    
    return epsilon;
    
end );

##
InstallMethod( LeftDualizingFunctor,
        "for homalg graded rings",
        [ IsHomalgGradedRing, IsString ],
        
  function( S, name )
    
    return InsertObjectInMultiFunctor( Functor_GradedHom_ForGradedModules, 2, 1 * S, name );
    
end );

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

##
InstallMethod( RightDualizingFunctor,
        "for homalg graded rings",
        [ IsHomalgGradedRing, IsString ],
        
  function( S, name )
    
    return InsertObjectInMultiFunctor( Functor_GradedHom_ForGradedModules, 2, S * 1, name );
    
end );

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

##
InstallMethod( Dualize,
        "for graded modules or graded submodules",
        [ IsGradedModuleOrGradedSubmoduleRep ],
        
  GradedHom );

##
InstallMethod( Dualize,
        "for graded module maps",
        [ IsMapOfGradedModulesRep ],
        
  GradedHom );

RightDerivedCofunctor( Functor_GradedHom_ForGradedModules );

##
## TensorProduct
##

InstallGlobalFunction( _Functor_TensorProduct_OnGradedModules,		### defines: TensorProduct (object part)
  function( M, N )
    local S, degM, degN, degMN, T, alpha, p;
    
    S := HomalgRing( M );
    
    ## do not use CheckIfTheyLieInTheSameCategory here
    if not IsIdenticalObj( S, HomalgRing( N ) ) then
        Error( "the rings of the source and target modules are not identical\n" );
    fi;
    
    degM := DegreesOfGenerators( M );
    degN := DegreesOfGenerators( N );
    degMN := Concatenation( List( degM, m -> m + degN ) );
    
    T := TensorProduct( UnderlyingModule( M ), UnderlyingModule( N ) );
    
    alpha := NaturalGeneralizedEmbedding( T );
    
    p := PositionOfTheDefaultPresentation( T );
    SetPositionOfTheDefaultPresentation( T, 1 );
    
    alpha := GradedMap( alpha, "create", degMN, S );
    
    Assert( 4, IsMorphism( alpha ) );
    SetIsMorphism( alpha, true );
    
    T := Source( alpha );
    
    SetPositionOfTheDefaultPresentation( T, p );
    
    T!.NaturalGeneralizedEmbedding := alpha;
    
    if HasIsModuleOfGlobalSectionsTruncatedAtCertainDegree( M ) and IsInt( IsModuleOfGlobalSectionsTruncatedAtCertainDegree( M ) ) and
       HasIsModuleOfGlobalSectionsTruncatedAtCertainDegree( N ) and IsInt( IsModuleOfGlobalSectionsTruncatedAtCertainDegree( N ) ) and
       IsModuleOfGlobalSectionsTruncatedAtCertainDegree( M ) = IsModuleOfGlobalSectionsTruncatedAtCertainDegree( N ) then
        SetIsModuleOfGlobalSectionsTruncatedAtCertainDegree( T, IsModuleOfGlobalSectionsTruncatedAtCertainDegree( M ) );
    fi;
    
    return T;
    
end );

##
InstallGlobalFunction( _Functor_TensorProduct_OnGradedMaps,	### defines: TensorProduct (morphism part)
  function( F_source, F_target, arg_before_pos, phi, arg_behind_pos )
    local psi;
    
    if arg_before_pos = [ ] and Length( arg_behind_pos ) = 1 then
        
        psi := GradedMap( TensorProduct( UnderlyingMorphism( phi ), UnderlyingModule( arg_behind_pos[1] ) ), F_source, F_target );
        
    elif Length( arg_before_pos ) = 1 and arg_behind_pos = [ ] then
        
        psi := GradedMap( TensorProduct( UnderlyingModule( arg_before_pos[1] ), UnderlyingMorphism( phi ) ), F_source, F_target );
        
    else
        Error( "wrong input\n" );
    fi;
    
    if HasIsMorphism( phi ) and IsMorphism( phi ) then
        Assert( 4, IsMorphism( psi ) );
        SetIsMorphism( psi, true );
    fi;
    
    return psi;
    
end );

##  <#GAPDoc Label="Functor_TensorProduct:code">
##      <Listing Type="Code"><![CDATA[
InstallValue( Functor_TensorProduct_ForGradedModules,
        CreateHomalgFunctor(
                [ "name", "TensorProduct" ],
                [ "category", HOMALG_GRADED_MODULES.category ],
                [ "operation", "TensorProductOp" ],
                [ "number_of_arguments", 2 ],
                [ "1", [ [ "covariant", "left adjoint", "distinguished" ], HOMALG_GRADED_MODULES.FunctorOn ] ],
                [ "2", [ [ "covariant", "left adjoint" ], HOMALG_GRADED_MODULES.FunctorOn ] ],
                [ "OnObjects", _Functor_TensorProduct_OnGradedModules ],
                [ "OnMorphisms", _Functor_TensorProduct_OnGradedMaps ],
                [ "MorphismConstructor", HOMALG_GRADED_MODULES.category.MorphismConstructor ]
                )
       );
##  ]]></Listing>
##  <#/GAPDoc>
    
Functor_TensorProduct_ForGradedModules!.ContainerForWeakPointersOnComputedBasicObjects := true;

Functor_TensorProduct_ForGradedModules!.ContainerForWeakPointersOnComputedBasicMorphisms := true;

## TensorProduct might have been defined elsewhere
if not IsBound( TensorProduct ) then
    
    DeclareGlobalFunction( "TensorProduct" );
    
    ##
    InstallGlobalFunction( TensorProduct,
      function ( arg )
        local  d;
        if Length( arg ) = 0  then
            Error( "<arg> must be nonempty" );
        elif Length( arg ) = 1 and IsList( arg[1] )  then
            if IsEmpty( arg[1] )  then
                Error( "<arg>[1] must be nonempty" );
            fi;
            arg := arg[1];
        fi;
        d := TensorProductOp( arg, arg[1] );
        if ForAll( arg, HasSize )  then
            if ForAll( arg, IsFinite )  then
                SetSize( d, Product( List( arg, Size ) ) );
            else
                SetSize( d, infinity );
            fi;
        fi;
        return d;
    end );
fi;

##
## BaseChange
##

##
InstallGlobalFunction( _functor_BaseChange_OnGradedModules,		### defines: BaseChange (object part)
  function( _R, M )
    local R, S, N, p;
    
    R := HomalgRing( _R );
    
    if IsIdenticalObj( HomalgRing( M ), R ) then
        TryNextMethod( ); ## i.e. the tensor product with the ring
    fi;
    
    if IsHomalgGradedRingRep( R ) then
    
        N := UnderlyingNonGradedRing( R ) * UnderlyingModule( M );
        
        p := PositionOfTheDefaultPresentation( N );
        SetPositionOfTheDefaultPresentation( N, 1 );
        
        N := GradedModule( N, DegreesOfGenerators( M ), R );
        
        SetPositionOfTheDefaultPresentation( N, p );
        
        return N;
      
    else
    
        return R * UnderlyingModule( M );
    
    fi;
    
end );

##
InstallOtherMethod( BaseChange,
        "for homalg graded maps",
        [ IsHomalgRing, IsMapOfGradedModulesRep ], 1001,
        
  function( R, phi )
    local psi;
    if IsHomalgGradedRingRep( R ) then
      psi := GradedMap( UnderlyingNonGradedRing( R ) * UnderlyingMorphism( phi ), R * Source( phi ), R * Range( phi ) );
    else
      psi := HomalgMap( R * MatrixOfMap( UnderlyingMorphism( phi ) ), R * Source( phi ), R * Range( phi ) );
    fi;
    
    if HasIsMorphism( phi ) and IsMorphism( phi ) then
        Assert( 4, IsMorphism( psi ) );
        SetIsMorphism( psi, true );
    fi;
    
    return psi;
    
end );

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

InstallValue( functor_BaseChange_ForGradedModules,
        CreateHomalgFunctor(
                [ "name", "BaseChange" ],
                [ "category", HOMALG_GRADED_MODULES.category ],
                [ "operation", "BaseChange" ],
                [ "number_of_arguments", 2 ],
                [ "1", [ [ "covariant" ] ] ],
                [ "2", [ [ "covariant" ], HOMALG_GRADED_MODULES.FunctorOn ] ],
                [ "OnObjects", _functor_BaseChange_OnGradedModules ]
                )
        );

functor_BaseChange_ForGradedModules!.ContainerForWeakPointersOnComputedBasicObjects := true;

functor_BaseChange_ForGradedModules!.ContainerForWeakPointersOnComputedBasicMorphisms := true;

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

##
InstallFunctor( functor_Cokernel_ForGradedModules );

##
InstallFunctorOnObjects( functor_ImageObject_ForGradedModules );

##
InstallFunctor( Functor_GradedHom_ForGradedModules );

##
InstallFunctor( Functor_TensorProduct_ForGradedModules );

if not IsOperation( TensorProduct ) then
    
    ## GAP 4.5 style
    ##
    InstallMethod( TensorProductOp,
            "for homalg objects",
            [ IsList, IsStructureObjectOrObjectOrMorphism ],
            
      function( L, M )
        
        return Iterated( L, TensorProductOp );
        
    end );
    
fi;

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

## for convenience
InstallOtherMethod( \*,
        "for homalg modules",
        [ IsGradedModuleRep, 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 );

InstallFunctor( functor_BaseChange_ForGradedModules );

##
## GradedExt
##

##
RightSatelliteOfCofunctor( Functor_GradedHom_ForGradedModules, "GradedExt" );

##
## Hom
##

ComposeFunctors( Functor_HomogeneousPartOfDegreeZeroOverCoefficientsRing_ForGradedModules, 1, Functor_GradedHom_ForGradedModules, "Hom", "Hom" );

SetProcedureToReadjustGenerators(
        Functor_Hom_for_fp_graded_modules,
        function( arg )
          local mor;
          
          mor := CallFuncList( GradedMap, arg );
          
          ## check assertion
          Assert( 3, IsMorphism( mor ) );
          
          SetIsMorphism( mor, true );
          
          return mor;
          
      end );

##
## Ext
##

##
RightSatelliteOfCofunctor( Functor_Hom_for_fp_graded_modules, "Ext" );

##
## Tor
##

##
LeftSatelliteOfFunctor( Functor_TensorProduct_ForGradedModules, "Tor" );
