# GAP Implementation
# $Id: homomorphisms.gi,v 1.40 2012/09/28 12:57:10 sunnyquiver Exp $

#############################################################################
##
#M  ImageElm( <map>, <elm> )  . . . for PathAlgebraMatModuleMap and element
##
InstallMethod( ImageElm, 
    "for a map between representations and an element in a representation.",
    [ IsPathAlgebraMatModuleHomomorphism, IsAlgebraModuleElement ], 0, 
    function( map, elem )

    local elt, n, fam, image, temp, zero, new_image;

    if elem in Source(map) then
        if Dimension(Range(map)) = 0 then
            return Zero(Range(map));
        fi;
  	elt := ExtRepOfObj(elem);
        n := Zero(Range(map));
        image := List([1..Length(elt![1])], x -> elt![1][x]*map!.maps[x]);

        image := PathModuleElem(FamilyObj(Zero(Range(map))![1]),image);        
        return Objectify( TypeObj( n ), [ image ] );
    else
        Error("the element entered is not an element in the source of the map,");
    fi; 
end);

#############################################################################
##
#M  ImagesSet( <map>, <elms> ) . . for a PathAlgebraMatModuleMap and finite collection
##
InstallMethod( ImagesSet, 
    "for a map between representations and a set of elements in a representation.",
    [ IsPathAlgebraMatModuleHomomorphism, IsCollection ], 0, 
    function( map, elms )

    local elt, B, images;

    images := [];
    if IsList(elms) then
        if IsFinite(elms) then
            B := elms;
        fi;
    else
        if IsPathAlgebraMatModule(elms) then 
            B := BasisVectors(CanonicalBasis(elms));
        else
            Error("input of wrong type,");
        fi;
    fi;
    for elt in B do
       if ImageElm(map,elt) <> Zero(Range(map)) then 
          Add(images,ImageElm(map,elt));
       fi;
    od;
    return images;
end
);
#############################################################################
##
#M  PreImagesRepresentative( <map>, <elms> ) . . for a 
##                              PathAlgebraMatModuleMap and finite collection
##
InstallMethod( PreImagesRepresentative, 
    "for a map between representations and an element in a representation.",
    [ IsPathAlgebraMatModuleHomomorphism, IsAlgebraModuleElement ], 0, 
    function( map, elem )

    local elt, m, fam, preimage;

    if elem in Range(map) then
        elt := ExtRepOfObj(elem)![1];
        m := Basis(Source(map))[1];
        preimage := List([1..Length(elt)], x -> SolutionMat(map!.maps[x],elt[x]));
        if ForAll(preimage,x -> x <> fail) then 
           preimage := PathModuleElem(FamilyObj(Zero(Source(map))![1]),preimage);        
           return Objectify( TypeObj( m ), [ preimage ] );
        else
	   return fail;
        fi;
    else
        Error("the element entered is not an element in the range of the map,");
    fi; 
end);

#######################################################################
##
#O  RightModuleHomOverAlgebra( <M>, <N>, <linmaps> )
##
##  This function constructs a homomorphism  f  from the module  <M>  to
##  the module  <N>  from the linear maps given in  <linmaps>. The 
##  function chechs if  <M>  and  <N>  are modules over the same algebra 
##  and checks if the linear maps  <linmaps>  defines a homomorphism from  
##  <M>  to  <N>. The source and the range of  f  can be recovered from 
##  f  via the function  Source(f)  and  Range(f). The linear maps can 
##  be recovered via  f!.maps  or  
##  MatricesOfPathAlgebraMatModuleHomomorphism(f).
##
InstallMethod( RightModuleHomOverAlgebra,
    "for two representations of a quiver",
    [ IsPathAlgebraMatModule, IsPathAlgebraMatModule, IsList ], 0,
    function( M, N, linmaps)

    local A, K, mat_M, mat_N, map, Fam, dim_M, dim_N, Q, arrows, 
        vertices, a, i, origin, target, j;

    A := RightActingAlgebra(M); 
    if A <> RightActingAlgebra(N) then
        Error("the two modules are not over the same algebra, ");
    fi;
    dim_M := DimensionVector(M);
    dim_N := DimensionVector(N);
#
# Checking the number of matrices entered. 
#
    if Length( linmaps ) <> Length(dim_M) then 
        Error("the number of matrices entered is wrong,");
    fi;     
#
# Check the matrices of linmaps
#
    K := LeftActingDomain(A);
    for i in [1..Length(dim_M)] do
        if ( dim_M[i] = 0 ) then 
            if dim_N[i] = 0 then 
	        if linmaps[i] <> NullMat(1,1,K) then 
                     Error("the dimension of matrix number ",i," is wrong (A),");
                fi; 
            else
                if linmaps[i] <> NullMat(1,dim_N[i],K) then
                    Error("the dimension of matrix number ",i," is wrong (B),");
                fi;
            fi;
        else
            if dim_N[i] = 0 then
                if linmaps[i] <> NullMat(dim_M[i],1,K) then 
                    Error("the dimension of matrix number ",i," is wrong (C),");
                fi;
            else 	        
                if DimensionsMat(linmaps[i])[1] <> dim_M[i] or DimensionsMat(linmaps[i])[2] <> dim_N[i] then
                    Error("the dimension of matrix number ",i," is wrong (D),"); 
                fi;
            fi;
        fi; 	 
    od;
# 
# Check commutativity relations with the matrices in M and N.
#
    mat_M := MatricesOfPathAlgebraModule(M);
    mat_N := MatricesOfPathAlgebraModule(N);
    Q := QuiverOfPathAlgebra(A);
    arrows   := ArrowsOfQuiver(Q);
    vertices := VerticesOfQuiver(Q);
    for a in arrows do
        i := Position(arrows,a); 
        origin := Position(vertices,SourceOfPath(a));
        target := Position(vertices,TargetOfPath(a)); 
        if mat_M[i]*linmaps[target] <> linmaps[origin]*mat_N[i] then 
            Error("entered map is not a module map between the representations entered, error for vertex number ",i," and arrow ",a,",");
        fi;
    od;
#
#
    map := Objectify( NewType( CollectionsFamily( GeneralMappingsFamily(
                   ElementsFamily( FamilyObj( M ) ),
                   ElementsFamily( FamilyObj( N ) ) ) ), 
                   IsPathAlgebraMatModuleHomomorphism and IsPathAlgebraMatModuleHomomorphismRep ), rec( maps := linmaps ));
    SetPathAlgebraOfMatModuleMap(map, A);
    SetSource(map, M);
    SetRange(map, N);  
    SetIsWholeFamily(map, true);
    
    return map;
end 
);

#######################################################################
##
#O  MatricesOfPathAlgebraMatModuleHomomorphism( <f> )
##
##  This function returns the matrices defining the homomorphism
##  <f>. 
##
InstallMethod( MatricesOfPathAlgebraMatModuleHomomorphism, 
    "for a PathAlgebraMatModuleHomomorphism",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )
    return f!.maps;  
end
);

#######################################################################
##
#M  ViewObj( <f> )
##
##  This function defines how View prints a 
##  PathAlgebraMatModuleHomomorphism  <f>.
##
InstallMethod( ViewObj, 
    "for a PathAlgebraMatModuleHomomorphism",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ], NICE_FLAGS+1,
    function ( f );

    Print("<");
    View(Source(f));
    Print(" ---> ");
    View(Range(f));
    Print(">");
end
); 

#######################################################################
##
#M  PrintObj( <f> )
##
##  This function defines how Print prints a 
##  PathAlgebraMatModuleHomomorphism  <f>.
##
InstallMethod( PrintObj, 
    "for a PathAlgebraMatModuleHomomorphism",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ], NICE_FLAGS + 1,
    function ( f );

    Print("<",Source(f)," ---> ",Range(f),">");
end
); 

#######################################################################
##
#M  Display( <f> )
##
##  This function defines how Display prints a 
##  PathAlgebraMatModuleHomomorphism  <f>.
##
InstallMethod( Display, 
    "for a PathAlgebraMatModuleHomomorphism",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ], NICE_FLAGS + 1,
    function ( f )
    
    local i;

    Print(f);
    Print("\nwith ");
    for i in [1..Length(DimensionVector(Source(f)))] do
        Print("linear map for vertex number ",i,":\n");
        PrintArray(f!.maps[i]);
    od;
end
); 

#######################################################################
##
#M  Zero( <f> )
##
##  This function returns the zero mapping with the same source and 
##  range as  <f>. 
##
InstallMethod ( Zero, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )
    
    local M, N, K, dim_M, dim_N, i, mats;

    M := Source(f);
    N := Range(f);
    K := LeftActingDomain(Source(f));
    dim_M := DimensionVector(M);
    dim_N := DimensionVector(N);
    mats := [];
    for i in [1..Length(dim_M)] do
        if dim_M[i] = 0 then
            if dim_N[i] = 0 then 
                Add(mats,NullMat(1,1,K));
            else
                Add(mats,NullMat(1,dim_N[i],K));
            fi;
        else
            if dim_N[i] = 0 then 
                Add(mats,NullMat(dim_M[i],1,K));
            else
                Add(mats,NullMat(dim_M[i],dim_N[i],K));
            fi;
        fi;
    od;
  
    return RightModuleHomOverAlgebra(Source(f),Range(f),mats);
end
);

#######################################################################
##
#M  ZeroMapping( <M>, <N> )
##
##  This function returns the zero mapping from the module  <M>  to the 
##  module  <N>. 
##
InstallMethod ( ZeroMapping, 
    " between two PathAlgebraMatModule's ",
    true,
    [ IsPathAlgebraMatModule, IsPathAlgebraMatModule ],
    0,
    function( M, N )
    
    local A, K, dim_M, dim_N, i, mats;

    A := RightActingAlgebra(M);
    if ( A = RightActingAlgebra(N) ) then 
        K := LeftActingDomain(M);
        dim_M := DimensionVector(M);
        dim_N := DimensionVector(N);
        mats  := [];
        for i in [1..Length(dim_M)] do
            if dim_M[i] = 0 then
                if dim_N[i] = 0 then 
                    Add(mats,NullMat(1,1,K));
                else
                    Add(mats,NullMat(1,dim_N[i],K));
                fi;
            else
                if dim_N[i] = 0 then 
                    Add(mats,NullMat(dim_M[i],1,K));
                else
                    Add(mats,NullMat(dim_M[i],dim_N[i],K));
                fi;
            fi;
        od;
  
        return RightModuleHomOverAlgebra(M,N,mats);
    else
        Error("the two modules entered are not modules of one and the same algebra, or they are not modules over the same (quotient of a) path algebra, ");
    fi;
end
);

#######################################################################
##
#M  IdentityMapping( <M> )
##
##  This function returns the identity homomorphism from  <M>  to  <M>.
##
InstallMethod ( IdentityMapping, 
    "for a PathAlgebraMatModule",
    true,
    [ IsPathAlgebraMatModule ],
    0,
    function( M )
    
    local K, dim_M, i, mats;
#
# Representing the identity map from M to M with identity matrices 
# of the right size, including if dim_M[i] = 0, then the identity 
# is represented by a one-by-one identity matrix.
#
    K := LeftActingDomain(M);
    dim_M := DimensionVector(M);
    mats := [];
    for i in [1..Length(dim_M)] do
        if dim_M[i] = 0 then
            Add(mats,NullMat(1,1,K));
        else
            Add(mats,IdentityMat(dim_M[i],K));
        fi;
    od;
    
    return RightModuleHomOverAlgebra(M,M,mats);
end
);

#######################################################################
##
#M  \=( <f>, <g> )
##
##  This function returns true if the homomorphisms  <f>  and  <g>  have
##  the same source, the same range and the matrices defining the 
##  homomorphisms are identitical.
##
InstallMethod ( \=, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f, g )
    
    local a;

    if ( Source(f) = Source(g) ) and ( Range(f) = Range(g) ) 
       and ( f!.maps = g!.maps ) then
        return true;
    else
        return false;
    fi;
end
);

#######################################################################
##
#O  SubRepresentationInclusion( <M>, <gen> )
##
##  This function returns the inclusion from the submodule of  <M>  
##  generated by the elements  <gen>  to the module  <M>. The function
##  checks if all the elements on the list  <gen>  are elements of  the
##  module  <M>. 
##
InstallMethod( SubRepresentationInclusion,
    "for a path algebra module and list of its elements",
    true,
    [IsPathAlgebraMatModule, IsList], 0,
    function( M, gen )

    local A, q, K, num_vert, basis_M, vertices, arrows_as_path, arrows_of_quiver, 
          newgen, g, v, submodspan, temp, new, V_list, m, V, basis_submod, submod_list, 
          dim_vect_sub, i, cnt, j, dim_size, s, t, big_mat, a, mat, dom_a, im_a, pd, 
          pi, arrow, submodule, dim_vect_M, dim_size_M, inclusion, map;

    if not ForAll(gen, g -> g in M) then
        Error("entered elements are not in the module <M>, ");
    fi;

    A := RightActingAlgebra(M);
    q := QuiverOfPathAlgebra(A);
    K := LeftActingDomain(M);
    num_vert := Length(VerticesOfQuiver(q));
    basis_M := Basis(M);
#
# vertices, vertices as elements of algebra
# arrows_as_path, arrows as elements of algebra
# arrows, as arrows in the quiver
#
    if Length(gen) = 0 then 
        return ZeroMapping(ZeroModule(A),M);
    else
        vertices := List(VerticesOfQuiver(q), x -> x*One(A));
        arrows_as_path := List(ArrowsOfQuiver(q), x -> x*One(A));
        arrows_of_quiver := GeneratorsOfQuiver(q){[1+num_vert..num_vert+Length(ArrowsOfQuiver(q))]};
#
# Ensuring uniform generators
#
        newgen := [];
        for g in gen do 
            for v in vertices do 
                if g^v <> Zero(M) then 
                    Add(newgen,g^v);
                fi;
            od;
        od;
#
# Finding the linear span of the submodule generated by gen in M
#  
        submodspan := [];
        temp := [];
        new := newgen;
        while new <> [] do
            for m in new do
                for a in arrows_as_path do
                    if m^a <> Zero(M) then
                        Add(temp,m^a);            
                    fi;
                od;
            od;
            Append(submodspan,new);
            new := temp;
            temp := [];
        od;
        V_list := List(submodspan, x -> Coefficients(basis_M,x));
        V := VectorSpace(K,V_list);
        basis_submod := CanonicalBasis(V);
#
# Converting elements in basis_submod to a list of elements in M 
#
        submod_list := List(basis_submod, x -> LinearCombination(basis_M,x));
#
# Finding the dimension vector of the submodule of M
#
        dim_vect_sub:=[];
        for i in [1.. num_vert] do
            cnt := 0;
            for j in [1..Length(submod_list)] do
                if submod_list[j]^vertices[i] <> Zero(M) then
                    cnt:=cnt+1;   
                fi;
            od;
            Add(dim_vect_sub,cnt);    
        od;
        if Dimension(V) <> Sum(dim_vect_sub) then
            Error("Bug alert: Something is wrong in this code! Report this.\n");
        fi;
#    
# CanonicalBasis(V) gives the basis in "upper triangular form", so that 
# first comes the basis vectors for the vector space in vertex 1, in 
# vertex 2, ...., in vertex n. Next we find the intervals of the basis
# vector [[?..?],[?..?],...,[?..?]]. Is no basis vectors for a vertex, [] is entered.
#
        dim_size := [];
        s := 1;
        t := 0;
        for i in [1..Length(vertices)] do
            t := dim_vect_sub[i] + t;
            if t < s then 
                Add(dim_size,[]);
            else
                Add(dim_size,[s..t]);
            fi;
            s := t + 1;
        od;  
#
# Finding the submodule as a representation of the quiver
#  
        big_mat := [];
        for a in arrows_as_path do
            mat := [];
            for v in vertices do
                if v*a <> Zero(A) then
                    dom_a := v;
                fi;
            od;
            for v in vertices do
                if a*v <> Zero(A) then
                    im_a := v;
                fi;
            od; 
            
            pd := Position(vertices,dom_a);
            pi := Position(vertices,im_a);
            
            arrow := arrows_of_quiver[Position(arrows_as_path,a)];
            if ( dim_vect_sub[pd] = 0 ) or ( dim_vect_sub[pi] = 0 ) then 
                mat := [dim_vect_sub[pd],dim_vect_sub[pi]];
            else 
                for m in submod_list{dim_size[pd]} do
                    Add(mat,Coefficients(basis_submod,Coefficients(basis_M,m^a)){dim_size[pi]}); 
                od;
            fi;
            Add(big_mat,[arrow,mat]);
        od;
        
        if IsPathAlgebra(A) then 
            submodule := RightModuleOverPathAlgebra(A,big_mat);
        else
            submodule := RightModuleOverPathAlgebra(A,big_mat); 
        fi;      

#
# Finding inclusion map of submodule into M
#
        mat := [];
        for i in [1..Length(basis_submod)] do
            Add(mat,basis_submod[i]);
        od;
        
        dim_vect_M := DimensionVector(M);
        dim_size_M := [];
        s := 1;
        t := 0;
        for i in [1..Length(vertices)] do
            t := dim_vect_M[i] + t;
            if t < s then 
                Add(dim_size_M,[]);
            else
                Add(dim_size_M,[s..t]);
            fi;
            s := t + 1;
        od;
        
        inclusion := [];
        for i in [1..Length(vertices)] do
            if dim_vect_sub[i] = 0 then
                if dim_vect_M[i] = 0 then 
                    Add(inclusion,NullMat(1,1,K));
                else
                    Add(inclusion,NullMat(1,dim_vect_M[i],K)); 
                fi;
            else 
                Add(inclusion,mat{dim_size[i]}{dim_size_M[i]});
            fi;
        od;
        return RightModuleHomOverAlgebra(submodule,M,inclusion);
    fi;
end
);

