#############################################################################
##
##  HomalgToCAS.gi           HomalgToCAS package             Mohamed Barakat
##
##  Copyright 2007-2010 Lehrstuhl B für Mathematik, RWTH Aachen
##
##  Implementation stuff for HomalgToCAS.
##
#############################################################################

####################################
#
# global variables:
#
####################################

# a central place for configuration variables:

InstallValue( HOMALG_IO,
        rec(
            show_banners := true,
            variable_name := "homalg_variable_",
            InformAboutCASystemsWithoutActiveRings := true,
            SaveHomalgMaximumBackStream := false,
            color_display := false,
            DirectoryForTemporaryFiles := "/tmp/",
            DoNotFigureOutAnAlternativeDirectoryForTemporaryFiles := false,
            DoNotDeleteTemporaryFiles := false,
            ListOfAlternativeDirectoryForTemporaryFiles := [ "/tmp/", "/dev/shm/", "/var/tmp/", "./" ],
            FileNameCounter := 1,
            DeletePeriod := 500,
            PID := IO_getpid(),
            save_CAS_commands_to_file := false,
            suppress_CAS := false,
            suppress_PID := false,
              ##  <#GAPDoc Label="Pictograms">
              ##  <ManSection>
              ##    <Var Name="HOMALG_IO.Pictograms"/>
              ##    <Description>
              ##      The record of pictograms is a component of the record <C>HOMALG_IO</C>.
              ##    <Listing Type="Code"><![CDATA[
            Pictograms := rec(
                
                ##
                ## colors:
                ##
                
                ## pictogram color of a "need_command" or assignment operation:
                color_need_command                      := "\033[1;33;44m",
                
                ## pictogram color of a "need_output" or "need_display" operation:
                color_need_output                       := "\033[1;34;43m",
                
                ##
                ## good morning computer algebra system:
                ##
                
                ## initialize:
                initialize                              := "ini",
                
                ## define macros:
                define                                  := "def",
                
                ## get time:
                time                                    := ":ms",
                
                ## memory usage:
                memory                                  := "mem",
                
                ## unknown:
                unknown                                 := "???",
                
                ##
                ## external garbage collection:
                ##
                
                ## delete a variable:
                delete                                  := "xxx",
                
                ## delete serveral variables:
                multiple_delete                         := "XXX",
                
                ## trigger the garbage collector:
                garbage_collector                       := "grb",
                
                ##
                ## create lists:
                ##
                
                ## define a list:
                CreateList                              := "lst",
                
                ##
                ## create rings:
                ##
                
                ## define a ring:
                CreateHomalgRing                        := "R:=",
                
                ## get the names of the "variables" defining the ring:
                variables                               := "var",
                
                ## define zero:
                Zero                                    := "0:=",
                
                ## define one:
                One                                     := "1:=",
                
                ## define minus one:
                MinusOne                                := "-:=",
                
                ##
                ## mandatory ring operations:
                ##
                
                ## get the name of an element:
                ## (important if the CAS pretty-prints ring elements,
                ##  we need names that can be used as input!)
                ## (install a method instead of a homalgTable entry)
                homalgSetName                           := "\"a\"",
                
                ## a = 0 ?
                IsZero                                  := "a=0",
                
                ## a = 1 ?
                IsOne                                   := "a=1",
                
                ## substract two ring elements
                ## (needed by SimplerEquivalentMatrix in case
                ##  CopyRow/ColumnToIdentityMatrix are not defined):
                Minus                                   := "a-b",
                
                ## divide the element a by the unit u
                ## (needed by SimplerEquivalentMatrix in case
                ##  DivideEntryByUnit is not defined):
                DivideByUnit                            := "a/u",
                
                ## important ring operations:
                ## (important for performance since existing
                ##  fallback methods cause a lot of traffic):
                
                ## is u a unit?
                ## (mainly needed by the fallback methods for matrices, see below):
                IsUnit                                  := "?/u",
                
                ##
                ## optional ring operations:
                ##
                
                ## copy an element:
                CopyElement                             := "a>a",
                
                ## add two ring elements:
                Sum                                     := "a+b",
                
                ## multiply two ring elements:
                Product                                 := "a*b",
                
                ## the (greatest) common divisor:
                Gcd                                     := "gcd",
                
                ## cancel the (greatest) common divisor:
                CancelGcd                               := "ccd",
                
                ## random polynomial:
                RandomPol                               := "rpl",
                
                ## numerator:
                Numerator                               := "num",
                
                ## denominator:
                Denominator                             := "den",
                
                ## evaluate polynomial:
                Evaluate                                := "evl",
                
                ## degree of a multivariate polynomial
                DegreeOfRingElement                     := "deg",
                
                ## is irreducible:
                IsIrreducible                           := "irr",
                
                ##
                ## create matrices:
                ##
                
                ## define a matrix:
                HomalgMatrix                            := "A:=",
                
                ## copy a matrix:
                CopyMatrix                              := "A>A",
                
                ## load a matrix from file:
                LoadHomalgMatrixFromFile                := "A<<",
                
                ## save a matrix to file:
                SaveHomalgMatrixToFile                  := "A>>",
                
                ## get a matrix entry as a string:
                MatElm                  := "<ij",
                
                ## set a matrix entry from a string:
                SetMatElm                  := ">ij",
                
                ## add to a matrix entry from a string:
                AddToMatElm                := "+ij",
                
                ## get a list of the matrix entries as a string:
                GetListOfHomalgMatrixAsString           := "\"A\"",
                
                ## get a listlist of the matrix entries as a string:
                GetListListOfHomalgMatrixAsString       := "\"A\"",
                
                ## get a "sparse" list of the matrix entries as a string:
                GetSparseListOfHomalgMatrixAsString     := ".A.",
                
                ## assign a "sparse" list of matrix entries to a variable:
                sparse                                  := "spr",
                
                ## list of assumed inequalities:
                Inequalities                            := "<>0",
                
                ## list of assumed inequalities:
                MaximalIndependentSet                   := "idp",
                
                ##
                ## mandatory matrix operations:
                ##
                
                ## test if a matrix is the zero matrix:
                ## CAUTION: the external system must be able to check
                ##          if the matrix is zero modulo possible ring relations
                ##          only known to the external system!
                IsZeroMatrix                            := "A=0",
                
                ## number of rows:
                NrRows                                  := "#==",
                
                ## number of columns:
                NrColumns                               := "#||",
                
                ## determinant of a matrix over a (commutative) ring:
                Determinant                             := "det",
                
                ## create a zero matrix:
                ZeroMatrix                              := "(0)",
                
                ## create a initial zero matrix:
                InitialMatrix                           := "[0]",
                
                ## create an identity matrix:
                IdentityMatrix                          := "(1)",
                
                ## create an initial identity matrix:
                InitialIdentityMatrix                   := "[1]",
                
                ## "transpose" a matrix (with "the" involution of the ring):
                Involution                              := "A^*",
                
                ## get certain rows of a matrix:
                CertainRows                             := "===",
                
                ## get certain columns of a matrix:
                CertainColumns                          := "|||",
                
                ## stack to matrices vertically:
                UnionOfRows                             := "A_B",
                
                ## glue to matrices horizontally:
                UnionOfColumns                          := "A|B",
                
                ## create a block diagonal matrix:
                DiagMat                                 := "A\\B",
                
                ## the Kronecker (tensor) product of two matrices:
                KroneckerMat                            := "AoB",
                
                ## multiply a ring element with a matrix:
                MulMat                                  := "a*A",
                
                ## multiply a matrix with a ring element:
                MulMatRight                             := "A*a",
                
                ## add two matrices:
                AddMat                                  := "A+B",
                
                ## substract two matrices:
                SubMat                                  := "A-B",
                
                ## multiply two matrices:
                Compose                                 := "A*B",
                
                ## pullback a matrix by a ring map:
                Pullback                                := "pbk",
                
                ##
                ## important matrix operations:
                ## (important for performance since existing
                ##  fallback methods cause a lot of traffic):
                ##
                
                ## test if two matrices are equal:
                ## CAUTION: the external system must be able to check
                ##          equality of the two matrices modulo possible ring relations
                ##          only known to the external system!
                AreEqualMatrices                        := "A=B",
                
                ## test if a matrix is the identity matrix:
                IsIdentityMatrix                        := "A=1",
                
                ## test if a matrix is diagonal (needed by the display method):
                IsDiagonalMatrix                        := "A=\\",
                
                ## get the positions of the zero rows:
                ZeroRows                                := "0==",
                
                ## get the positions of the zero columns:
                ZeroColumns                             := "0||",
                
                ## get "column-independent" unit positions
                ## (needed by ReducedBasisOfModule):
                GetColumnIndependentUnitPositions       := "ciu",
                
                ## get "row-independent" unit positions
                ## (needed by ReducedBasisOfModule):
                GetRowIndependentUnitPositions          := "riu",
                
                ## get the position of the "first" unit in the matrix
                ## (needed by SimplerEquivalentMatrix):
                GetUnitPosition                         := "gup",
                
                ## position of the first non-zero entry per row
                PositionOfFirstNonZeroEntryPerRow       := "fnr",
                
                ## position of the first non-zero entry per column
                PositionOfFirstNonZeroEntryPerColumn    := "fnc",
                
                ## indicator matrix of non-zero entries
                IndicatorMatrixOfNonZeroEntries         := "<>0",
                
                ## transposed matrix:
                TransposedMatrix                        := "^tr",
                
                ## divide an entry of a matrix by a unit
                ## (needed by SimplerEquivalentMatrix in case
                ##  DivideRow/ColumnByUnit are not defined):
                DivideEntryByUnit                       := "ij/",
                
                ## divide a row by a unit
                ## (needed by SimplerEquivalentMatrix):
                DivideRowByUnit                         := "-/u",
                
                ## divide a column by a unit
                ## (needed by SimplerEquivalentMatrix):
                DivideColumnByUnit                      := "|/u",
                
                ## divide a row by a unit
                ## (needed by SimplerEquivalentMatrix):
                CopyRowToIdentityMatrix                 := "->-",
                
                ## divide a column by a unit
                ## (needed by SimplerEquivalentMatrix):
                CopyColumnToIdentityMatrix              := "|>|",
                
                ## set a column (except a certain row) to zero
                ## (needed by SimplerEquivalentMatrix):
                SetColumnToZero                         := "|=0",
                
                ## get the positions of the rows with a single one
                ## (needed by SimplerEquivalentMatrix):
                GetCleanRowsPositions                   := "crp",
                
                ## convert a single row matrix into a matrix
                ## with specified number of rows/columns
                ## (needed by the display methods for homomorphisms):
                ConvertRowToMatrix                      := "-%A",
                
                ## convert a single column matrix into a matrix
                ## with specified number of rows/columns
                ## (needed by the display methods for homomorphisms):
                ConvertColumnToMatrix                   := "|%A",
                
                ## convert a matrix into a single row matrix:
                ConvertMatrixToRow                      := "A%-",
                
                ## convert a matrix into a single column matrix:
                ConvertMatrixToColumn                   := "A%|",
                
                ##
                ## basic matrix operations:
                ##
                
                ## compute a (r)educed (e)chelon (f)orm:
                ReducedEchelonForm                      := "ref",
                
                ## compute a "(bas)is" of a given set of module elements:
                BasisOfModule                           := "bas",
                
                ## compute a reduced "(Bas)is" of a given set of module elements:
                ReducedBasisOfModule                    := "Bas",
                
                ## (d)e(c)ide the ideal/submodule membership problem,
                ## i.e. if an element is (0) modulo the ideal/submodule:
                DecideZero                              := "dc0",
                
                ## compute a generating set of (syz)ygies:
                SyzygiesGenerators                      := "syz",
                
                ## compute a generating set of reduced (Syz)ygies:
                ReducedSyzygiesGenerators               := "Syz",
                
                ## compute a (R)educed (E)chelon (F)orm
                ## together with the matrix of coefficients:
                ReducedEchelonFormC                     := "REF",
                
                ## compute a "(BAS)is" of a given set of module elements
                ## together with the matrix of coefficients:
                BasisCoeff                              := "BAS",
                
                ## (D)e(C)ide the ideal/submodule membership problem,
                ## i.e. write an element effectively as (0) modulo the ideal/submodule:
                DecideZeroEffectively                   := "DC0",
                
                ##
                ## optional matrix operations:
                ##
                
                ## Hilbert-Poincare series of a module:
                HilbertPoincareSeries                   := "HPs",
                
                ## Hilbert polynomial of a module:
                HilbertPolynomial                       := "Hil",
                
                ## affine dimension of a module:
                AffineDimension                         := "dim",
                
                ## affine degree of a module:
                AffineDegree                            := "adg",
                
                ## the constant term of the hilbert polynomial:
                ConstantTermOfHilbertPolynomial         := "P_0",
                
                ## primary decomposition:
                PrimaryDecomposition                    := "YxZ",
                
                ## eliminate variables:
                Eliminate                               := "eli",
                
                ## leading module:
                LeadingModule                           := "led",
                
                ## the i-th monomial matrix
                MonomialMatrix                          := "mon",
                
                ## matrix of symbols:
                MatrixOfSymbols                         := "smb",
                
                ## leading module:
                ## coefficients:
                Coefficients                            := "cfs",
                
                ##
                ## optional module operations:
                ##
                
                ## compute a better equivalent matrix
                ## (field -> row+col Gauss, PIR -> Smith, Dedekind domain -> Krull, etc ...):
                BestBasis                               := "(\\)",
                
                ## compute elementary divisors:
                ElementaryDivisors                      := "div",
                
                ##
                ## for the eye:
                ##
                
                ## display objects:
                Display                                 := "dsp",
                
                ## the LaTeX code of the mathematical entity:
                homalgLaTeX                             := "TeX",
                
              )
              ##  ]]></Listing>
              ##    </Description>
              ##  </ManSection>
              ##  <#/GAPDoc>
           )
);