#######################################################################
##
#O  SubRepresentation( <M>, <gen> )
##
##  This function returns a module isomorphic to the submodule of  <M>  
##  generated by the elements  <gen>  to the module  <M>. The function
##  checks if all the elements on the list  <gen>  are elements of  the
##  module  <M>. 
##
InstallMethod( SubRepresentation,
    "for a path algebra module and list of its elements",
    true,
    [ IsPathAlgebraMatModule, IsList], 0,
    function( M, gen );

    return Source(SubRepresentationInclusion(M,gen));
end
);

#######################################################################
##
#O  RadicalOfModuleInclusion( <M> )
##
##  This function returns the inclusion from the radical of  <M>  
##  to the module  <M>. 
##
InstallMethod( RadicalOfModuleInclusion,
    "for a path algebra module",
    true,
    [ IsPathAlgebraMatModule ], 0,
    function( M )

    local A, q, num_vert, arrows_as_path, basis_M, generators, a, b, run_time; 
    
    #
    # Checking if the algebra  <A>  is finite dimensional.  If it is and is 
    # a path algebra, then the radical is the ideal generated by the arrows.  
    # If it is not a path algebra but an admissible quotient of a path algebra,
    # the radical is the ideal generated by the arrows, and this function 
    # applies. 
    #
    A := RightActingAlgebra(M);
    q := QuiverOfPathAlgebra(A);
    if not IsFiniteDimensional(A) then
        TryNextMethod();
    fi; 
    if not IsPathAlgebra(A) and not IsAdmissibleQuotientOfPathAlgebra(A) then
        TryNextMethod();
    fi;

    num_vert := Length(VerticesOfQuiver(q));
#
# Note arrows_as_path will change if A is a quotient of a path algebra !!!!
#
    arrows_as_path := List(ArrowsOfQuiver(q), x -> x*One(A));
    basis_M := Basis(M);
    generators := [];
    for a in arrows_as_path do
        for b in basis_M do 
            if b^a <> Zero(M) then 
                Add(generators,b^a);
            fi;
        od;
    od;
    
    generators := Unique(generators);
    return SubRepresentationInclusion(M,generators);
end
);

#######################################################################
##
#O  RadicalOfModule( <M> )
##
##  This function returns a module isomorphic to the radical of  <M>. 
##
InstallMethod( RadicalOfModule,
    "for a path algebra module",
    true,
    [ IsPathAlgebraMatModule ], 0,
    function( M )

    return Source(RadicalOfModuleInclusion(M));
end
);

#######################################################################
##
#M  IsInjective( <f> )
##
##  This function returns a module isomorphic to the radical of  <M>. 
##
InstallOtherMethod ( IsInjective, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )
    
    local M, K, V_list, dim_K, dim_M, i, VS_list;

    M := Source(f);
    K := LeftActingDomain(M);
    V_list := [];
    dim_K := 0;
    dim_M := DimensionVector(M);
#
# Computing the kernel of each f_i and making a vector space of Ker f_i with 
# basis given by the vectors supplied by NullspaceMat(f!.maps[i]).
#
    for i in [1..Length(dim_M)] do
        if dim_M[i] <> 0 then 
            dim_K := dim_K + Length(NullspaceMat(f!.maps[i]));
        fi;
    od;
    if dim_K = 0 then
        SetIsInjective(f,true);
        return true;
    else
        SetIsInjective(f,false);
        return false;
    fi;
end
);

#######################################################################
##
#O  KernelInclusion( <f> )
##
##  This function returns the inclusion from a module isomorphic to the 
##  kernel of  <f>  to the module  <M>. 
##
InstallMethod ( KernelInclusion, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )
    
    local M, dim_M, V_list, V_dim, i, dim_K, VS_list, A, K, vertices, 
            arrows, mats, kermats, a, apos, j, matrix, k, Kerf, kermap;

    M := Source(f);
    K := LeftActingDomain(M);
    V_list := [];
    dim_K  := [];
    dim_M := DimensionVector(M);
    VS_list := [];
#
# Computing the kernel of each f_i and making a vector space of Ker f_i with 
# basis given by the vectors supplied by NullspaceMat(f!.maps[i]).
#
    for i in [1..Length(dim_M)] do
        if dim_M[i] <> 0 then
            Add(V_list,NullspaceMat(f!.maps[i]));
            Add(dim_K,Length(V_list[i]));
            if Length(V_list[i]) > 0 then 
                Add(VS_list,VectorSpace(K,V_list[i],"basis"));
            else
                Add(VS_list,TrivialSubmodule(VectorSpace(K,[[One(K)]])));
            fi;
        else
            Add(V_list,[]);
            Add(dim_K,0);
            Add(VS_list,TrivialSubmodule(VectorSpace(K,[[One(K)]])));
        fi;
    od;
    V_list := List(VS_list, V -> Basis(V));
    V_dim := Sum(dim_K);
    A := RightActingAlgebra(M);
    if V_dim = 0 then 
        SetIsInjective(f,true);
        kermap := ZeroMapping(ZeroModule(A),Source(f)); 
    else 
        vertices := VerticesOfQuiver(QuiverOfPathAlgebra(A));
        arrows := ArrowsOfQuiver(QuiverOfPathAlgebra(A));
        mats := MatricesOfPathAlgebraModule(M);
        kermats := [];
#
# The matrices f_alpha of the representation M is used to compute
# the induced action on the basis of Ker f_i.
#
        for a in arrows do
            apos := Position(arrows,a);
            i := Position(vertices,SourceOfPath(a));
            j := Position(vertices,TargetOfPath(a));
            matrix := [];
            if ( dim_K[i] = 0 ) or ( dim_K[j] = 0 ) then 
                matrix := [dim_K[i],dim_K[j]];
            else 
                for k in [1..Length(V_list[i])] do
                    Add(matrix,Coefficients(V_list[j],V_list[i][k]*mats[apos]));
                od;
            fi;
            Add(kermats,[a,matrix]);
        od;
#
# Extracting the basis vectors of each Ker f_i, as a subspace of M_i,
# which give the matrices of the inclusion of Ker f  into M.
#
        for i in [1..Length(dim_M)] do
            if Dimension(VS_list[i]) = 0 then
                V_list[i] := []; 
                if dim_M[i] = 0 then 
                    Add(V_list[i],Zero(K));
                else 
                    for j in [1..dim_M[i]] do
                        Add(V_list[i],Zero(K));
                    od;
                fi;
                V_list[i] := [V_list[i]];
            else 
                V_list[i] := BasisVectors(V_list[i]);
            fi;
        od;
        if IsPathAlgebra(A) then 
            Kerf := RightModuleOverPathAlgebra(A,kermats);
        else 
            Kerf := RightModuleOverPathAlgebra(A,kermats);
        fi;
        kermap := RightModuleHomOverAlgebra(Kerf,M,V_list);
    fi;  
    SetKernelOfWhat(kermap,f);
    SetIsInjective(kermap,true);
    return kermap;
end
);

#######################################################################
##
#M  KernelOfAdditiveGeneralMapping( <f> )
##
##  This function returns a module isomorphic to the kernel of  <f>.
##
InstallMethod ( KernelOfAdditiveGeneralMapping, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )

    return Source(KernelInclusion(f));
end
);

#######################################################################
##
#O  ImageProjectionInclusion( <f> )
##
##  This function returns a projection from the source of  <f>  to a 
##  module isomorphic to the image of  <f>  and an inclusion from a 
##  module isomorphic to the image of  <f>  to the module  <M>. 
##
InstallMethod ( ImageProjectionInclusion, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )
    
    local M, N, image, images, A, K, num_vert, vertices, arrows, gen_list, B, i,fam, n, s, v,
          pos, dim_M, dim_N, V, W, projection, dim_image, basis_image, basis_M, basis_N, a, b, image_mat, 
          mat, mats, source_a, target_a, pos_a, bb, image_f, C, map, partmap,
          inclusion, image_inclusion, image_projection;

    M := Source(f);
    N := Range(f);
    A := RightActingAlgebra(M);
    K := LeftActingDomain(M); 
    num_vert := Length(VerticesOfQuiver(QuiverOfPathAlgebra(A)));
#
    vertices := List(VerticesOfQuiver(QuiverOfPathAlgebra(A)), x -> x*One(A));
#
# Finding a basis for the vector space in each vertex for the image
#
    image := ImagesSet(f,Source(f));
    gen_list := [];
    for i in [1..Length(vertices)] do
        Add(gen_list,[]);
    od;
    if Length(image) > 0 then 
        for n in image do
            for v in vertices do
                if n^v <> Zero(N) then
                    pos := Position(vertices,v);
                    Add(gen_list[pos],ExtRepOfObj(n)![1][pos]);
                fi;
            od;
        od;
        dim_N := DimensionVector(N);
        V := [];    
        W := [];
        basis_image := [];   
        basis_N := [];   
        for i in [1..Length(vertices)] do
            V[i] := K^dim_N[i];
            basis_N[i] := CanonicalBasis(V[i]);
            W[i] := Subspace(V[i],gen_list[i]);
            Add(basis_image,CanonicalBasis(W[i]));
        od;
#
# Finding the matrices of the representation image
#
        arrows := ArrowsOfQuiver(QuiverOfPathAlgebra(A));
        mats := MatricesOfPathAlgebraModule(N);
        image_mat := [];
        for a in arrows do 
            mat := [];
            pos_a := Position(arrows,a);
            source_a := Position(vertices,SourceOfPath(a)*One(A));
            target_a := Position(vertices,TargetOfPath(a)*One(A));
            if ( Length(basis_image[source_a]) = 0 ) or ( Length(basis_image[target_a]) = 0 ) then 
                mat := [Length(basis_image[source_a]),Length(basis_image[target_a])];
            else 
                for b in basis_image[source_a] do
                    Add(mat,Coefficients(basis_image[target_a],b*mats[pos_a]));
                od;
            fi;
            Add(image_mat,[a,mat]);
        od;
        if IsPathAlgebra(A) then 
            image_f := RightModuleOverPathAlgebra(A,image_mat);
        else
            image_f := RightModuleOverPathAlgebra(A,image_mat);
        fi;
#
# Finding inclusion map from the image to Range(f)
#     
        inclusion := [];
        for i in [1..num_vert] do 
            mat := [];
            if Length(basis_image[i]) = 0 then 
                if dim_N[i] = 0 then 
                    Add(inclusion,NullMat(1,1,K));
                else
                    Add(inclusion,NullMat(1,dim_N[i],K));
                fi;
            else
                mat := [];
                for b in basis_image[i] do 
                    Add(mat,b);
                od;
                Add(inclusion,mat);
            fi;
        od; 
        image_inclusion := RightModuleHomOverAlgebra(image_f,Range(f),inclusion);
        SetImageOfWhat(image_inclusion,f);
        SetIsInjective(image_inclusion,true);
#
# Finding the projection for Source(f) to the image
#
        dim_M := DimensionVector(M);
        basis_M := [];
        for i in [1..num_vert] do
            Add(basis_M,CanonicalBasis(K^dim_M[i]));
        od;
        projection := [];
        for i in [1..num_vert] do
            mat := [];
            if Length(basis_image[i]) = 0 then
                if dim_M[i] = 0 then  
                    mat := NullMat(1,1,K);
                else
                    mat := NullMat(dim_M[i],1,K);
                fi;
                Add(projection,mat);
            else 
                for b in basis_M[i] do
                    Add(mat,Coefficients(basis_image[i],b*f!.maps[i]));
                od;
                Add(projection,mat);
            fi;
        od;
        image_projection := RightModuleHomOverAlgebra(Source(f),image_f,projection);
        SetImageOfWhat(image_projection,f);
        SetIsSurjective(image_projection,true);
        
        return [image_projection,image_inclusion];
    else
        return [ZeroMapping(M,ZeroModule(A)),ZeroMapping(ZeroModule(A),N)];
    fi;
end
);

#######################################################################
##
#O  ImageProjection( <f> )
##
##  This function returns a projection from the source of  <f>  to a 
##  module isomorphic to the image of  <f>. 
##
InstallMethod ( ImageProjection, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f );

    return ImageProjectionInclusion(f)[1];
end
);

#######################################################################
##
#O  ImageInclusion( <f> )
##
##  This function returns an inclusion from a module isomorphic to the 
##  image of  <f>  to the module  <M>. 
##
InstallMethod ( ImageInclusion, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f );

    return ImageProjectionInclusion(f)[2];