####################################
#
# global functions and operations:
#
####################################

##
InstallGlobalFunction( homalgTime,
  function( arg )
    local nargs, st, object, stream, t;
    
    nargs := Length( arg );
    
    if nargs = 0 then
        return homalgTotalRuntimes( );
    elif nargs = 1 then
        st := arg[1];
        if IsInt( st ) then
            return homalgTotalRuntimes( ) - st;
        fi;
        return homalgTime( st, 0 );
    fi;
    
    ## we now know thar nargs > 1
    
    object := arg[1];
    
    if IsRecord( object ) then
        if not ( IsBound( object.lines ) and IsBound( object.pid ) ) then
            Error( "the first argument is a record but not a stream\n" );
        fi;
        stream := object;
    else
        stream := homalgStream( object );
    fi;
    
    t := arg[2];
    
    if not IsInt( t ) then
        Error( "the second argument must be an integer\n" );
    fi;
    
    if not IsBound( stream.time ) then
        Error( "the stream does not include a component called \"time\"\n" );
    fi;
    
    return stream.time( stream, t );
    
end );

##
InstallGlobalFunction( homalgMemoryUsage,
  function( arg )
    local nargs, o, object, stream;
    
    nargs := Length( arg );
    
    if nargs = 0 then
        Error( "empty input\n" );
    elif nargs = 1 then
        o := arg[1];
        return homalgMemoryUsage( o, 0 );
    fi;
    
    ## we now know thar nargs > 1
    
    object := arg[1];
    
    if IsRecord( object ) then
        if not ( IsBound( object.lines ) and IsBound( object.pid ) ) then
            Error( "the first argument is a record but not a stream\n" );
        fi;
        stream := object;
    else
        stream := homalgStream( object );
    fi;
    
    o := arg[2];
    
    if not IsInt( o ) then
        Error( "the second argument must be an integer\n" );
    fi;
    
    if not IsBound( stream.memory_usage ) then
        Error( "the stream does not include a component called \"memory_usage\"\n" );
    fi;
    
    return stream.memory_usage( stream, o );
    
end );