end
);

#######################################################################
##
#M  ImagesSource( <f> )
##
##  This function returns a module isomorphic to the image of  <f>. 
##  TODO: Should this delegate to ImagesSet as for the general GAP
##  command?
##
InstallMethod ( ImagesSource, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f );

    return Range(ImageProjectionInclusion(f)[1]);
end
);

#######################################################################
##
#M  IsZero( <f> )
##
##  This function returns true if all the matrices of the homomorphism
##  <f>  are identically zero. 
##
InstallMethod ( IsZero, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f );

    return ForAll(f!.maps, IsZero);
end
);

#######################################################################
##
#M  IsSurjective( <f> )
##
##  This function returns true if the homomorphism  <f>  is surjective. 
##
InstallOtherMethod ( IsSurjective, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )
    
    local image, dim_image, K, V;

    image := ImagesSet(f,Source(f));
    if Length(image) = 0 then 
        dim_image := 0;
    else
        image := List(image, x -> Flat(ExtRepOfObj(x)![1])); 
        K := LeftActingDomain(Source(f));
        V := K^Length(image[1]);
        dim_image :=  Dimension(Subspace(V,image));
    fi;
    if dim_image = Dimension(Range(f)) then 
        SetIsSurjective(f,true);
        return true;
    else
        SetIsSurjective(f,false);
        return false;
    fi;
end
);

#######################################################################
##
#M  IsIsomorphism( <f> )
##
##  This function returns true if the homomorphism  <f>  is an 
##  isomorphism. 
##
InstallMethod ( IsIsomorphism, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f );

    return IsInjective(f) and IsSurjective(f);
end
);

#######################################################################
##
#O  CoKernelProjection( <f> )
##
##  This function returns a projection from the range of  <f>  to a 
##  module isomorphic to the cokernel of  <f>. 
##
InstallMethod ( CoKernelProjection, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )
    
    local M, N, image, A, K, num_vert, vertices, arrows, basis_list,i, n, v,
          pos, dim_N, V, W, projection, coker, basis_coker, basis_N, a, b,
          mat, mats, source_a, target_a, pos_a, bb, cokermat, C, map, partmap,
          morph;

    M := Source(f);
    N := Range(f);
    A := RightActingAlgebra(M);
    K := LeftActingDomain(M); 
    num_vert := Length(VerticesOfQuiver(QuiverOfPathAlgebra(A)));
#
    vertices := List(VerticesOfQuiver(QuiverOfPathAlgebra(A)), x -> x*One(A));
#
# Finding a basis for the vector space in each vertex for the cokernel
#
    image := ImagesSet(f,Source(f));
    basis_list := [];
    for i in [1..Length(vertices)] do
        Add(basis_list,[]);
    od;
    for n in image do
        for v in vertices do
            if n^v <> Zero(N) then
                pos := Position(vertices,v);
                Add(basis_list[pos],ExtRepOfObj(n)![1][pos]);
            fi;
        od;
    od;
    dim_N := DimensionVector(N);
    V := [];    
    W := [];
    projection := [];
    basis_coker := [];   
    basis_N := [];   
    coker := [];
    for i in [1..Length(vertices)] do
        V[i] := K^dim_N[i];
        basis_N[i] := CanonicalBasis(V[i]);
        W[i] := Subspace(V[i],basis_list[i]);
        projection[i] := NaturalHomomorphismBySubspace(V[i],W[i]);
        Add(basis_coker,Basis(Range(projection[i])));
        coker[i] := Range(projection[i]);
    od;
#
# Finding the matrices of the representation of the cokernel 
#
    mats := MatricesOfPathAlgebraModule(N);
    arrows := ArrowsOfQuiver(QuiverOfPathAlgebra(A));
    cokermat := [];
    for a in arrows do 
        mat := [];
        source_a := Position(vertices,SourceOfPath(a)*One(A));
        target_a := Position(vertices,TargetOfPath(a)*One(A));
        pos_a := Position(arrows,a);
        if ( Length(basis_coker[source_a]) = 0 ) or ( Length(basis_coker[target_a]) = 0 ) then
            mat := [Length(basis_coker[source_a]),Length(basis_coker[target_a])];
        else  
            for b in basis_coker[source_a] do
                bb := PreImagesRepresentative(projection[source_a],b);
                bb := bb*mats[pos_a]; # computing bb^a
                Add(mat,Coefficients(basis_coker[target_a],Image(projection[target_a],bb)));
            od;
        fi;
        Add(cokermat,[a,mat]);
    od;
    
    if IsPathAlgebra(A) then 
        C := RightModuleOverPathAlgebra(A,cokermat);
    else
        C := RightModuleOverPathAlgebra(A,cokermat);
    fi;
#
# Finding the map for Range(f) to the cokernel 
#
    map := [];
    for i in [1..Length(vertices)] do
        partmap := [];
        if dim_N[i] = 0 then
            partmap := NullMat(1,1,K);
        elif Length(basis_coker[i]) = 0 then 
            partmap := NullMat(dim_N[i],1,K);
        else 
            for b in basis_N[i] do 
                Add(partmap,Coefficients(basis_coker[i],Image(projection[i],b)));
            od;
        fi;
        Add(map,partmap);
    od;
    
    morph := RightModuleHomOverAlgebra(Range(f),C,map);
    SetCoKernelOfWhat(morph,f);
    SetIsSurjective(morph,true);
    
    return morph;
end
);

#######################################################################
##
#O  CoKernelOfAdditiveGeneralMapping( <f> )
##
##  This function returns a module isomorphic to the cokernel of  <f>. 
##
InstallMethod ( CoKernelOfAdditiveGeneralMapping, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    SUM_FLAGS+1,
    function( f )

    return Range(CoKernelProjection(f));
end
);

#######################################################################
##
#A  TopOfModuleProjection( <M> )
##
##  This function computes the map from  M  to the top of  M, 
##  M ---> M/rad(M).
##
InstallMethod( TopOfModuleProjection, 
    "for a pathalgebramatmodule",
    true, 
    [ IsPathAlgebraMatModule ], 0,
    function( M ) 

    local K, A, Q, vertices, num_vert, incomingarrows, mats, arrows, subspaces,
          i, a, dim_M, Vspaces, Wspaces, naturalprojections, index,
          dim_top, matrices, topofmodule, topofmoduleprojection, W;

    A := RightActingAlgebra(M);
    if Dimension(M) = 0 then 
        return ZeroMapping(M,M);
    else
        K := LeftActingDomain(A);
        Q := QuiverOfPathAlgebra(A);
        vertices := VerticesOfQuiver(Q);
        num_vert := Length(vertices);
        incomingarrows := List([1..num_vert], x -> IncomingArrowsOfVertex(vertices[x]));
        mats := MatricesOfPathAlgebraModule(M);
        arrows := ArrowsOfQuiver(Q);
        subspaces := List([1..num_vert], x -> []);
        for i in [1..num_vert] do
            for a in incomingarrows[i] do
                Append(subspaces[i], StructuralCopy(mats[Position(arrows,a)]));
            od;
        od;
        dim_M := DimensionVector(M);
        Vspaces := List([1..num_vert], x -> FullRowSpace(K,dim_M[x]));
        Wspaces := List([1..num_vert], x -> []);
        for i in [1..num_vert] do
            if dim_M[i] <> 0 then 
                Wspaces[i] := Subspace(Vspaces[i],subspaces[i]);
            else
                Wspaces[i] := Subspace(Vspaces[i],[]);
            fi;
        od;
        naturalprojections := List([1..num_vert], x -> NaturalHomomorphismBySubspace(Vspaces[x],Wspaces[x]));
        dim_top := List([1..num_vert], x -> Dimension(Range(naturalprojections[x]))); 
        index := function( n )
            if n = 0 then 
                return 1;
            else
                return n;
            fi;
        end;
        matrices := [];
        for i in [1..num_vert] do
            if dim_top[i] <> 0 then
                Add(matrices, List(BasisVectors(Basis(Vspaces[i])), y -> ImageElm(naturalprojections[i],y)));
            else
                Add(matrices, NullMat(index(dim_M[i]),1,K));
            fi;
        od;
        topofmodule := RightModuleOverPathAlgebra(A,dim_top,[]);
        topofmoduleprojection := RightModuleHomOverAlgebra(M,topofmodule,matrices);

        SetTopOfModule(M,topofmodule);

        return topofmoduleprojection;
    fi;
end
);

#######################################################################
##
#A  TopOfModule( <M> )
##
##  This function computes the top  M/rad(M)  of the module M. 
##
InstallMethod( TopOfModule, 
    "for a pathalgebramatmodule",
    true, 
    [ IsPathAlgebraMatModule ], 0,
    function( M )

    return Range(TopOfModuleProjection(M));
end
);

#######################################################################
##
#M  \+( <f>, <g> )
##
##  This function returns the sum of two homomorphisms  <f>  and  <g>,
##  when the sum is defined, otherwise it returns an error message. 
##
InstallMethod( \+,
    "for two PathAlgebraMatModuleMap's",
    true,
#  IsIdenticalObj,
    [ IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f, g )
    
    local i, num_vert, x, Fam;

    if ( Source(f) = Source(g) ) and ( Range(f) = Range(g) ) then 
     	num_vert := Length(f!.maps);
        x := List([1..num_vert], y -> f!.maps[y] + g!.maps[y]);
        return RightModuleHomOverAlgebra(Source(f),Range(f),x);
    else
	Error("the two arguments entered do not live in the same homomorphism set, ");
    fi;
end
);
  
#######################################################################
##
#M  \*( <f>, <g> )
##
##  This function returns the composition  <f*g>  of two homomorphisms  
##  <f>  and  <g>, that is, first the map  <f> then followed by  <g>,  
##  when the composition is defined, otherwise it returns an error 
##  message. 
##
InstallMethod( \*,
    "for two PathAlgebraMatModuleMap's",
    true,
    [ IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f, g )
    
    local i, num_vert, x;

    if Range(f) = Source(g) then 
     	num_vert := Length(f!.maps);
        x := List([1..num_vert], y -> f!.maps[y]*g!.maps[y]);
	return RightModuleHomOverAlgebra(Source(f),Range(g),x);
    else
        Error("codomain of the first argument is not equal to the domain of the second argument, ");
    fi;
end
);
  
#######################################################################
##
#M  \*( <a>, <g> )
##
##  This function returns the scalar multiple  <a*g>  of a scalar  <a> 
##  with a homomorphism <g>, when this scalar multiplication is defined, 
##  otherwise it returns an error message. 
##
InstallOtherMethod( \*,
    "for two PathAlgebraMatModuleMap's",
    true,
    [ IsScalar, IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( a, g )
    
    local K, i, num_vert, x;

    K := LeftActingDomain(Source(g));
    if a in K then 
     	num_vert := Length(g!.maps);
        x := List([1..num_vert], y -> a*g!.maps[y]);
        return RightModuleHomOverAlgebra(Source(g),Range(g),x);
     else
         Error("the scalar is not in the same field as the algbra is over,");
     fi;
end
);
  
#######################################################################
##
#M  AdditiveInverseOp( <f> )
##
##  This function returns the additive inverse of the homomorphism 
##  <f>. 
##
InstallMethod( AdditiveInverseOp,
    "for a morphism in IsPathAlgebraMatModuleHomomorphism",
    [ IsPathAlgebraMatModuleHomomorphism ],

    function ( f ) 

    local i, num_vert, x;

    num_vert := Length(f!.maps);
    x := List([1..num_vert], y -> (-1)*f!.maps[y]);

    return RightModuleHomOverAlgebra(Source(f),Range(f),x);
end
);

#######################################################################
##
#M  \*( <f>, <a> )
##
##  This function returns the scalar multiple  <f*a>  of a homomorphism
##  <f>  and a scalar  <a>. 
##
InstallOtherMethod( \*,
    "for two PathAlgebraMatModuleMap's",
    true,
    [ IsPathAlgebraMatModuleHomomorphism, IsScalar ],
    0,
    function( f, a )
    
    local K, i, num_vert, x;

    K := LeftActingDomain(Source(f));
    if a in K then 
     	num_vert := Length(f!.maps);
        x := List([1..num_vert], y -> f!.maps[y]*a);
	return RightModuleHomOverAlgebra(Source(f),Range(f),x);
    else
 	Error("the scalar is not in the same field as the algbra is over,");
    fi;
end
);
  
#######################################################################
##
#O  HomOverAlgebra( <M>, <N> )
##
##  This function computes a basis of the vector space of homomorphisms
##  from the module  <M>  to the module  <N>. The algorithm it uses is
##  based purely on linear algebra.
##
InstallMethod( HomOverAlgebra,
    "for two representations of a quiver",
    [ IsPathAlgebraMatModule, IsPathAlgebraMatModule ], 0,
    function( M, N )

    local A, F, dim_M, dim_N, num_vert, support_M, support_N, num_rows, num_cols, 
          block_rows, block_cols, block_intervals, 
          i, j, equations, arrows, vertices, v, a, source_arrow, target_arrow, 
          mats_M, mats_N, prev_col, prev_row, row_start_pos, col_start_pos, 
          row_end_pos, col_end_pos, l, m, n, hom_basis, map, mat, homs, x, y, k, b, 
          dim_hom, zero;

    A := RightActingAlgebra(M); 
    if A <> RightActingAlgebra(N) then
        Print("The two modules entered are not modules over the same algebra.");
        return fail;
    fi;
    F := LeftActingDomain(A);
    #
    # Finding the support of M and N
    # 
    vertices := VerticesOfQuiver(QuiverOfPathAlgebra(OriginalPathAlgebra(A)));
    dim_M := DimensionVector(M);
    dim_N := DimensionVector(N);
    num_vert := Length(dim_M);   
    support_M := [];
    support_N := [];
    for i in [1..num_vert] do
        if (dim_M[i] <> 0) then 
            AddSet(support_M,i);
        fi;
        if (dim_N[i] <> 0) then 
            AddSet(support_N,i);
        fi;
    od;
    #
    # Deciding the size of the equations, 
    # number of columns and rows
    #
    num_cols := 0;
    num_rows := 0;
    block_intervals := [];
    block_rows := [];
    block_cols := [];
    prev_col := 0;
    prev_row := 0;
    for i in support_M do
        num_rows := num_rows + dim_M[i]*dim_N[i];
        block_rows[i] := prev_row+1;
        prev_row := num_rows;
        for a in OutgoingArrowsOfVertex(vertices[i]) do
            source_arrow := Position(vertices,SourceOfPath(a));
            target_arrow := Position(vertices,TargetOfPath(a));
            if (target_arrow in support_N) and ( (source_arrow in support_N) or (target_arrow in support_M)) then 
                num_cols := num_cols + dim_M[source_arrow]*dim_N[target_arrow];
                Add(block_cols,[a,prev_col+1,num_cols]);
            fi;
            prev_col := num_cols; 
        od;
    od;
    #
    # Finding the linear equations for the maps between M and N
    #
    equations := MutableNullMat(num_rows, num_cols, F);

    arrows := ArrowsOfQuiver(QuiverOfPathAlgebra(OriginalPathAlgebra(A)));
    mats_M := MatricesOfPathAlgebraModule(M);
    mats_N := MatricesOfPathAlgebraModule(N);
    prev_col := 0;
    prev_row := 0;
    for i in support_M do
        for a in OutgoingArrowsOfVertex(vertices[i]) do
            source_arrow := Position(vertices,SourceOfPath(a));
            target_arrow := Position(vertices,TargetOfPath(a));
            if (target_arrow in support_N) and ( (source_arrow in support_N) or (target_arrow in support_M)) then
                for j in [1..dim_M[source_arrow]] do
                    row_start_pos := block_rows[source_arrow] + (j-1)*dim_N[source_arrow]; 
                    row_end_pos := block_rows[source_arrow] - 1 + j*dim_N[source_arrow];
                    col_start_pos := prev_col + 1 + (j-1)*dim_N[target_arrow];
                    col_end_pos := prev_col + j*dim_N[target_arrow];
                    if (source_arrow in support_N) then 
                        equations{[row_start_pos..row_end_pos]}{[col_start_pos..col_end_pos]} := mats_N[Position(arrows,a)];
                    fi;
                    if (target_arrow in support_M) then 
                        for m in [1..DimensionsMat(mats_M[Position(arrows,a)])[2]] do
                            for n in [1..dim_N[target_arrow]] do
                                b := block_rows[target_arrow]+(m-1)*dim_N[target_arrow];
                                equations[b+n-1][col_start_pos+n-1] := equations[b+n-1][col_start_pos+n-1]+(-1)*mats_M[Position(arrows,a)][j][m];
                            od;
                        od;
                    fi;
                od;
                prev_col := prev_col + dim_M[source_arrow]*dim_N[target_arrow];
            fi;
        od;
    od;
    #
    # Creating the maps between the module M and N
    #
    homs := [];
    if (num_rows <> 0) and (num_cols <> 0) then 
        dim_hom := 0; 
        hom_basis := NullspaceMat(equations);
        for b in hom_basis do
            map := [];
            dim_hom := dim_hom + 1;
            k := 1;
            for i in [1..num_vert] do 
                if dim_M[i] = 0 then 
                    if dim_N[i] = 0 then 
                        Add(map,NullMat(1,1,F));
                    else
                        Add(map,NullMat(1,dim_N[i],F));
                    fi;
                else
                    if dim_N[i] = 0 then 
                        Add(map,NullMat(dim_M[i],1,F));
                    else
                        mat := MutableNullMat(dim_M[i],dim_N[i], F);
                        for y in [1..dim_M[i]] do 
                            for x in [1..dim_N[i]] do 
                                mat[y][x] := b[k];
                                k := k + 1;
                            od;
                        od;
                        Add(map,mat);
                    fi;
                fi;
            od;
            homs[dim_hom] := Objectify( NewType( CollectionsFamily( GeneralMappingsFamily(
                                     ElementsFamily( FamilyObj( M ) ),
                                     ElementsFamily( FamilyObj( N ) ) ) ), 
                                     IsPathAlgebraMatModuleHomomorphism and IsPathAlgebraMatModuleHomomorphismRep and IsAttributeStoringRep ), rec( maps := map ));
            SetPathAlgebraOfMatModuleMap(homs[dim_hom], A);
            SetSource(homs[dim_hom], M);
            SetRange(homs[dim_hom], N);
            SetIsWholeFamily(homs[dim_hom],true);
        od;
        return homs;
    else
        homs := [];
        if Dimension(M) = 0 or Dimension(N) = 0 then 
            return homs;
        else 
            dim_hom := 0;
            zero := [];
            for i in [1..num_vert] do
                if dim_M[i] = 0 then 
                    if dim_N[i] = 0 then 
                        Add(zero,NullMat(1,1,F));
                    else
                        Add(zero,NullMat(1,dim_N[i],F));
                    fi;
                else
                    if dim_N[i] = 0 then 
                        Add(zero,NullMat(dim_M[i],1,F));
                    else
                        Add(zero,NullMat(dim_M[i],dim_N[i],F));
                    fi;
                fi;
            od;      
            for i in [1..num_vert] do
                if (dim_M[i] <> 0) and (dim_N[i] <> 0) then 
                    for m in BasisVectors(Basis(FullMatrixSpace(F,dim_M[i],dim_N[i]))) do
                        dim_hom := dim_hom + 1;
                        homs[dim_hom] := ShallowCopy(zero);
                        homs[dim_hom][i] := m;
                    od;
                fi;
            od;
            for i in [1..dim_hom] do 
                homs[i] := Objectify( NewType( CollectionsFamily( GeneralMappingsFamily(
                                   ElementsFamily( FamilyObj( M ) ),
                                   ElementsFamily( FamilyObj( N ) ) ) ), 
                                   IsPathAlgebraMatModuleHomomorphism and IsPathAlgebraMatModuleHomomorphismRep and IsAttributeStoringRep ), rec( maps := homs[i] ));
                SetPathAlgebraOfMatModuleMap(homs[i], A);
                SetSource(homs[i], M);
                SetRange(homs[i], N);
                SetIsWholeFamily(homs[i],true);
            od;

            return homs;
        fi;
    fi;
end
);

#######################################################################
##
#A  EndOverAlgebra( <M>, <N> )
##
##  This function computes endomorphism ring of the module  <M>  and
##  representing it as an general GAP algebra. The algorithm it uses is
##  based purely on linear algebra.
##
InstallMethod( EndOverAlgebra,
    "for a representations of a quiver",
    [ IsPathAlgebraMatModule ], 0,
    function( M )

    local EndM, R, F, dim_M, alglist, i, j, r, maps, A; 

    EndM := HomOverAlgebra(M,M);
    R := RightActingAlgebra(M); 
    F := LeftActingDomain(R);
    dim_M := DimensionVector(M);
    alglist := [];
    for i in [1..Length(dim_M)] do 
        if dim_M[i] <> 0 then 
            Add(alglist, MatrixAlgebra(F,dim_M[i]));
        fi;
    od;
    maps := [];
    for i in [1..Length(EndM)] do
        maps[i] := NullMat(Dimension(M),Dimension(M),F);
        r := 1; 
        for j in [1..Length(dim_M)] do 
            if dim_M[j] <> 0 then 
                maps[i]{[r..r+dim_M[j]-1]}{[r..r+dim_M[j]-1]} := EndM[i]!.maps[j];
            fi;
            r := r + dim_M[j];
        od; 
    od;
    A := DirectSumOfAlgebras(alglist); 
    
    return SubalgebraWithOne(A,maps,"basis"); 
end
);

#######################################################################
##
#A  RightFacApproximation( <M>, <C> )
##
##  This function computes a right Fac<M>-approximation of the module 
##  <C>. 
##
InstallMethod( RightFacApproximation,
    "for a representations of a quiver",
    [ IsPathAlgebraMatModule, IsPathAlgebraMatModule ], 0,
    function( M, C )

    local homMC, i, generators; 

    homMC := HomOverAlgebra(M,C); 
    generators := [];
    for i in [1..Length(homMC)] do
        Append(generators,ImagesSet(homMC[i],Source(homMC[i])));
    od;
    
    return SubRepresentationInclusion(C,generators);
end
);

#######################################################################
##
#O  NumberOfNonIsoDirSummands( <M> )
##
##  This function computes the number of non-isomorphic indecomposable 
##  direct summands of the module  <M>, and in addition returns the 
##  dimensions of the simple blocks of the semisimple ring  
##  End(M)/rad End(M). 
##
InstallMethod( NumberOfNonIsoDirSummands,
    "for a representations of a quiver",
    [ IsPathAlgebraMatModule ], 0,
    function( M )
 
    local EndM, K, J, gens, I, A, top, AA, B, n,
          i, j, genA, V, W, d;

    EndM := EndOverAlgebra(M);
    K := LeftActingDomain(M);
    J := RadicalOfAlgebra(EndM);
    gens := GeneratorsOfAlgebra(J);
    I := Ideal(EndM,gens); 
    A := EndM/I;
    top := CentralIdempotentsOfAlgebra(A);
    
    return [Length(top),List(DirectSumDecomposition(EndM/I),Dimension)];
end
);

#######################################################################
##
#A  DualOfModuleHomomorphism( <f> )
##
##  This function computes the dual of a homomorphism from the module  
##  <M>  to the module  <N>.
##
InstallMethod ( DualOfModuleHomomorphism,
    "for a map between representations of a quiver",
    [ IsPathAlgebraMatModuleHomomorphism ], 0,
    function( f )

    local mats, M, N;
   
    mats := f!.maps;
    mats := List(mats, x -> TransposedMat(x));
    M := DualOfModule(Source(f));
    N := DualOfModule(Range(f));

    return RightModuleHomOverAlgebra(N,M,mats);
end
);

#######################################################################
##
#A  SocleOfModuleInclusion( <M> )
##
##  This function computes the map from the socle of  M  to the module M, 
##  soc(M) ---> M.
##
InstallMethod( SocleOfModuleInclusion, 
    "for a pathalgebramatmodule",
    true, 
    [ IsPathAlgebraMatModule ], 0,
    function( M )

    local A, K, Q, vertices, num_vert, outgoingarrows, mats, arrows, dim_M, 
          subspaces, i, a, j, socle, matrixfunction, dim_socle, socleofmodule, 
          socleinclusion, V, temp;

    A := RightActingAlgebra(M);
    if Dimension(M) = 0 then 
        return ZeroMapping(M,M);
    else
        K := LeftActingDomain(A);
        Q := QuiverOfPathAlgebra(A);
        vertices := VerticesOfQuiver(Q);
        num_vert := Length(vertices);
        outgoingarrows := List([1..num_vert], x -> OutgoingArrowsOfVertex(vertices[x]));
        dim_M := DimensionVector(M);
        mats := MatricesOfPathAlgebraModule(M);
        arrows := ArrowsOfQuiver(Q);
        dim_socle := List([1..num_vert], x -> 0);
        socle := List([1..num_vert], x -> []);
        for i in [1..num_vert] do
            if ( Length(outgoingarrows[i]) = 0 ) or ( dim_M[i] = 0 ) then
                dim_socle[i] := dim_M[i];
                if  dim_M[i] = 0 then
                    socle[i] := NullMat(1,1,K);
                else
                    socle[i] := IdentityMat(dim_M[i],K);
                fi;
            else
                subspaces := List([1..dim_M[i]], y -> []);   
                for a in outgoingarrows[i] do
                    for j in [1..dim_M[i]] do
                        Append(subspaces[j], StructuralCopy(mats[Position(arrows,a)][j]));
                    od;
                od;
                V := FullRowSpace(K,dim_M[i]);
                temp := NullspaceMat(subspaces);
                dim_socle[i] := Dimension(Subspace(V,temp));
                if dim_socle[i] = 0 then
                    socle[i] := NullMat(1,dim_M[i],K);
                else
                    socle[i] := temp;
                fi;
            fi;
        od;
        socleofmodule := RightModuleOverPathAlgebra(A,dim_socle,[]);
        socleinclusion := RightModuleHomOverAlgebra(socleofmodule,M,socle);
#        SetSocleOfModule(M,socleofmodule);
        return socleinclusion;
    fi;
end
  );

#######################################################################
##
#A  SocleOfModule( <M> )
##
##  This function computes the socle  soc(M)  of the module M. 
##
InstallMethod( SocleOfModule, 
    "for a pathalgebramatmodule",
    true, 
    [ IsPathAlgebraMatModule ], 0,
    function( M )

    return Source(SocleOfModuleInclusion(M));
end
);

#######################################################################
##
#O  CommonDirectSummand( <M>, <N> )
##
##  This function is using the algorithm for finding a common direct 
##  summand presented in the paper "Gauss-Elimination und der groesste
##  gemeinsame direkte Summand von zwei endlichdimensionalen Moduln"
##  by K. Bongartz, Arch Math., vol. 53, 256-258, with the modification
##  done by Andrzej Mroz found in "On the computational complexity of Bongartz's
##  algorithm" (improving the complexity of the algorithm).
##
InstallMethod( CommonDirectSummand, 
    "for two path algebra matmodules",
    [ IsPathAlgebraMatModule, IsPathAlgebraMatModule  ], 0,
    function( M, N ) 

    local K, HomMN, HomNM, mn, nm, n, m, i, j,  
          l, zero, f, fnm, nmf;

    if RightActingAlgebra(M) <> RightActingAlgebra(N) then 
        Print("The two modules are not modules over the same algebra.\n");
        return fail;
    else
        HomMN := HomOverAlgebra(M,N);
        HomNM := HomOverAlgebra(N,M);
        mn := Length(HomMN);
        nm := Length(HomNM);
      
        if mn = 0 or nm = 0 then 
            return false;
        fi;
      
        m := Maximum(DimensionVector(M));
        n := Maximum(DimensionVector(N));
        if n = m then
            l := n;
        else
            l := Minimum([n,m]) + 1;
        fi;
      
        zero := ZeroMapping(M,M);
      
        for j in [1..nm] do
            for i in [1..mn] do
                if l>1 then  # because hom^0*hom => error!       
                    f := (HomMN[i]*HomNM[j])^(l-1)*HomMN[i];
                else 
                    f := HomMN[i];
                fi;
                
                fnm := f*HomNM[j];
              
                if fnm <> zero then
                    nmf := HomNM[j]*f; 
                    return [Image(fnm),Kernel(fnm),Image(nmf),Kernel(nmf)];
                fi;
            od;
        od;

        return false;
    fi; 
end
);

#######################################################################
##
#O  MaximalCommonDirectSummand( <M>, <N> )
##
##  This function is using the algorithm for finding a maximal common 
##  direct summand based on the algorithm presented in the paper 
##  "Gauss-Elimination und der groesste gemeinsame direkte Summand von 
##  zwei endlichdimensionalen Moduln" by K. Bongartz, Arch Math., 
##  vol. 53, 256-258, with the modification done by Andrzej Mroz found 
##  in "On the computational complexity of Bongartz's algorithm" 
##  (improving the complexity of the algorithm).
##
InstallMethod( MaximalCommonDirectSummand, 
    "for two path algebra matmodules",
    [ IsPathAlgebraMatModule, IsPathAlgebraMatModule  ], 0,
    function( M, N ) 

    local U, V, maxcommon, L;

    U := M;
    V := N;
    maxcommon := [];
    repeat 
        L := CommonDirectSummand(U,V);
        if L <> false and L <> fail then 
            Add(maxcommon,L[1]);
            U := L[2];
            V := L[4];
            if Dimension(L[2]) = 0 or Dimension(L[4]) = 0 then
                break;
            fi;
        fi;
    until  L = false or L = fail;
    
    if Length(maxcommon) = 0 then 
        return false;
    else 
        return [maxcommon,U,V];
    fi;     
end
);

#######################################################################
##
#O  IsomorphicModules( <M>, <N> )
##
##  This function returns true if the modules  <M>  and  <N>  are 
##  isomorphic, an error message if  <M>  and  <N>  are not modules over 
##  the same algebra and false otherwise.
##  
InstallMethod( IsomorphicModules, 
    "for two path algebra matmodules",
    [ IsPathAlgebraMatModule, IsPathAlgebraMatModule  ], 0,
    function( M, N ) 

    local L;

    if DimensionVector(M) <> DimensionVector(N) then 
        return false;
    elif Dimension(M) = 0 and Dimension(N) = 0 then 
        return true; 
    else 
        L := MaximalCommonDirectSummand(M,N);
        if L = false then 
            return false;
        else
            if Dimension(L[2]) = 0 and Dimension(L[3]) = 0 then 
                return true;
            else
                return false;
            fi;
        fi;
    fi;
end
); 

#######################################################################
##
#O  IsDirectSummand( <M>, <N> )
##
##  This function returns true if the module  <M>  is isomorphic to a 
##  direct of the module  <N>, an error message if  <M>  and  <N>  are 
##  not modules over the same algebra and false otherwise.
##  
InstallMethod( IsDirectSummand, 
    "for two path algebra matmodules",
    [ IsPathAlgebraMatModule, IsPathAlgebraMatModule  ], 0,
    function( M, N ) 

    local L;

    if not DimensionVectorPartialOrder(M,N) then 
        return false;
    else 
        L := MaximalCommonDirectSummand(M,N);
        if L = false then 
            return false;
        else 
            if Dimension(L[2]) = 0 then 
                return true;
            else
                return false;
            fi;
        fi;
    fi;
end
); 

#######################################################################
##
#O  IsInAdditiveClosure( <M>, <N> )
##
##  This function returns true if the module  <M>  is in the additive
##  closure of the module  <N>, an error message if  <M>  and  <N>  are 
##  not modules over the same algebra and false otherwise.
##
InstallMethod( IsInAdditiveClosure, 
    "for two path algebra matmodules",
    [ IsPathAlgebraMatModule, IsPathAlgebraMatModule  ], 0,
    function( M, N ) 

    local K, HomMN, HomNM, MM, i, j, HomMM, V_M;

    if RightActingAlgebra(M) <> RightActingAlgebra(N) then 
        return fail;
    else
#
# Computing Hom(M,N) and Hom(N,M), finding the subspace in Hom(M,M)
# spanned by Hom(M,N)*Hom(M,N), if they have the same dimension, then 
# the identity on  M  is in the linear span of Hom(M,N)*Hom(M,N) and  
# module M is in the additive closure of N. 
#
        K := LeftActingDomain(M);
        HomMN := HomOverAlgebra(M,N);
        HomNM := HomOverAlgebra(N,M);
        MM := [];
        for i in [1..Length(HomMN)] do
            for j in [1..Length(HomNM)] do
                Add(MM,HomMN[i]*HomNM[j]);
            od;
        od;
        MM := List(MM,x->x!.maps);
        for i in [1..Length(MM)] do
            MM[i] := List(MM[i],x->Flat(x)); 
            MM[i] := Flat(MM[i]);
        od;
        HomMM:=HomOverAlgebra(M,M);
        if Length(MM) = 0 then 
            V_M := TrivialSubspace(K);
        else 
            V_M := VectorSpace(K,MM);
        fi; 
        if Dimension(V_M) = Length(HomOverAlgebra(M,M)) then
            return true;
        else
            return false;
        fi;
    fi;
end
);

#######################################################################
##
#O  MorphismOnKernel( <f>, <g>, <beta>, <alpha> )
##
##  Given the following commutative diagram
##          B -- f --> C
##          |          |
##     beta |          | alpha
##          V          V 
##          B' - g --> C' 
##  this function finds the induced homomorphism on the kernels of 
##  f and g. 
##
InstallMethod ( MorphismOnKernel, 
    "for commutative diagram of PathAlgebraMatModuleMaps",
    true,
    [ IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism ], 
    0,
    function( f, g, beta, alpha )

    local K, kerf, kerg, B, dim_vec_kerf, dim_vec_kerg, map, j, i, p, mat;
#
#  Checking if a commutative diagram was entered
# 
    if f*alpha = beta*g then 
# 
#
        K := LeftActingDomain(Source(f));
        kerf := KernelInclusion(f);
        kerg := KernelInclusion(g);
#
#  Computing the information needed for lifting the morphism.
#
        B := BasisVectors(Basis(Source(kerf)));
        B := List(B, x -> ImageElm(kerf*beta,x));
        B := List(B, x -> PreImagesRepresentative(kerg,x));
#
#  Computing dimension vectors so that we can insert zero matrices of 
#  the right size. 
#
        dim_vec_kerf := DimensionVector(Source(kerf));
        dim_vec_kerg := DimensionVector(Source(kerg));
        map := [];
        j := 0;
        for i in [1..Length(dim_vec_kerf)] do
#
#  If the kernel of  f  is zero in vertex  i, then insert a zero matrix of
#  the right size, do not use any of the lifting information.
# 
            if dim_vec_kerf[i] = 0 then
                if  dim_vec_kerg[i] = 0 then 
                    Add(map,NullMat(1,1,K));
                else
                    Add(map,NullMat(1,dim_vec_kerg[i],K));
                fi;
            else
#
#  If the kernel of  f  is non-zero in vertex  i, then use the lifting 
#  information to compute the right matrix for the map from vertex  i  to  i.
# 
                mat := [];
                for p in [1..dim_vec_kerf[i]] do
                    j := j + 1;
                    Add(mat,ExtRepOfObj(ExtRepOfObj(B[j]))[i]);
                od;
                Add(map,mat);
            fi;
        od;
        return RightModuleHomOverAlgebra(Source(kerf),Source(kerg),map);
    else 
       return fail;
    fi;
end
);

#######################################################################
##
#O  MorphismOnImage( <f>, <g>, <beta>, <alpha> )
##
##  Given the following commutative diagram
##          B -- f --> C
##          |          |
##     beta |          | alpha
##          V          V 
##          B' - g --> C' 
##  this function finds the induced homomorphism on the images of 
##  f and g. 
##
InstallMethod ( MorphismOnImage, 
    "for commutative diagram of PathAlgebraMatModuleMaps",
    true,
    [ IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism ], 
    0,
    function( f, g, beta, alpha )

    local K, imagef, imageg, B, dim_vec_imagef, dim_vec_imageg, map, j, i, p, mat;
#
#  Checking if a commutative diagram was entered
# 
    if f*alpha = beta*g then 
# 
#
        K := LeftActingDomain(Source(f));
        imagef := ImageProjection(f);
        imageg := ImageProjection(g);
#
#  Computing the information needed for lifting the morphism.
#
        B := BasisVectors(Basis(Range(imagef)));
        B := List(B, x -> PreImagesRepresentative(imagef,x));
        B := List(B, x -> ImageElm(beta*imageg,x));
#
#  Computing dimension vectors so that we can insert zero matrices of 
#  the right size. 
#
        dim_vec_imagef := DimensionVector(Range(imagef));
        dim_vec_imageg := DimensionVector(Range(imageg));
        map := [];
        j := 0;
        for i in [1..Length(dim_vec_imagef)] do
#
#  If the image of  f  is zero in vertex  i, then insert a zero matrix of
#  the right size, do not use any of the lifting information.
# 
            if dim_vec_imagef[i] = 0 then
                if  dim_vec_imageg[i] = 0 then 
                    Add(map,NullMat(1,1,K));
                else
                    Add(map,NullMat(1,dim_vec_imageg[i],K));
                fi;
            else
#
#  If the cokernel of  f  is non-zero in vertex  i, then use the lifting 
#  information to compute the right matrix for the map from vertex  i  to  i.
# 
                mat := [];
                for p in [1..dim_vec_imagef[i]] do
                    j := j + 1;
                    Add(mat,ExtRepOfObj(ExtRepOfObj(B[j]))[i]);
                od;
                Add(map,mat);
            fi;
        od;
        return RightModuleHomOverAlgebra(Range(imagef),Range(imageg),map);
    else 
        return fail;
    fi;
end
);

#######################################################################
##
#O  MorphismOnCoKernel( <f>, <g>, <beta>, <alpha> )
##
##  Given the following commutative diagram
##          B -- f --> C
##          |          |
##     beta |          | alpha
##          V          V 
##          B' - g --> C' 
##  this function finds the induced homomorphism on the cokernels of 
##  f and g. 
##
InstallMethod ( MorphismOnCoKernel, 
    "for commutative diagram of PathAlgebraMatModuleMaps",
    true,
    [ IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism ], 
    0,
    function( f, g, beta, alpha )

    local K, cokerf, cokerg, B, dim_vec_cokerf, dim_vec_cokerg, map, j, i, p, mat;
#
#  Checking if a commutative diagram was entered
# 
    if f*alpha = beta*g then 
# 
#
        K := LeftActingDomain(Source(f));
        cokerf := CoKernelProjection(f);
        cokerg := CoKernelProjection(g);
#
#  Computing the information needed for lifting the morphism.
#
        B := BasisVectors(Basis(Range(cokerf)));
        B := List(B, x -> PreImagesRepresentative(cokerf,x));
        B := List(B, x -> ImageElm(alpha*cokerg,x));
#
#  Computing dimension vectors so that we can insert zero matrices of 
#  the right size. 
#
        dim_vec_cokerf := DimensionVector(Range(cokerf));
        dim_vec_cokerg := DimensionVector(Range(cokerg));
        map := [];
        j := 0;
        for i in [1..Length(dim_vec_cokerf)] do
#
#  If the cokernel of  f  is zero in vertex  i, then insert a zero matrix of
#  the right size, do not use any of the lifting information.
# 
            if dim_vec_cokerf[i] = 0 then
                if  dim_vec_cokerg[i] = 0 then 
                    Add(map,NullMat(1,1,K));
                else
                    Add(map,NullMat(1,dim_vec_cokerg[i],K));
                fi;
            else
#
#  If the cokernel of  f  is non-zero in vertex  i, then use the lifting 
#  information to compute the right matrix for the map from vertex  i  to  i.
# 
                mat := [];
                for p in [1..dim_vec_cokerf[i]] do
                    j := j + 1;
                    Add(mat,ExtRepOfObj(ExtRepOfObj(B[j]))[i]);
                od;
                Add(map,mat);
            fi;
        od;
        return RightModuleHomOverAlgebra(Range(cokerf),Range(cokerg),map);
    else 
        return fail;
    fi;
end
);


#######################################################################
##
#O  LiftingMorphismFromProjective( <f>, <g> )
##
##  Given the following diagram
##                P
##                |
##                | g
##            f   V 
##        B ----> C 
##  where  P  is a direct sum of indecomposable projective modules 
##  constructed via DirectSumOfQPAModules and  f  an epimorphism, 
##  this function finds a lifting of  g  to  B.
##
InstallMethod ( LiftingMorphismFromProjective, 
    "for two PathAlgebraMatModuleMaps",
    true,
    [ IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism ], 
    0,
    function( f, g )

    local P, K, B, inclusions, projections, i, m, hmap; 
#
#  Checking if the input is as required
# 
    P := Source(g);
    if IsProjectiveModule(P) and IsDirectSumOfModules(P) and IsSurjective(f) and ( Range(f) = Range(g) ) then 
# 
#
        K := LeftActingDomain(P);
        inclusions  := DirectSumInclusions(P);
        projections := DirectSumProjections(P);
        hmap := [];
#
#  First construct the lifting from each indecomposable direct summand of  P.
# 
        for i in [1..Length(inclusions)] do
            m := MinimalGeneratingSetOfModule(Source(inclusions[i]))[1];
            if ImageElm(g,ImageElm(inclusions[i],m)) = Zero(Range(g)) then 
                Add(hmap,ZeroMapping(Source(inclusions[i]),Source(f)));
            else 
                m := PreImagesRepresentative(f,ImageElm(g,ImageElm(inclusions[i],m)));
                Add(hmap,HomFromProjective(m,Source(f)));
            fi;
        od;
#
#  Make sure that the partial liftings start in the right modules/variables. 
#
        hmap := List([1..Length(inclusions)], x -> RightModuleHomOverAlgebra(Source(inclusions[x]),Source(f),hmap[x]!.maps));
        return projections*hmap;
    else 
        return fail;
    fi;
end
);

#######################################################################
##
#O  LiftingInclusionMorphisms( <f>, <g> )
##
##  Given the following diagram
##                A
##                |
##                | g
##            f   V 
##        B ----> C 
##  where  f  and  g  are inclusions, this function constructs a 
##  morphism (inclusion) from  A  to  B, whenever the image of  g  is
##  contained in the image of  f.  Otherwise the function returns fail.
##
InstallMethod ( LiftingInclusionMorphisms, 
    "for two PathAlgebraMatModuleMaps",
    true,
    [ IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism ], 
    0,
    function( f, g )

    local pi, K, B, dim_vec_sourcef, dim_vec_sourceg, map, i, j, mat, p; 
#
#  Checking if the image of  g  is contained in the image of  f.
# 
    pi := CoKernelProjection(f);
    if ( IsInjective(f) and IsInjective(g) ) and ( Range(f) = Range(g) ) and ( g*pi = ZeroMapping(Source(g),Range(pi)) ) then 
# 
#
        K := LeftActingDomain(Source(g));
        B := BasisVectors(Basis(Source(g)));
        B := List(B, x -> PreImagesRepresentative(f,ImageElm(g,x)));
#
#  Computing dimension vectors so that we can insert zero matrices of 
#  the right size. 
#
        dim_vec_sourceg := DimensionVector(Source(g));
        dim_vec_sourcef := DimensionVector(Source(f));
        map := [];
        j := 0;
        for i in [1..Length(dim_vec_sourceg)] do
#
#  If the source of  g  is zero in vertex  i, then insert a zero matrix of
#  the right size, do not use any of the lifting information.
# 
            if dim_vec_sourceg[i] = 0 then
                if  dim_vec_sourcef[i] = 0 then 
                    Add(map,NullMat(1,1,K));
                else
                    Add(map,NullMat(1,dim_vec_sourcef[i],K));
                fi;
            else
#
#  If the source of  g  is non-zero in vertex  i, then use the lifting 
#  information to compute the right matrix for the map from vertex  i  to  i.
# 
                mat := [];
                for p in [1..dim_vec_sourceg[i]] do
                    j := j + 1;
                    Add(mat,ExtRepOfObj(ExtRepOfObj(B[j]))[i]);
                od;
                Add(map,mat);
            fi;
        od;
        return RightModuleHomOverAlgebra(Source(g),Source(f),map);
    else 
        return fail;
    fi;
end
);

#######################################################################
##
#O  IntersectionOfSubmodules( <f>, <g> )
##
##                                              f              g
##  Given two submodules/subrepresentations  M ---> X  and  N ---> X  of 
##  a module  X  by two monomorphism  f  and  g, this function computes 
##  the intersection of the images of  f  and  g  in the sense that it 
##  computes the pullback of the diagram
##             f'
##          E ---> N
##          |      |
##       g' |      | g 
##          |      |
##          V  f   V     
##          M ---> X
##  
##  and it returns the maps [ f', g', g'*f ]
##
InstallOtherMethod ( IntersectionOfSubmodules, 
    "for two IsPathAlgebraMatModuleHomomorphisms",
    true,
    [ IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism ], 
    0,
    function( f, g )

    local pullback;
#
#   Checking the input
#
    if not IsInjective(f) then 
        Error("the first argument is not a monomorphism,");
    fi;
    if not IsInjective(g) then 
        Error("the second argument is not a monomorphism,");
    fi;
    if not ( Range(f) = Range(g) ) then 
        Error("must have two submodules of the same module,");
    fi;
#
#   Doing the computations
#
    pullback := PullBack(f,g);
    
    return [pullback[1],pullback[2],pullback[2]*f];
end
);

#######################################################################
##
#O  IntersectionOfSubmodules( <args> )
##
##                                            f_i             
##  Given submodules/subrepresentations  M_i -----> X  for i = 1,...,n 
##  of a module  X  by  n  monomorphism  f_i, this function computes 
##  the intersection of the images of  f  and  g  in the sense that it 
##  computes the pullbacks of the diagrams
##             f_1'
##         E_1 ----> N           E_2 ---------> N
##          |        |            |             |
##     f_2' |        | f_2   f_3' |             | f_3  and so on.
##          |        |            |             |
##          V  f_1   V            V   f_2'*f_1  V
##          M -----> X           E_1 ---------> X
##  
##  It returns the map f_n'*f_(n-1)* ... * f_2'*f_1.
##
InstallMethod ( IntersectionOfSubmodules, 
    "for two IsPathAlgebraMatModuleHomomorphisms",
    true,
    [ IsDenseList ], 
    0,
    function( list )

    local A, f, i;
#
#   Checking the input
#
    if IsEmpty( list ) then 
        Error( "<list> must be non-empty" ); 
    fi;
    A := RightActingAlgebra(Source(list[1]));
    for f in list do
        if RightActingAlgebra( Source(f) ) <> A then
            Error( "all entries in <list> must be homomorphisms of modules over the same algebra");
        fi;
    od;
    if not ForAll(list, IsInjective) then 
        Error("not all the arguments are monomorphisms,");
    fi;
    A := Range(list[1]);
    if not ForAll(list, x -> Range(x) = A) then 
        Error("must have submodules of the same module,");
    fi;
#
#   Doing the computations.
#
    f := list[1];
    for i in [2..Length(list)] do
        f := IntersectionOfSubmodules(f,list[i])[3];
    od;

    return f;
end
);

#######################################################################
##
#O  SumOfSubmodules( <f>, <g> )
##
##                                              f              g
##  Given two submodules/subrepresentations  M ---> X  and  N ---> X  of 
##  a module  X  by two monomorphism  f  and  g, this function computes 
##  the sum of the images of  f  and  g  in the sense that it computes 
##  the submodule/subrepresentation  M + N  generated by the image of  f  
##  and the image of  g. It returns the maps M + N ---> X,  M ---> M + N,  
##  N ---> M + N, in that order.  
##
InstallOtherMethod ( SumOfSubmodules, 
    "for two IsPathAlgebraMatModuleHomomorphisms",
    true,
    [ IsPathAlgebraMatModuleHomomorphism, IsPathAlgebraMatModuleHomomorphism ], 
    0,
    function( f, g )

    local sumMN, inclusions, projections, F;
#
#   Checking the input
#
    if not IsInjective(f) then 
        Error("the first argument is not a monomorphism,");
    fi;
    if not IsInjective(g) then 
        Error("the second argument is not a monomorphism,");
    fi;
    if not ( Range(f) = Range(g) ) then 
        Error("must have two submodules of the same module,");
    fi;
#
#   Doing the computations
#
    sumMN := DirectSumOfQPAModules([Source(f),Source(g)]);
    inclusions := DirectSumInclusions(sumMN);
    projections := DirectSumProjections(sumMN);

    F := ImageProjectionInclusion(projections[1]*f + projections[2]*g);

    return [F[2],inclusions[1]*F[1],inclusions[2]*F[1]];
end
);

#######################################################################
##
#O  SumOfSubmodules( <args> )
##
##                                            f_i             
##  Given submodules/subrepresentations  M_i -----> X  for i = 1,...,n 
##  of a module  X  by  n  monomorphism  f_i, this function computes 
##  the intersection of the images of  f  and  g  in the sense that it 
##  computes the pullbacks of the diagrams
##             f_1'
##         E_1 ----> N           E_2 ---------> N
##          |        |            |             |
##     f_2' |        | f_2   f_3' |             | f_3  and so on.
##          |        |            |             |
##          V  f_1   V            V   f_2'*f_1  V
##          M -----> X           E_1 ---------> X
##  
##  and it returns the map f_n'*f_(n-1)* ... * f_2'*f_1.
##
InstallMethod ( SumOfSubmodules, 
    "for two IsPathAlgebraMatModuleHomomorphisms",
    true,
    [ IsDenseList ], 
    0,
    function( list )

    local A, f, i;
#
#   Checking the input
#
    if IsEmpty( list ) then 
        Error( "<list> must be non-empty" ); 
    fi;
    A := RightActingAlgebra(Source(list[1]));
    for f in list do
        if RightActingAlgebra( Source(f) ) <> A then
            Error( "all entries in <list> must be homomorphisms of modules over the same algebra");
        fi;
    od;
    A := Range(list[1]);
    if not ForAll(list, x -> Range(x) = A) then 
        Error("must have submodules of the same module,");
    fi;   
    if not ForAll(list, IsInjective) then 
        Error("not all the arguments are monomorphisms,");
    fi;
#
#   Doing the computations
#
    f := list[1];
    for i in [2..Length(list)] do
        f := SumOfSubmodules(f,list[i])[1];
    od;

    return f;
end
);

#######################################################################
##
#O  HomFactoringThroughProjOverAlgebra( <M>, <N> )
##
##  Given two modules  M  and  N  over a finite dimensional quotient
##  of a path algebra, this function computes a basis of the homomorphisms
##  that factor through a projective module.
##
InstallMethod ( HomFactoringThroughProjOverAlgebra,
    "for two PathAlgebraMatModule's",
    true,
    [ IsPathAlgebraMatModule, IsPathAlgebraMatModule ],
    0,
    function( M, N )

    local f, HomMP_N, homthroughproj, flathomthroughproj, K, V, B, dim_M,
          dim_N, homs, hom, i, j, matrices, dimension, d1, d2, matrix;
    #
    # Testing input
    #
    if RightActingAlgebra(M) <> RightActingAlgebra(N) then
        Error("the entered modules are not modules over the same algebra,");
    fi;
    #
    # if one module is zero, return the empty list
    #
    if Dimension(M) = 0 or Dimension(N) = 0 then
        return [];
    fi;
    #
    # The maps factoring through projectives are those factoring through
    # the projective cover of  N.
    #
    f := ProjectiveCover(N);
    HomMP_N := HomOverAlgebra(M,Source(f));
    homthroughproj := List(HomMP_N, x -> x*f);
    flathomthroughproj := List(homthroughproj, x -> Flat(x!.maps));
    flathomthroughproj := Filtered(flathomthroughproj, x -> x <> Zero(x));
    homthroughproj := [];
    if Length(flathomthroughproj) > 0 then
        K := LeftActingDomain(M);
        V := VectorSpace(K, flathomthroughproj);
        B := BasisVectors(Basis(V));
        dim_M := DimensionVector(M);
        dim_N := DimensionVector(N);
        homs := [];
        for hom in B do
            matrices := [];
            dimension := 0;
            for i in [1..Length(dim_M)] do
                if dim_M[i] = 0 then
                    d1 := 1;
                else
                    d1 := dim_M[i];
                fi;
                if dim_N[i] = 0 then
                    d2 := 1;
                else
                    d2 := dim_N[i];
                fi;
                matrix := [];
                for j in [1..d1] do
                    Add(matrix,hom{[dimension + 1..dimension + d2]});
                    dimension := dimension + d2;
                od;
                Add(matrices,matrix);
            od;
            Add(homs,matrices);
        od;
        homthroughproj := List(homs, x -> RightModuleHomOverAlgebra(M,N,x));
    fi;

    return homthroughproj;
end
);

#######################################################################
##
#O  EndModuloProjOverAlgebra( <M>, <N> )
##
##  Given a module  M  over a finite dimensional quotient of a path
##  algebra, this function computes the homomorphism from
##  End(M) ----> End(M)/P(M,M), where P(M,M) is the ideal in End(M)
##  generated by the morphisms factoring through projective modules.
##
InstallMethod ( EndModuloProjOverAlgebra,
    "for a PathAlgebraMatModule",
    true,
    [ IsPathAlgebraMatModule ],
    0,
    function( M )

    local EndM, factorproj, I;

    if Dimension(M) = 0 then
        Error("zero module given as input for EndModuleProjOverAlgebra,");
    else
        EndM := EndOverAlgebra(M);
        factorproj := HomFactoringThroughProjOverAlgebra(M,M);
        factorproj := List(factorproj, x -> FromHomMMToEndM(x));
        I := Ideal(EndM,factorproj);

        return NaturalHomomorphismByIdeal(EndM,I);
    fi;
end
);

#######################################################################
##
#O  FromHomMMToEndM( <f> )
##
##  This function gives a translation from endomorphisms of a module  M  
##  to the corresponding enodomorphism represented in the algebra
##  EndOverAlgebra(M). 
##
InstallMethod ( FromHomMMToEndM, 
    "for an endomorphism to an element of EndOverAlgebra",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )

    local K, dim_vect, end_f, r, j;

    K := LeftActingDomain(Source(f));
    dim_vect := DimensionVector(Source(f));
    end_f := NullMat(Dimension(Source(f)),Dimension(Source(f)),K);
    r := 1; 
    for j in [1..Length(dim_vect)] do 
        if dim_vect[j] <> 0 then 
            end_f{[r..r+dim_vect[j]-1]}{[r..r+dim_vect[j]-1]} := f!.maps[j];
            r := r + dim_vect[j];
        fi;
    od; 

    return end_f;
end
);