##
InstallGlobalFunction( FigureOutAnAlternativeDirectoryForTemporaryFiles,
  function( arg )
    local nargs, file, list, separator, pos_sep, l, directory, filename, fs;
    
    nargs := Length( arg );
    
    if nargs = 0 then
        Error( "empty input" );
    fi;
    
    file := arg[1];
    
    if IsBound( HOMALG_IO.ListOfAlternativeDirectoryForTemporaryFiles ) then
        list := HOMALG_IO.ListOfAlternativeDirectoryForTemporaryFiles;
    else
        list := [ "/tmp/", "/dev/shm/", "/var/tmp/" ];
    fi;
    
    ## figure out the directory separtor:
    if IsBound( GAPInfo.UserHome ) then
        separator := GAPInfo.UserHome[1];
    else
        separator := '/';
    fi;
    
    if nargs > 1 then
        pos_sep := PositionProperty( Reversed( file ), c -> c = separator );
        if pos_sep <> fail then
            l := Length( file );
            file := file{[ l - pos_sep + 2 .. l ]};
        fi;
    fi;
    
    for directory in list do
        
        if directory[Length( directory )] <> separator then
            filename := Concatenation( directory, [ separator ], file );
        else
            filename := Concatenation( directory, file );
        fi;
        
        fs := IO_File( filename, "w" );
        
        if fs <> fail then
            IO_Close( fs );
            if nargs > 1 and arg[2] = "with_filename" then
                return filename;
            else
                return directory;
            fi;
        fi;
        
    od;
    
    return fail;
    
end );