#######################################################################
##
#O  FromEndMToHomMM( <M>, <mat> )
##
##  This function gives a translation from an element in 
##  EndOverAlgebra(<M>) to an endomorphism of the module  M. 
##
InstallMethod ( FromEndMToHomMM, 
    "for a subset of EndOverAlgebra to HomOverAlgebra",
    true,
    [ IsPathAlgebraMatModule, IsMatrix ],
    0,
    function( M, mat )

    local K, dim_vect, maps, i, r;

    K := LeftActingDomain(M); 
    dim_vect := DimensionVector(M);

    maps := [];
    r := 1;
    for i in [1..Length(dim_vect)] do
        if dim_vect[i] = 0 then 
            Add(maps,NullMat(1,1,K));
        else
            Add(maps,mat{[r..r+dim_vect[i]-1]}{[r..r+dim_vect[i]-1]});
            r := r + dim_vect[i];
        fi;
    od;

    return RightModuleHomOverAlgebra(M,M,maps);
end
);

#######################################################################
##
#P  IsRightMinimal( <f> )
##
##  This function returns true is the homomorphism  <f>  is right 
##  minimal. 
##
InstallMethod ( IsRightMinimal, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )

    local B, C, BB, mat, Ann_f, radEndB;

    B := Source(f);
    C := Range(f);
    BB := HomOverAlgebra(B,B);
    mat := List(BB, x -> x*f);
    mat := List(mat,x -> Flat(x!.maps));
    Ann_f := NullspaceMat(mat);
    Ann_f := List(Ann_f,x -> LinearCombination(BB,x));
    radEndB := RadicalOfAlgebra(EndOverAlgebra(B)); 
    Ann_f := List(Ann_f, x -> FromHomMMToEndM(x));

    if ForAll(Ann_f, x -> x in radEndB) then 
        return true;
    else
        return false;
    fi;
end
);

#######################################################################
##
#P  IsLeftMinimal( <f> )
##
##  This function returns true is the homomorphism  <f>  is left 
##  minimal. 
##
InstallMethod ( IsLeftMinimal, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )

    local A, B, BB, mat, Ann_f, radEndB;

    A := Source(f);
    B := Range(f);
    BB := HomOverAlgebra(B,B);
    mat := List(BB, x -> f*x );
    mat := List(mat,x -> Flat(x!.maps));
    Ann_f := NullspaceMat(mat);
    Ann_f := List(Ann_f,x -> LinearCombination(BB,x));
    radEndB := RadicalOfAlgebra(EndOverAlgebra(B)); 
    Ann_f := List(Ann_f, x -> FromHomMMToEndM(x));

    if ForAll(Ann_f, x -> x in radEndB) then 
        return true;
    else
        return false;
    fi;
end
);

#######################################################################
##
#A  RightInverseOfHomomorphism( <f> )
##
##  This function returns false if the homomorphism  <f>  is not a  
##  splittable monomorphism, otherwise it returns a splitting of the
##  split monomorphism  <f>. 
##
InstallMethod ( RightInverseOfHomomorphism, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )

    local B, C, CB, id_B, mat, flat_id_B, split_f;

    B := Source(f);
    C := Range(f);
    if IsInjective(f) then 
        CB := HomOverAlgebra(C,B);
        if Length(CB) = 0 then 
            return false;
        else 
            mat := [];
            mat := List(CB, x -> f*x);
            id_B := IdentityMapping(B); 
            mat := List(mat,x -> Flat(x!.maps));
            flat_id_B := Flat(id_B!.maps); 
            split_f := SolutionMat(mat,flat_id_B);
            
            if split_f <> fail then 
                split_f := LinearCombination(CB,split_f);
                SetIsSplitEpimorphism(split_f,true);
                SetIsSplitMonomorphism(f,true);
                SetLeftInverseOfHomomorphism(split_f,f); 
                return split_f;
            else
                SetIsSplitMonomorphism(f,false);
                return false;
            fi;
        fi;
    else
        return false;
    fi;
end
  );

#######################################################################
##
#A  LeftInverseOfHomomorphism( <f> )
##
##  This function returns false if the homomorphism  <f>  is not a  
##  splittable epimorphism, otherwise it returns a splitting of the
##  split epimorphism  <f>. 
##
InstallMethod ( LeftInverseOfHomomorphism, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )

    local B, C, CB, id_C, mat, flat_id_C, split_f;

    B := Source(f);
    C := Range(f);
    if IsSurjective(f) then 
        CB := HomOverAlgebra(C,B);
        if Length(CB) = 0 then 
            return false;
        else 
            mat := List(CB, x -> x*f );
            id_C := IdentityMapping(C); 
            mat := List(mat,x -> Flat(x!.maps));
            flat_id_C := Flat(id_C!.maps); 
            split_f := SolutionMat(mat,flat_id_C);
            
            if split_f <> fail then 
                split_f := LinearCombination(CB,split_f);
                SetIsSplitMonomorphism(split_f,true);
                SetIsSplitEpimorphism(f,true);
                SetRightInverseOfHomomorphism(split_f,f); 
                return split_f;
            else
                SetIsSplitEpimorphism(f,false);
                return false;
            fi;
        fi;
    else
        return false;
    fi;
end
  );