##  <#GAPDoc Label="homalgIOMode">
##  <ManSection>
##    <Func Arg="str[, str2[, str3]]" Name="homalgIOMode"/>
##    <Description>
##      This function sets different modes which influence how much of the communication becomes visible.
##      Handling the string <A>str</A> is <E>not</E> case-sensitive. <C>homalgIOMode</C> invokes
##      the global function <C>homalgMode</C> defined in the &homalg; package with an <Q>appropriate</Q> argument (see code below).
##      Alternatively, if a second or more strings are given, then <C>homalgMode</C> is invoked with the remaining strings
##      <A>str2</A>, <A>str3</A>, ... at the end. In particular, you can use <C>homalgIOMode</C>( <A>str</A>, "" ) to reset the effect
##      of invoking <C>homalgMode</C>.
##      <Table Align="l|c|l">
##      <Row>
##        <Item><A>str</A></Item>
##        <Item><A>str</A> (long form)</Item>
##        <Item>mode description</Item>
##      </Row>
##      <HorLine/>
##      <Row><Item></Item><Item></Item><Item></Item></Row>
##      <Row>
##        <Item>""</Item>
##        <Item>""</Item>
##        <Item>the default mode, i.e. the communication protocol won't be visible</Item>
##      </Row>
##      <Row>
##        <Item></Item>
##        <Item></Item>
##        <Item>(<C>homalgIOMode</C>( ) is a short form for <C>homalgIOMode</C>( "" ))</Item>
##      </Row>
##      <Row><Item></Item><Item></Item><Item></Item></Row>
##      <Row>
##        <Item>"a"</Item>
##        <Item>"all"</Item>
##        <Item>combine the modes "debug" and "file"</Item>
##      </Row>
##      <Row><Item></Item><Item></Item><Item></Item></Row>
##      <Row>
##        <Item>"b"</Item>
##        <Item>"basic"</Item>
##        <Item>the same as "picto" + <C>homalgMode</C>( "basic" )</Item>
##      </Row>
##      <Row><Item></Item><Item></Item><Item></Item></Row>
##      <Row>
##        <Item>"d"</Item>
##        <Item>"debug"</Item>
##        <Item>view the complete communication protocol</Item>
##      </Row>
##      <Row><Item></Item><Item></Item><Item></Item></Row>
##      <Row>
##        <Item>"f"</Item>
##        <Item>"file"</Item>
##        <Item>dump the communication protocol into a file with the name</Item>
##      </Row>
##      <Row>
##        <Item></Item>
##        <Item></Item>
##        <Item><C>Concatenation</C>( "commands_file_of_", CAS, "_with_PID_", PID )</Item>
##      </Row>
##      <Row><Item></Item><Item></Item><Item></Item></Row>
##      <Row>
##        <Item>"p"</Item>
##        <Item>"picto"</Item>
##        <Item>view the abbreviated communication protocol</Item>
##      </Row>
##      <Row>
##        <Item></Item>
##        <Item></Item>
##        <Item>using the preassigned pictograms</Item>
##      </Row>
##      <Row><Item></Item><Item></Item><Item></Item></Row>
##      <HorLine/>
##      </Table>
##      All modes other than the "default"-mode only set their specific values and leave
##      the other values untouched, which allows combining them to some extent. This also means that
##      in order to get from one mode to a new mode (without the aim to combine them)
##      one needs to reset to the "default"-mode first. <Br/><Br/>
##      <E>Caution</E>:
##      <List>
##        <Item>In case you choose one of the modes "file" or "all" you might want to set
##          the global variable <C>HOMALG_IO.DoNotDeleteTemporaryFiles</C> := <C>true</C>;
##          this is only important if during the computations some matrices get converted via files
##          (using <C>ConvertHomalgMatrixViaFile</C>), as reading these files will be part of the protocol!</Item>
##        <Item>It makes sense for the dumped communication protocol to be (re)executed with the respective external system,
##          only in case the latter is deterministic (i.e. same-input-same-output).</Item>
##      </List>
##      <Listing Type="Code"><![CDATA[
InstallGlobalFunction( homalgIOMode,
  function( arg )
    local nargs, mode, s;
    
    nargs := Length( arg );
    
    if nargs = 0 or ( IsString( arg[1] ) and arg[1] = "" ) then
        mode := "default";
    elif IsString( arg[1] ) then	## now we know, the string is not empty
        s := arg[1];
        if LowercaseString( s{[1]} ) = "a" then
            mode := "all";
        elif LowercaseString( s{[1]} ) = "b" then
            mode := "basic";
        elif LowercaseString( s{[1]} ) = "d" then
            mode := "debug";
        elif LowercaseString( s{[1]} ) = "f" then
            mode := "file";
        elif LowercaseString( s{[1]} ) = "p" then
            mode := "picto";
        else
            mode := "";
        fi;
    else
        Error( "the first argument must be a string\n" );
    fi;
    
    if mode = "default" then
        ## reset to the default values
        HOMALG_IO.color_display := false;
        HOMALG_IO.show_banners := true;
        HOMALG_IO.save_CAS_commands_to_file := false;
        HOMALG_IO.DoNotDeleteTemporaryFiles := false;
        HOMALG_IO.SaveHomalgMaximumBackStream := false;
        HOMALG_IO.InformAboutCASystemsWithoutActiveRings := true;
        SetInfoLevel( InfoHomalgToCAS, 1 );
        homalgMode( );
    elif mode = "all" then
        homalgIOMode( "debug" );
        homalgIOMode( "file" );
    elif mode = "basic" then
        HOMALG_IO.color_display := true;
        HOMALG_IO.show_banners := true;
        SetInfoLevel( InfoHomalgToCAS, 4 );
        homalgMode( "basic" );	## use homalgIOMode( "basic", "" ) to reset
    elif mode = "debug" then
        HOMALG_IO.color_display := true;
        HOMALG_IO.show_banners := true;
        SetInfoLevel( InfoHomalgToCAS, 8 );
        homalgMode( "debug" );	## use homalgIOMode( "debug", "" ) to reset
    elif mode = "file" then
        HOMALG_IO.save_CAS_commands_to_file := true;
    elif mode = "picto" then
        HOMALG_IO.color_display := true;
        HOMALG_IO.show_banners := true;
        SetInfoLevel( InfoHomalgToCAS, 4 );
        homalgMode( "logic" );	## use homalgIOMode( "picto", "" ) to reset
    fi;
    
    if nargs > 1 and IsString( arg[2] ) then
        CallFuncList( homalgMode, arg{[ 2 .. nargs ]} );
    fi;
    
end );
##  ]]></Listing>
##    </Description>
##    <#Include Label="homalgSendBlocking:view_communication">
##  </ManSection>
##  <#/GAPDoc>