#######################################################################
##
#P  IsSplitMonomorphism( <f> )
##
##  This function returns false if the homomorphism  <f>  is not a  
##  splittable monomorphism, otherwise it returns a splitting of the
##  homomorphism  <f>. 
##
InstallMethod ( IsSplitMonomorphism, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )

    local B, C, CB, id_B, mat, flat_id_B, split_f;

    B := Source(f);
    C := Range(f);
    if IsInjective(f) then 
        CB := HomOverAlgebra(C,B);
        if Length(CB) = 0 then 
            return false;
        else 
            mat := [];
            mat := List(CB, x -> f*x);
            id_B := IdentityMapping(B); 
            mat := List(mat,x -> Flat(x!.maps));
            flat_id_B := Flat(id_B!.maps); 
            split_f := SolutionMat(mat,flat_id_B);
            
            if split_f <> fail then 
                split_f := LinearCombination(CB,split_f);
                SetLeftInverseOfHomomorphism(split_f,f);
                SetRightInverseOfHomomorphism(f,split_f);
                SetIsSplitEpimorphism(split_f,true);
                return true;
            else
                return false;
            fi;
        fi;
    else
        return false;
    fi;
end
  );

#######################################################################
##
#P  IsSplitEpimorphism( <f> )
##
##  This function returns false if the homomorphism  <f>  is not a  
##  splittable epimorphism, otherwise it returns true. 
##
InstallMethod ( IsSplitEpimorphism, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )

    local B, C, CB, id_C, mat, flat_id_C, split_f;

    B := Source(f);
    C := Range(f);
    if IsSurjective(f) then 
        CB := HomOverAlgebra(C,B);
        if Length(CB) = 0 then 
            return false;
        else 
            mat := List(CB, x -> x*f );
            id_C := IdentityMapping(C); 
            mat := List(mat,x -> Flat(x!.maps));
            flat_id_C := Flat(id_C!.maps); 
            split_f := SolutionMat(mat,flat_id_C);
            
            if split_f <> fail then 
                split_f := LinearCombination(CB,split_f);
                SetLeftInverseOfHomomorphism(f, split_f);
                SetRightInverseOfHomomorphism(split_f,f);
                SetIsSplitMonomorphism(split_f,true);
                return true;
            else
                return false;
            fi;
        fi;
    else
        return false;
    fi;
end
);

InstallMethod ( MoreRightMinimalVersion, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f ) 

    local B, g, A, HomBA, HomAA, n, i, j, gg, hh;

    B := Source(f);
    g := KernelInclusion(f);
    A := Source(g);
    HomBA := HomOverAlgebra(B,A);
    if Length(HomBA) = 0 then 
        return f;
    else 
        HomAA  := HomOverAlgebra(A,A);
        n := Maximum(Concatenation(DimensionVector(A),DimensionVector(B)));
        for i in [1..Length(HomBA)] do 
            for j in [1..Length(HomAA)] do
                gg := (g*HomBA[i]*HomAA[j])^n;
                if gg <> ZeroMapping(A,A) then
                    hh := (HomBA[i]*HomAA[j]*g)^n;
                    return [KernelInclusion(hh)*f,ImageInclusion(hh)*f];
                fi;
            od;
        od;
        return f;
    fi;
end
);

InstallMethod ( MoreLeftMinimalVersion, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f ) 

    local B, g, C, HomCB, HomCC, n, i, j, gg, hh, t;

    B := Range(f);
    g := CoKernelProjection(f);
    C := Range(g);
    HomCB := HomOverAlgebra(C,B);
    if Length(HomCB) = 0 then 
        return f;
    else 
        HomCC := HomOverAlgebra(C,C);
        n := Maximum(Concatenation(DimensionVector(B),DimensionVector(C)));
        for i in [1..Length(HomCC)] do 
            for j in [1..Length(HomCB)] do
                gg := (HomCC[i]*HomCB[j]*g)^n;
                if gg <> ZeroMapping(C,C) then
                    hh := (g*HomCC[i]*HomCB[j])^n;
#                    t := IsSplitMonomorphism(KernelInclusion(hh));
                    t := RightInverseOfHomomorphism(KernelInclusion(hh));
                    return [f*t,f*ImageProjection(hh)];
                fi;
            od;
        od;
        return f;
    fi;
end
);

#######################################################################
##
#A  RightMinimalVersion( <f> )
##
##  This function returns a right minimal version  f'  of the 
##  homomorphism  <f>  in addition to a list of modules  B such that 
##  Source(f') direct sum the modules on the list  B  is isomorphic to
##  Source(f).
##
InstallMethod ( RightMinimalVersion, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )

    local Bprime, g, L;

    Bprime := [];
    g := f;
    repeat
        L := MoreRightMinimalVersion(g);
        if L <> g then 
            g := L[1];
            Add(Bprime,Source(L[2]));
        fi;
    until 
        L = g;
    
    SetIsRightMinimal(g,true);

    return [g,Bprime];
end
);

#######################################################################
##
#A  LeftMinimalVersion( <f> )
##
##  This function returns a left minimal version  f'  of the 
##  homomorphism  <f>  in addition to a list of modules  B such that 
##  Range(f') direct sum the modules on the list  B  is isomorphic to
##  Range(f).
##
InstallMethod ( LeftMinimalVersion, 
    "for a PathAlgebraMatModuleMap",
    true,
    [ IsPathAlgebraMatModuleHomomorphism ],
    0,
    function( f )

    local Bprime, g, L;

    Bprime := [];
    g := f;
    repeat
        L := MoreLeftMinimalVersion(g);
        if L <> g then 
            g := L[1];
            Add(Bprime,Range(L[2]));
        fi;
    until 
        L = g;

    SetIsLeftMinimal(g,true);

    return [g,Bprime];
end
);

#######################################################################
##
#O  HomFromProjective(<m>,<M>)
##
##  This function checks if the element  <m>  is an element of  <M>  and
##  if the element  <m>  is uniform. If so, the function constructs the 
##  map from the indecomposable projective generated by the vertex of 
##  support to the module  <M>  given by sending the vertex to the 
##  element  <m>. 
##
InstallMethod ( HomFromProjective, 
    "for an element in a PathAlgebraMatModule and the PathAlgebraMatModule",
    IsElmsColls,
    [ IsRightAlgebraModuleElement, IsPathAlgebraMatModule ],
    0,
    function( m, M )

    local A, K, num_vert, support, pos, P, B, mats, zeros, i, f;
#
# Finding the algebra acting on M and the number of vertices.
#
    A := RightActingAlgebra(M);
    K := LeftActingDomain(M);
    num_vert := Length(m![1]![1]);
#   
# Finding the support of m.
#
    support := SupportModuleElement(m);
#
# If the element  m  is uniform, we proceed to find the map.
# 
    if Length(support) = 1 then 
#
# And we need the "number of" the vertex ...
#
        pos := VertexPosition(support[1]);
#
# And the basis for the correct projective module, and the proj. module itself
#
        B := BasisOfProjectives(A)[pos];
        P := ShallowCopy(IndecProjectiveModules(A)[pos]);
#
# Then we calculate the matrices for the homomorphism
#
        mats := List([1..num_vert],x -> List(B[x], y -> ExtRepOfObj(m^y)![1][x]));
        zeros := Filtered([1..num_vert], x -> DimensionVector(P)[x] = 0);
        for i in zeros do 
            if DimensionVector(M)[i] <> 0 then         
                mats[i] := NullMat(1,DimensionVector(M)[i],K);
            else 
                mats[i] := NullMat(1,1,K);
            fi;
        od;
#
# Construct the homomorphism
#
        f := RightModuleHomOverAlgebra(P,M,mats);
        return f;
    else
        return fail;
    fi;
end
);

######################################################
##
#O InverseOfIsomorphism( <f> )
##
## <f> is a map which is iso. 
##
## Output is the map f^(-1), or the inverse of f.
##
InstallMethod( InverseOfIsomorphism,
               [ IsPathAlgebraMatModuleHomomorphism ],
               function( f )
    local maps, invmaps, m;
    if not(IsIsomorphism(f)) then
        Error("entered map is not an isomorphism");
    fi;
    maps := f!.maps;
    invmaps := [];
    for m in maps do
        if IsZero(m) then
            Append(invmaps, [m]);
        else
            Append(invmaps, [Inverse(m)]);
        fi;
    od;

    return RightModuleHomOverAlgebra(Range(f), Source(f), invmaps);

end);

#######################################################################
##
#O  HomomorphismFromImages( <M>, <N>, <genImages> )
##
##  <M> and <N> are modules over the same algebra.  This operation
##  computes a homomorphism f: M ---> N such that the basis
##
##    B := BasisVectors( Basis( M ) );
##
##  is sent to the list <genImages>, that is, B[i] is sent to genImages[i].
##  If this does not give a well-defined homomorphism, the final 
##  call to RightModuleHomOverAlgebra will fail.
##
InstallMethod( HomomorphismFromImages,
[ IsPathAlgebraMatModule, IsPathAlgebraMatModule, IsList ],
    function( M, N, genImages )

    local B, K, dim_vec_N, dim_vec_M, map, i, j, mat, p; 

    K := LeftActingDomain(M);

    B := BasisVectors( Basis( M ) );

#
#  Checks that the element B[i] is sent to an element of N with support in 
#  the same vertex as B[i].
#
    for i in [1..Length(B)] do
        if ( SupportModuleElement( B[i] ) <> SupportModuleElement( genImages[i] ) ) and
           ( not IsZero( genImages[i] ) ) then
            Error("does not give a homomorphism -- wrong support,");
        fi;
    od;
    
#
#  Computing dimension vectors so that we can insert zero matrices of 
#  the right size. 
#
    dim_vec_M := DimensionVector(M);
    dim_vec_N := DimensionVector(N);
    map := [];
    j := 0;
    for i in [1..Length(dim_vec_M)] do
#
#  If M is zero in vertex  i, then insert a zero matrix of
#  the right size, do not use any of the lifting information.
# 
        if dim_vec_M[i] = 0 then
            if  dim_vec_N[i] = 0 then 
                Add(map,NullMat(1,1,K));
            else
                Add(map,NullMat(1,dim_vec_N[i],K));
            fi;
        else
#
#  If M is non-zero in vertex  i, then use genImages
#  to compute the right matrix for the map from vertex  i  to  i.
# 
            mat := [];
            for p in [1..dim_vec_M[i]] do
                j := j + 1;
                if not ( IsPathModuleElem(genImages[j]) ) then
                    if IsList( ExtRepOfObj( genImages[j] ) ) then
                        Add(mat, ExtRepOfObj( genImages[j] )[i]);
                    else
                        Add(mat, ExtRepOfObj(ExtRepOfObj(genImages[j]))[i]);
                    fi;
                else
                    Add(mat,ExtRepOfObj(ExtRepOfObj(genImages[j]))[i]);
                fi;
            od;
            Add(map,mat);
        fi;
    od;
    return RightModuleHomOverAlgebra(M, N, map);

end
);

######################################################
##
#O MultiplyListsOfMaps( <projections>, <matrix>, <inclusions> )
##
## <projections> is a list of m maps, <matrix> is a list of
## m lists of n maps, <inclusions> is a list of n maps. 
## Considering <projections> as a 1xm-matrix, <matrix> as an
## mxn-matrix and and <inclusions> as an nx1-matrix, the 
## matrix product is computed. Naturally, the maps in the 
## matrices must be composable.
##
## Output is a map (not a 1x1-matrix).
##
## Utility method not supposed to be here, but there seemed
## to be no existing GAP method for this.
##
InstallMethod( MultiplyListsOfMaps,
                    [ IsList, IsList, IsList ],
                    function( projections, matrix, inclusions )
    local sum, list, n, m, i, j;

    n := Length(projections);
    m := Length(inclusions);
    list := [1..m];
    sum := 0;

    for i in [1..m] do
       list[i] := projections*matrix[i];
    od;

    sum := list*inclusions;
    return sum;
end);

#######################################################################
##
#O  IsomorphismOfModules( <M>, <N> )
##
##  Given two modules  <M>  and  <N>  over a (quotient of a) path algebra
##  this function return an isomorphism from  <M>  to  <N>  is the two
##  modules are isomorphic, and false otherwise.
##
InstallMethod ( IsomorphismOfModules, 
    "for two PathAlgebraMatModules",
    true,
    [ IsPathAlgebraMatModule, IsPathAlgebraMatModule ], 
    0,
    function( M, N )

    local K, dim_M, mats, i, maps, splittingmaps, fn, fsplit, gn, gsplit, 
          f_alpha, f_beta, g_alpha, g_beta, q, B, genImages, delta, 
          recursiveIOM;
    # 
    # If  M  and  N are not modules over the same algebra, 
    # return an error message.
    #
    if RightActingAlgebra(M) <> RightActingAlgebra(N) then 
        return false;
    fi;
    #
    # If the dimension vectors of  M  and  N  are different, they
    # are not isomorphic, so return  false.
    #
    if DimensionVector(M) <> DimensionVector(N) then
        return false;
    fi;
    #
    # If both  M  and  N  are the zero module, return the zero map.
    #
    if Dimension(M) = 0 and Dimension(N) = 0 then
        return ZeroMapping(M,N);
    fi;
    #
    # If  M  and  N  are the same representations (i.e. with the 
    # same vector spaces and defining matrices, then return the 
    # "identity homomorphism". 
    #
    if M = N then 
        K := LeftActingDomain(M);
        dim_M := DimensionVector(M);
        mats := [];
        for i in [1..Length(dim_M)] do
            if dim_M[i] = 0 then
                Add(mats,NullMat(1,1,K));
            else
                Add(mats,IdentityMat(dim_M[i],K));
            fi;
        od;
        
        return RightModuleHomOverAlgebra(M,N,mats);
    fi;
    #
    # Finds a homomorphism from  M  to  N  and corresponding homomorphism
    # from  N  to  M  identifying a common non-zero direct summand of  M  
    # and  N, if such exists. Otherwise it returns false. 
    #
    splittingmaps := function( M, N) 
        local K, HomMN, HomNM, mn, nm, n, m, i, j,  
              l, zero, f, fnm, nmf; 
            
        HomMN := HomOverAlgebra(M,N);
        HomNM := HomOverAlgebra(N,M);
        mn := Length(HomMN);
        nm := Length(HomNM);
      
        if mn = 0 or nm = 0 then 
            return false;
        fi;
      
        m := Maximum(DimensionVector(M));
        n := Maximum(DimensionVector(N));
        if n = m then
            l := n;
        else
            l := Minimum([n,m]) + 1;
        fi;
      
        zero := ZeroMapping(M,M);
      
        for j in [1..nm] do
            for i in [1..mn] do
                if l>1 then  # because hom^0*hom => error!       
                    f := (HomMN[i]*HomNM[j])^(l-1)*HomMN[i];
                else 
                    f := HomMN[i];
                fi;
                
                fnm := f*HomNM[j];
              
                if fnm <> zero then
                    nmf := HomNM[j]*f; 
                    return [fnm, nmf, HomMN[i]];
                fi;
            od;
        od;

        return false;
    end; 
    
    maps := splittingmaps(M,N);
    #
    # No common non-zero direct summand,  M  and  N  are not isomorphic,
    # return false.
    #
    if maps = false then
        return false;
    fi;
    #
    # M  and  N  has a common non-zero direct summand, find the splitting of
    # the inclusion of this common direct summand in  M  and in  N.
    #
    fn := ImageInclusion(maps[1]);
#    fsplit := IsSplitMonomorphism(fn);
    fsplit := RightInverseOfHomomorphism(fn);
    gn := ImageInclusion(maps[2]);
#    gsplit := IsSplitMonomorphism(gn);
#     gsplit := LeftInverseOfHomomorphism(gn);    
    gsplit := RightInverseOfHomomorphism(gn);    
    #
    # Find the idempotents corresponding to these direct summands.
    #
    f_alpha := ImageInclusion(fsplit*fn);
    f_beta := KernelInclusion(fsplit*fn);    
    g_alpha := ImageInclusion(gsplit*gn);
    g_beta := KernelInclusion(gsplit*gn);
    #
    # If this direct summand is all of  M  and  N, return the appropriate
    # isomorphism.
    #
    if Dimension(Source(f_beta)) = 0 and Dimension(Source(g_beta)) = 0 then
       return maps[1]*maps[3]; 
    fi;
    #
    # If this direct summand is not all of  M  and  N, recursively construct
    # a possible isomorphism between the complements of this common direct 
    # summand in  M  and in  N.
    #
    q := f_alpha*maps[1]*maps[3];
    
    B := BasisVectors(Basis(Source(q)));
    genImages := List(B, x -> PreImagesRepresentative(g_alpha, ImageElm(q,x)));
    
    delta := HomomorphismFromImages(Source(f_alpha), Source(g_alpha), genImages);
    
    recursiveIOM := IsomorphismOfModules(Source(f_beta),Source(g_beta));
    
    if recursiveIOM <> false then
#        return IsSplitMonomorphism(f_alpha) * (delta * g_alpha) + 
#               IsSplitMonomorphism(f_beta) * (recursiveIOM * g_beta);
        return RightInverseOfHomomorphism(f_alpha) * (delta * g_alpha) + 
               RightInverseOfHomomorphism(f_beta) * (recursiveIOM * g_beta);        
    else 
        return false;
    fi;
end
  ); # IsomorphismOfModules

#######################################################################
##
#O  EndOfModuleAsQuiverAlgebra( <M> )
##
##  The argument of this function is a PathAlgebraMatModule  <M>  over
##  a quiver algebra over a field  K. It checks if the endomorphism ring
##  of  <M>  is  K-elementary (not necessary for it to be a quiver algebra, 
##  but this is a TODO improvement). If the endomorphism ring of  <M>  is
##  K-elementary, then it returns a list of three elements, (i) the 
##  endomorphism ring of  <M>, (ii) the adjacency matrix of the quiver of 
##  the endomorphism ring and (iii) the endomorphism ring as a quiver 
##  algebra.
##  
InstallMethod( EndOfModuleAsQuiverAlgebra, 
    "for a representations of a quiver",
    [ IsPathAlgebraMatModule ], 0,
    function( M )

    local decompmult, EndM, pids, rad, gensofrad, radEndM, map, A, elementarytest, i, temp, 
          gensofradsquare, radsquare, radmap, radmodradsq, Iradmodradsq,
          gensforarrows, adjacencymatrix, j, loewylength, Q, KQ, Jtplus1, AA, n, gensofAA, 
          gb, gbb, vertices, arrows, images, arrow_count, f, t, B, b, tempx, length,
          walk, image, matrix, solutions, Solutions, radSoluplusSolurad, V, W, h, idealgens;
    #
    #  Is the module basic?
    #
    decompmult := DecomposeModuleWithMultiplicities(M)[2];
    if not ForAll(decompmult, x -> x = 1) then 
        Error("the entered module is not basic, so the endomorphism ring is not a quotient of a path algebra,\n");
    fi;
    #
    #  Finding the primitive idempotents in the endomorphism ring of  M.
    #  Here we need an algebra over a finite field.
    #
    EndM := EndOverAlgebra(M);
    pids := IdempotentsForDecomposition(EndM);
    #
    #  Assume that the algebra over which  M  is a module is a  k-algebra.
    #
    #  Checking if the endomorphism ring of  M  modulo the radical is isomorphic to  k^t
    #  for some  t, i.e. if EndM is a k-elementary algebra.
    #
    rad := RadicalOfAlgebra(EndM);
    gensofrad := GeneratorsOfAlgebra(rad);
    radEndM := Ideal(EndM,gensofrad); 
    map := NaturalHomomorphismByIdeal(EndM,radEndM); 
    A := Range(map);
    #
    #  Computing the dimension of the endomorphism rings of the simple  EndM-modules.
    # 
    elementarytest := List([1..Length(pids)], x -> Filtered(ImageElm(map,pids[x])*BasisVectors(Basis(A)), y -> y <> Zero(y)));
    elementarytest := List([1..Length(pids)], x -> Filtered(elementarytest[x]*ImageElm(map,pids[x]), y -> y <> Zero(y)));
    elementarytest := List(elementarytest, x -> Dimension(Subspace(A,x)));
    # 
    #  Checking if the endomorphism ring of the module  M  is  k-elementary. 
    #    
    if not ForAll(elementarytest, x -> x = 1) then
        Error("the endomorphism algebra of the entered module is not K-elementary, that is, not a quotient of a path K-algebra, \n");
    fi;
    #  
    #  Finding the arrows. 
    #  Finding the generators of the square of the radical in  EndM.
    #
    temp := [];
    gensofradsquare := [];
    if Length(gensofrad) > 0 then
        for i in [1..Length(gensofrad)] do
    	    Append(temp, Filtered(gensofrad[i]*BasisVectors(Basis(EndM)), x -> x <> Zero(x)));
    	od;
        temp := Unique(temp); #  contains generators of the radical as a left ideal.
	for i in [1..Length(gensofrad)] do
            Append(gensofradsquare, Filtered(temp*gensofrad[i], x -> x <> Zero(x)));
    	od;
        gensofradsquare := Unique(gensofradsquare); # generators of the square of the radical in  EndM.
        #
    	#  Defining the square of the radical as an ideal inside  EndM, and finding the natural map 
    	#  EndM --->  EndM/radsquare.
    	#	
    	radsquare := Ideal(EndM, gensofradsquare); 
    	radmap := NaturalHomomorphismByIdeal(EndM, radsquare);
    	radmodradsq := Unique(List(gensofrad, x -> ImageElm(radmap,x)));
    	#
    	#  Defining the image of the radical in  EndM  as an ideal in  EndM/radsquare.
    	#
    	Iradmodradsq := Ideal(Range(radmap),radmodradsq);
    	#
	#  Constructing generators for the arrows in  EndM  and the adjacency matrix of 
	#  the quiver of the elementary algebra  EndM.
    	#  		
    	gensforarrows := List([1..Length(pids)], x -> List([1..Length(pids)], y -> []));
    	adjacencymatrix := NullMat(Length(pids),Length(pids));
    	for i in [1..Length(pids)] do
            for j in [1..Length(pids)] do
            	gensforarrows[i][j] := Filtered(ImageElm(radmap,pids[i])*BasisVectors(Basis(Iradmodradsq))*ImageElm(radmap,pids[j]), y -> y <> Zero(y));
            	gensforarrows[i][j] := BasisVectors(Basis(Subspace(Range(radmap),gensforarrows[i][j])));
            	gensforarrows[i][j] := List(gensforarrows[i][j], x -> PreImagesRepresentative(radmap,x));
            	adjacencymatrix[i][j] := Length(gensforarrows[i][j]);
        	od; 
    	od;
    	#
    	t := Length(RadicalSeriesOfAlgebra(EndM)) - 1; # then radEndM^t = (0) 
    	Q := Quiver(adjacencymatrix);
    	KQ := PathAlgebra(LeftActingDomain(M),Q);
    	Jtplus1 := NthPowerOfArrowIdeal(KQ,t + 1);
    	n := NumberOfVertices(Q);
    	images := ShallowCopy(pids);   #  images of the vertices/trivial paths
    	arrow_count := 0;
    	for i in [ 1 .. n ] do
            for j in [ 1 .. n ] do
            	Append(images,gensforarrows[i][j]);
        	od;
    	od;
    else
	adjacencymatrix := NullMat(Length(pids), Length(pids));
	Q := Quiver(adjacencymatrix);
	KQ := PathAlgebra(LeftActingDomain(M),Q);
	return [EndM, adjacencymatrix, KQ];
    fi;
    #
    #  If there are some arrows, continue.
    #
    if Length(Jtplus1) = 0 then 
        AA := KQ;
        gensofAA := GeneratorsOfAlgebra(AA);
        vertices := gensofAA{[1..n]};
        arrows := gensofAA{[n + 1..Length(gensofAA)]};
        f := [AA,EndM,gensofAA{[1..Length(gensofAA)]},images];

    else
        gb := GBNPGroebnerBasis(Jtplus1,KQ);
        Jtplus1 := Ideal(KQ,Jtplus1); 
        gbb := GroebnerBasis(Jtplus1,gb);
        AA := KQ/Jtplus1;
        gensofAA := GeneratorsOfAlgebra(AA);
        vertices := gensofAA{[2..n + 1]};
        arrows := gensofAA{[n + 2..Length(gensofAA)]};
        f := [AA,EndM,gensofAA{[2..Length(gensofAA)]},images];
    fi;
    #
    #  Define  A := KQ/J^(t + 1), where t = loewylength, and in addition define 
    #  f : A ---> EndM. Find this as a linear map and find the kernel, and
    #  construct the relations from this.
    #
    #  First giving the ring surjection  A ---> EndM  as a linear map.  Stored
    #  in the matrix called  <matrix>.
    #
    B := BasisVectors(Basis(AA)); 
    matrix := []; 
    for b in B do
        if IsPathAlgebra(AA) then 
            temp := CoefficientsAndMagmaElements(b);
        else 
            temp := CoefficientsAndMagmaElements(b![1]);
        fi;
        n := Length(temp)/2;
        tempx := Zero(f[2]);
        for i in [0..n - 1] do
            # for each term compute the image. 
            walk := WalkOfPath(temp[2*i + 1]); 
            length := Length(walk); 
            image := One(EndM); 
            if length = 0 then 
                image := image*f[4][Position(f[3],temp[2*i + 1]*One(AA))];
            else 
                for j in [1..length] do
                    image := image*f[4][Position(f[3],walk[j]*One(AA))];
                od;
            fi;
            tempx := tempx + temp[2*i + 2]*image;
        od;
        Add(matrix, Coefficients(Basis(f[2]),tempx));
    od;    
    #
    #  Finding a vector space basis for the kernel of the ring surjection  A ---> EndM.
    #
    solutions := NullspaceMat(matrix);
    Solutions := List(solutions, x -> LinearCombination(B,x));  # solutions as elements in  AA.
    #
    #  Finding a generating set for  J(Ker f) + (ker f)J. 
    #
    radSoluplusSolurad := List(arrows, x -> Filtered(Solutions*x, y -> y <> Zero(y)));
    Append(radSoluplusSolurad,List(arrows, x -> Filtered(x*Solutions, y -> y <> Zero(y))));
    radSoluplusSolurad := Flat(radSoluplusSolurad);
    V := Ideal(AA, Solutions);
    W := Subspace(V, radSoluplusSolurad);
    h := NaturalHomomorphismBySubspace(V,W);  
    #
    #  Constructing the relations in  KQ.
    # 
    idealgens := List(BasisVectors(Basis(Range(h))), x -> PreImagesRepresentative(h,x));
    #
    #  Lifting the relations back to  KQ  and returning the answer.
    #
    if not IsPathAlgebra(AA) then
        idealgens := List(idealgens, x -> x![1]);
    fi;
    if Length(idealgens) = 0 then 
        return [EndM, adjacencymatrix, AA];
    else
        return [EndM, adjacencymatrix, KQ/idealgens];
    fi;
end
  );


#######################################################################
##
#O  TraceOfModule( <M>, <N> )
##
##  This function computes trace of the module  <M>  in  <N> by doing 
##  the following: computes a basis for Hom_A(M,N) and then computes 
##  the sum of all the images of the elements in this basis. This is 
##  also a minimal right Fac(M)-approximation. 
##
InstallMethod( TraceOfModule,
    "for two PathAlgebraMatModules",
    [ IsPathAlgebraMatModule, IsPathAlgebraMatModule ],
    
    function( M, N )
    local homMN, B, trace;
    #
    # Checking if the modules  <M>  and  <N>  are modules over the same algebra.
    #
    if RightActingAlgebra(M) <> RightActingAlgebra(N) then
        Error("the entered modules are not modules over the same algebra,\n");
    fi;
    #
    # If  <M>  or  <N>  are zero, then the trace is the zero module.
    #
    if M = ZeroModule(RightActingAlgebra(M)) or N = ZeroModule(RightActingAlgebra(N)) then
        return ZeroMapping(ZeroModule(RightActingAlgebra(N)),N);
    fi;
    #
    # Finding a basis for  Hom(M,N), and taking the sum of all the images of these
    # homomorphisms from  <M>  to  <N>.  This is the trace of  <M>  in  <N>. 
    #
    homMN := HomOverAlgebra(M,N);
    if Length(homMN) = 0 then
        return ZeroMapping(ZeroModule(RightActingAlgebra(N)),N);
    fi;
    B := BasisVectors(Basis(M));
    trace := Flat(List(B, b -> List(homMN, f -> ImageElm(f,b))));
    
    return SubRepresentationInclusion(N,trace);
end
  );

#######################################################################
##
#O  RejectOfModule( <N>, <M> )
##
##  This function computes the reject of the module  <M>  in  <N> by doing 
##  the following: computes a basis for Hom_A(N,M) and then computes 
##  the intersections of all the kernels of the elements in this basis. 
##
InstallMethod( RejectOfModule,
    "for two PathAlgebraMatModules",
    [ IsPathAlgebraMatModule, IsPathAlgebraMatModule ],
    
    function( N, M )

    local  homNM;
    #
    # Checking if the modules  <M>  and  <N>  are modules over the same algebra.
    #
    if RightActingAlgebra(M) <> RightActingAlgebra(N) then
        Error("the entered modules are not modules over the same algebra,\n");
    fi;
    #
    # If  Hom( N, M )  is zero, then the reject is the identity homomorphism N ---> N.
    #
    homNM := HomOverAlgebra( N, M );
    if Length( homNM ) = 0 then 
        return IdentityMapping( N );
    fi;
    #
    # Using the basis we found for  Hom( N, M )  above, and taking the intersection of 
    # all the kernels of these homomorphisms in the basis.  This is the reject of  
    # <M>  in  <N>.
    #
    if Length( homNM ) = 1 then
       return KernelInclusion( homNM[ 1 ] );
    fi;
    if Length( homNM ) > 1 then
	return IntersectionOfSubmodules( List( homNM, f -> KernelInclusion( f ) ) );
    fi;
end
  );