##
InstallGlobalFunction( ApplyCommandToString,
  function( arg )
    local nargs, cmd, str, separator, directory, pointer, pid, file, filename, fs, output;
    
    nargs := Length( arg );
    
    if nargs = 0 then
        Error( "no arguments provided\n" );
    elif IsString( arg[1] ) then
        cmd := arg[1];
    else
        Error( "the first argument is not a string\n" );
    fi;

    if nargs = 1 then
    elif IsString( arg[2] ) then
        str := arg[2];
    else
        Error( "the second argument is not a string\n" );
    fi;
    
    ## figure out the directory separtor:
    if IsBound( GAPInfo.UserHome ) then
        separator := GAPInfo.UserHome[1];
    else
        separator := '/';
    fi;
    
    if IsBound( HOMALG_IO.DirectoryForTemporaryFiles ) then
        directory := HOMALG_IO.DirectoryForTemporaryFiles;
        if directory[Length(directory)] <> separator then
            directory[Length(directory) + 1] := separator;
        fi;
    else
        directory := "";
    fi;
    
    if IsBound( HOMALG_IO.FileNameCounter ) then
        pointer := Concatenation( "homalg_file_", String( HOMALG_IO.FileNameCounter ) );
        HOMALG_IO.FileNameCounter := HOMALG_IO.FileNameCounter + 1;
    else
        Error( "HOMALG_IO.FileNameCounter is not bound, filename creation for internal object failed.\n" );
    fi;
    
    if not IsBound( HOMALG_IO.PID ) or not IsInt( HOMALG_IO.PID ) then
        HOMALG_IO.PID := 99999; #this is not the real PID!
    fi;
    
    pid := Concatenation( "_PID_", String( HOMALG_IO.PID ) );
    
    file := Concatenation( pointer, pid );
    
    filename := Concatenation( directory, file );
    
    if IsBound( str ) then
        cmd := Filename( DirectoriesSystemPrograms(), cmd );
        
        Exec( Concatenation( "echo ", str, " | ", cmd, " ", " > ", filename ) );
    else
        Exec( Concatenation( cmd, " ", " > ", filename ) );
    fi;
    
    fs := IO_File( filename, "r" );
    
    if fs = fail then
        Error( "unable to open the file ", filename, " for reading\n" );
    fi;
    
    output := IO_ReadUntilEOF( fs );
        
    if IO_Close( fs ) = fail then
        Error( "unable to close the file ", filename, "\n" );
    fi;
    
    if not ( IsBound( HOMALG_IO.DoNotDeleteTemporaryFiles ) and HOMALG_IO.DoNotDeleteTemporaryFiles = true ) then
        Exec( Concatenation( "/bin/rm -f \"", filename, "\"" ) );
    fi;
    
    return output;
    
end );

##
InstallMethod( ShaSum,
        "for a string",
        [ IsString ],
        
  function( str )
    local sha;
    
    sha := ApplyCommandToString( "shasum", str );
    
    NormalizeWhitespace( sha );
    
    return sha{[ 1 .. Length( sha ) - 2 ]};
    
end );

##
InstallGlobalFunction( GetTimeOfDay,
  function( arg )
    local t;
    
    t := ApplyCommandToString( "date +\"%Y-%m-%d:%H:%M:%S,%N\"" );
    
    NormalizeWhitespace( t );
    
    return t;
    
end );

##
InstallGlobalFunction( FingerprintOfGapProcess,
  function( arg )
    local f;
    
    f := rec( Version := GAPInfo.Version,
              BuildDateTime := GAPInfo.BuildDateTime,
              Architecture := GAPInfo.Architecture,
              PID := IO_getpid(),
              TimeOfDay := GetTimeOfDay()
              );
    
    if Length( arg ) > 0 then
        if Length( arg ) = 1 then
            f.ID := arg[1];
        else
            f.ID := arg;
        fi;
    fi;
    
    return f;
    
end );

