Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  singular.g   Sprache: unbekannt

 
#############################################################################
##
#W    singular.g           Package singular            Willem de Graaf
#W                                                     Marco Costantini
##
#Y    Copyright (C) 2003 Willem de Graaf and Marco Costantini
#Y    Copyright (C) 2004, 2005, 2006 Marco Costantini
##

##############################################################################
##############################################################################

## PART 0. Singular executable file, options, directory ##


# <--- This line is for developing/debugging: this allow to do 
# ReadPackage("singular", "gap/singular.g");
# (by the function SingularReloadFile) as much as needed. Simply ignore it. 
if not IsBound( Sing_Proc ) then  



# The full path to the Singular executable file

# Here in this file must be added a line with the full path to the
# Singular executable file on your system (without the '#'), e.g.
# sing_exec := "/home/graaf/Singular/2-0-3/ix86-Linux/Singular";
# or, if the executable is in the system $PATH and has a name which
# is non-standard (e.g. singular in lowercase), just with its name
# as below:

if not IsBound( sing_exec ) then
sing_exec := "singular";
fi;

# The directory separator is always '/', even under DOS/Windows or
# MacOS, as in the following example:
# sing_exec := "/usr/local/Singular/2-0-4/ix86-Win/Singular.exe";

# If the Singular executable file is the the system $PATH and has
# the standard name "Singular" started in uppercase, then it is not
# necessary adding this line, because the interface should be able to
# find the executable file itself. You can get this path, from within 
# Singular, with the command 
# system( "Singular" );

# Singular command line options

# sing_exec_options a list of command-line options (given as strings)
# that will be passed to Singular at its startup. The option "-t" is
# necessary for proper working, but others can be added. See the
# documentation of Singular, paragraph "3.1.6 Command line options". 
# Similarly, it is possible to supply files to Singular containing user
# defined functions, as in the following example:
# sing_exec_options := [ "-t", "/full_path/my_file" ];

if not IsBound( sing_exec_options ) then
sing_exec_options := [ "-t" ];
fi;



# Temporary directory for i/o with Singular 

# You may set it like the following line if you prefer to have the
# temporary files in a specific directory. Examples:
# SingularTempDirectory := Directory( "/tmp" );
# SingularTempDirectory := DirectoryCurrent(  );
# If you don't specify it, the interface will set up a temporary one.

if not IsBound( SingularTempDirectory ) then
SingularTempDirectory := "";
fi;



##############################################################################

# No modification below this line is necessary.

##############################################################################
##############################################################################

## PART 1. Global variables, some of them mirroring Singular globals ##



# The following means that the variables sing_exec, sing_exec_options,
# and SingularTempDirectory need to be checked...
SingularExecutableAndTempDirAreOk := false;

# The Singular i/o process
Sing_Proc := fail; # not yet started

SingularNr := rec(  );
# How many times did Gap (re)start Singular (as InputOutputLocalProcess)?
SingularNr.Session := 0;
# How many times did Gap (re)start Singular (as Process)?
SingularNr.Process := 0;
# How many times did Gap send input to Singular (in this session)?
SingularNr.Input := 0;
# How many times has Gap received output from Singular (in this session)?
SingularNr.Output := 0;

# The limitations of Singular: see the documentation of Singular,
# paragraph "6.1 Limitations".

SingularLimitations := rec(
# the maximal characteristic of a prime field:
max_char_prime_field := 2147483629,
# the maximal size of a finite non prime field:
max_size_nonprime_field := 2^15,
# the maximal exponent of a ring variable:
max_exp_ring_var := 65536,
# the biggest integer (of type "int"):
max_int := 2147483647 );

## You can tell the interface which is the biggest number in Singular of 
## type "int" (it depends also on your hardware and on the version of 
## Singular). If you omit this, the interface will try to autodetermine.
## For safety, you can choose the smallest one.
#
#SingularLimitations.max_int := 2147483647; # on a 32-bit machine
## SingularLimitations.max_int := 9223372036854775807; # on a 64-bit 
## machine, with a new version of Singular
#
### try to autodetect
##if  not IsBound( OBJLEN ) and OBJLEN = 8  then # Gap 4.3
##if  not IsBound( GAPInfo.BytesPerVariable ) and GAPInfo.BytesPerVariable 
## = 8  then # Gap 4.4
##    SingularLimitations.max_int := 9223372036854775807;
##else 
##    SingularLimitations.max_int := 2147483647;
##fi;


# Singular version, an integer, as in the output of the Singular command
# system("version");
# The interface will ask Singular for it.
SingularVersion := 0; # not yet determined;

# The Libraries loaded in Singular
SingularLoadedLibraries := "";

SingularType := fail; # it will be defined later...
ParseGapRingToSingRing := fail; # it will be defined later...
ConvertGapObjToSingObj := fail; # it will be defined later...
SingularInterface := fail; # it will be defined later...
SingularSetBaseRing := fail; # it will be defined later...

# The Base Ring in Singular; the provided default should match the
# default of Singular.
SingularBaseRing := PolynomialRing( GF( 32003 ), 3 );

# The SingularBaseRing will be called GAP_ring in Singular;
# ideals will be called GAP_ideal_1, GAP_ideal_2, ... in Singular.
# If SingularNames.ideal = n, then the ideals GAP_ideal_1, ...,
# GAP_ideal_n have been sent to Singular or received from Singular.
#
# It will be checked, with the function
# HasCurrentRingSingularIdentifier, whether the ideal names refers to
# the current SingularBaseRing of Singular or two a previous ring.
# In the latter case, it will be necessary to send again the ideal.
#
# The same for modules.

SingularNames:= rec( ideal := 0, module := 0 );

SingularNamesThisRing := ShallowCopy( SingularNames );


# <--- For debug: see above, the beginning of the file.
fi; 


# This function checks whether the SingularIdentifier of an ideal or
# module refers to the current session of Singular or it is the old
# SingularIdentifier of a previous SingularBaseRing

BindGlobal( "HasCurrentRingSingularIdentifier", function ( obj )

    local  s, t;

    if not HasSingularIdentifier( obj )  then
        return false;
    fi;

    t := SingularType( obj );
    s := SingularIdentifier( obj );
    if t = "ideal"  and Int( s{[ 11 .. Length( s ) ]} ) > 
                        SingularNamesThisRing.ideal or
       t = "module" and Int( s{[ 12 .. Length( s ) ]} ) > 
                        SingularNamesThisRing.module  then
        return true;
    else
        return false;
    fi;

end );




# It would be possible to add also something like this for backward
# compatibility:
#
# InstallMethod( \=, IsIdenticalObj, [ IsPolynomialRing,
#  IsPolynomialRing ], 0, 
# ...
# function ( V, W )
#    if
#     HasDimension( V ) and HasDimension( W ) and IsIdenticalObj( 
# LeftActingDomain( V ), LeftActingDomain( W ) )  then
#        if Dimension( V ) <> Dimension( W )  then
#            return false;
#        elif IsInt( Dimension( V ) )  then
#            return IsSubset( V, W );
#        fi;
#    fi;
#    return IsSubset( V, W ) and IsSubset( W, V );
# end;




##############################################################################
##############################################################################

## PART 2. Singular interface at low level ##




BindGlobal( "CheckSingularExecutableAndTempDir", function (  )
    local i, IsExec;

    # check the Singular executable file, and if needed try to
    # autodetermine, or print an appropriate error message

    IsExec := path -> IsString( path ) and IsDirectoryPath( path ) <>
        true and IsExecutableFile( path ) = true;

    # try to correct the string in case that only the directory or the
    # filename was supplied
    if IsBound( sing_exec ) and IsString( sing_exec ) then
         if IsDirectoryPath( sing_exec ) = true  then
            sing_exec := Filename( Directory( sing_exec ), "Singular" );
        elif not IsExecutableFile( sing_exec ) = true and
             not "/" in sing_exec  then
            sing_exec := Filename( DirectoriesSystemPrograms(  ), sing_exec );
        fi;
   fi;

    # try to detect the executable file
    if not IsBound( sing_exec ) or not IsExec( sing_exec )  then
        sing_exec := Filename( DirectoriesSystemPrograms(  ), "Singular" );
        if sing_exec <> fail then
            Info( InfoSingular, 2, "Found Singular executable file ",
                  sing_exec );
        fi;
    fi;

    # check the executable file, if failed print an error message
    while not IsBound( sing_exec ) or not IsExec( sing_exec ) do
        Print( "  Type 'sing_exec:=\"<path>\"; return;' where <path>\n" );
        Print( "  is the path of the Singular executable file on your \
system.\n" );
        if IsBound( sing_exec )  then
            if not IsString( sing_exec )  then
                Print( "  'sing_exec' must be a string.\n" );
            else
                Print( "'", sing_exec, "' is not an executable file.\n" );
            fi;
        fi;
        Error( "Singular executable file not found!\n" );
    od;


    # check the Singular command line options

    # sing_exec_options must be a dense list of strings
    if not (IsList( sing_exec_options ) and IsDenseList( sing_exec_options ) )
         then
        Error( "sing_exec_options must be a (dense) list\n" );
    fi;
    if not ForAll( sing_exec_options, IsString )  then
        Error( "all the components of sing_exec_options must be strings\n" );
    fi;

    # some options are necessary
    for i  in [ "-t" ]  do
        if not i in sing_exec_options  then
            Error( "Singular command line option ", i, " is necessary\n" );
        fi;
    od;

    # some options are not supported
    for i  in [ "-h", "--help", "-e", "--echo" ]  do
        if i in sing_exec_options  then
            Error( "Singular command line option ", i, " is not allowed\n" );
        fi;
    od;


    # check the temporary directory that will be used for i/o with Singular

    if IsBound( SingularTempDirectory ) and IsString( SingularTempDirectory )
       and Length( SingularTempDirectory ) > 0
        then SingularTempDirectory := Directory( SingularTempDirectory );
    fi;

    if not IsBound( SingularTempDirectory![1] ) or 
       not IsDirectoryPath( SingularTempDirectory![1] ) = true or 
#       not IsReadableFile( SingularTempDirectory![1] ) = true or 
       not IsWritableFile( SingularTempDirectory![1] ) = true or 
       not IsExecutableFile( SingularTempDirectory![1] ) = true  then
        SingularTempDirectory := DirectoryTemporary( "Sing" );

        if SingularTempDirectory = fail  then
            Error( "cannot create a temporary directory\n" );
        fi;

        Info( InfoSingular, 2, "Using temporary ", SingularTempDirectory );

    fi;

    SingularExecutableAndTempDirAreOk := true;

end );



# A function for closing (killing) Singular

BindGlobal( "CloseSingular", function (  )
    if IsStream( Sing_Proc )  then
        if not IsClosedStream( Sing_Proc )  then
            WriteLine( Sing_Proc, ";quit;" );
            CloseStream( Sing_Proc );
        else
            Info( InfoSingular, 2, "Singular already closed." );
        fi;
    fi;
    # after closing Singular, the names become out of date.
    SingularNamesThisRing := ShallowCopy( SingularNames );
end );


# Kill Singular when Gap terminates
InstallAtExit( CloseSingular );



# The low level function for i/o with Singular. This function splits the
# string with the Singular input into several lines, sends each of them
# to Singular, waiting for the Singular prompt "> " or ". " at end of
# output, relative to that line, before sending the next line. 
# This is necessary because some versions of Singular ignore the input
# that is received before giving the prompt.
# After that, this function calls "GAP_Done ();" (to have a '@' in the 
# output, to be sure that Singular finished), waits to receive the 
# prompt "@\n> ", and then returns all the output of Singular.
# (The char before "> ", ". " or "@\n> " depends on the operating
# system, and on the sing_exec_options "-t".)

BindGlobal( "SingWriteAndReadUntilDone", function ( string )

    local read_blocking, read_non_blocking, read, out, OUT, s, i;

    read_blocking := ReadAll;

    read_non_blocking := function ( stream )
        local  sl, outl;
        outl := "";
        repeat
            sl := READ_IOSTREAM_NOWAIT( stream![1], 1 );
            if sl <> fail  then
                Append( outl, sl );
            fi;
        until sl = fail;
        return outl;
    end;

    # choose exactly one of the following lines:

    # read := read_non_blocking;
    read := read_blocking;

    # read_blocking: Gap blocks while Singular is running, resulting in
    # a faster execution; Gap cannot be interrupted by <ctrl>-C in case
    # of interface error. Suggested for normal use.
    
    # read_non_blocking: Gap keeps running while Singular is running,
    # resulting in a slower execution; Gap can be interrupted by
    # <ctrl>-C in case of interface error. Suggested for debugging.
    # Requires Gap version at least 4.3.

    if '$' in string  then
        # a '$' would close Singular...
        Print( "Discarding the '$' in the Singular input\n" );
        string := ReplacedString( string, "$", "");
    fi;

    string := SplitString( string, '\n' );
    out := "";
    OUT := "";

    for i  in [ 1 .. Length( string ) ]  do
        if Length( string[i] ) > 4000  then    # max ~4050
            Error( "the command line for Singular is too long, ",
                   "please report\n" );
        fi;

        WriteLine( Sing_Proc, string[i] );

        SingularNr.Input := SingularNr.Input + 1;
        Info( InfoSingular, 3, "input ", SingularNr.Input, ": ", string[i] );

        repeat
            s := read( Sing_Proc );
            Append( out, s );
        until PositionSublist( out, "> ", Length( out ) - 2 ) <> fail
          or PositionSublist( out, ". ", Length( out ) - 2 ) <> fail;

        SingularNr.Output := SingularNr.Output + 1;
        Info( InfoSingular, 3, "output ", SingularNr.Output, ": ", out );

        Append( OUT, out );
        out := "";
    od;

    WriteLine( Sing_Proc, ";GAP_Done ();" );

    SingularNr.Input := SingularNr.Input + 1;
    Info( InfoSingular, 3, "input ", SingularNr.Input, ": ", 
          ";GAP_Done ();" );


    repeat
        s := read( Sing_Proc );
        Append( out, s );

    until PositionSublist( out, "@\n> ", Length( out ) - 4 ) <> fail;

#   with a very old version of Singular replace the previous line with
#   the following ones

#    until PositionSublist( out, "@\n" ) <> fail and
#          PositionSublist( out, "> ", Length( out ) - 2 ) <> fail;

    Append( OUT, out ); # is this needed?

#        # attempt to trap the Singular errors
#        pos := PositionSublist( OUT, "? error occurred in STDIN line " );
#        if pos <> fail  then
#             Error( "Singular error" );
#        fi;

    SingularNr.Output := SingularNr.Output + 1;
    Info( InfoSingular, 3, "output ", SingularNr.Output, ": ", out );

    return OUT;
end );




BindGlobal( "StartSingular", function (  )

    local  file_in, out, s;


    # is there a previous Singular running?

    if IsStream( Sing_Proc ) and not IsClosedStream( Sing_Proc )  then
        CloseSingular(  );
    fi;


    CheckSingularExecutableAndTempDir(  );


    # We also provide Singular with a function for producing a '@'; this
    # enables us to let Singular write a '@' without putting one in the
    # input; the latter strategy proved to be confusing with some
    # operating system, without the sing_exec_options "-t".
    # (Another possibility would be to send to Singular
    # LIB("general.lib"); proc GAP_Done () { return ( ASCII(64) ) }; .)

    # perhaps could be better using a file in DirectoriesPackageLibrary

    file_in := Filename( SingularTempDirectory, "sing.in" );

    PrintTo( file_in, "proc GAP_Done () { return ( \"@\" ) };\n",
                      "proc GAP_Apostrophe () { return ( \"'\" ) };\n", 
                      "GAP_Done();\n" );


    # this starts Singular, attaches it to the i/o process `Sing_Proc', and
    # reads <file_in> with the commands given above

    Sing_Proc := InputOutputLocalProcess( SingularTempDirectory, 
       sing_exec, Concatenation( sing_exec_options, [ file_in ] ) );


    SingularNr.Session := SingularNr.Session + 1;

    SingularNr.Input := 0;
    SingularNr.Output := 0;


    # We get the Singular banner and discard any output.
    out := ReadAll( Sing_Proc );
    if out = fail  then
        Error( "Singular didn't start!\n",
               "Is correct the value of sing_exec ( ", sing_exec, " )?\n",
               "Does Singular work, when called as a standalone program?\n");
    fi;

    while PositionSublist( out, "@\n> ", Length( out ) - 4 ) = fail do

#   with a very old version of Singular replace the previous line with
#   the following ones

#    while PositionSublist( out, "@\n" ) <> fail and 
#          PositionSublist( out, "> ", Length( out ) - 2 ) = fail do
        s := ReadAll( Sing_Proc );
        Append( out, s );
    od;

#    SingularNr.Output:= SingularNr.Output + 1;
    Info(InfoSingular, 3, "output ", SingularNr.Output, ":\n", out);

    # Now we check that Singular is working, and test the interface
    out := SingWriteAndReadUntilDone( "" );


    # ask Singular, to determine its version
    out := SingWriteAndReadUntilDone( "system(\"version\");" );
    SingularVersion := Int( Filtered( out, IsDigitChar ) );
    # SingularVersion := SingularInterface( "system", [ "version" ], "int" );

    Info( InfoSingular, 2, "Started Singular, version ", SingularVersion );

    # set the base ring in Singular according to the SingularBaseRing in Gap.
    SingularSetBaseRing( SingularBaseRing );


end );






##############################################################################
##############################################################################

## PART 3. Singular interface at medium level ##



# this function writes a Gap string to a file (that will be read by
# Singular) without the '\' at the end of the lines: the '\' confuses
# Singular

BindGlobal( "AppendStringToFile", function ( file, s )
    local  otf;
    otf := OutputTextFile( file, true );
    SetPrintFormattingStatus( otf, false );
    AppendTo( otf, s );
    CloseStream( otf );
end );


# This function could replace use of NormalizedWhitespace, or could be
# put inside ReadStringFromFile .
BindGlobal( "RemovedNewline", function ( string )
    if Length( string ) > 0 and string[Length( string )] = '\n'  then
        Unbind( string[Length( string )] );
    fi;
    return ReplacedString( string, "\n", " " );
end );


# This function reads a file (written by Singular), and returns it as a
# string to Gap, without the "\n", that confuse Gap.
BindGlobal( "ReadStringFromFile", function ( file )
    local  itf, r;
    itf := InputTextFile( file );
    r := ReadAll( itf );
    CloseStream( itf );
    return RemovedNewline( r );
end );



BindGlobal( "WithoutEndingSemicolon", function ( string )
    local  i;
    i := Length( string );
    while i > 0  do
        if string[i] = ' '  then
            i := i - 1;
        elif string[i] = ';'  then
            string[i] := ' ';
        else
            break;
        fi;
    od;
    return string;
end );



# This function is under construction... maybe it is not needed.
BindGlobal( "EscapeCharsInString", function ( string )
    string := ReplacedString( string, "\\", "\\\\" );
    string := ReplacedString( string, "\n", "\\\n" );
    string := ReplacedString( string, "\"", "\\\"" );
    string := ReplacedString( string, "'", "\\'" );
    string := ReplacedString( string, "\b", "\\\b" );
    string := ReplacedString( string, "\r", "\\\r" );
    string := ReplacedString( string, "\c", "\\\c" );
    return string;
end );



# In the following functions, 'precommand' is used, for instance, to
# send the SingularBaseRing, then only the output of 'command' will be
# returned. 'command' must be a single command, but 'precommand' may be
# a semicolon-separated list of commands

# "Stream" and "File", in the name of the following functions, refers
# only to the way of sending the mathematical data, all these functions
# use the stream for low-level communications.

BindGlobal( "SingCommandInStreamOutStream", function ( precommand, command )

    local  singcom, out, pos1, pos2;

    if not IsStream( Sing_Proc ) or IsClosedStream( Sing_Proc )  then
        StartSingular(  );
    fi;

    # test the input
    if '@' in precommand or '@' in command  then
        Error( "please do not use '@' in the commands \n" );
    fi;
    if ''' in precommand or ''' in command  then
        Error( "please do not use ''' in the commands \n" );
    fi;

    # prepare the input to Singular, asking for an output between two '''
    singcom := Concatenation( precommand, ";\nGAP_Apostrophe();",
                              command, ";GAP_Apostrophe();" );

    # send it, and get the output of Singular
    out := SingWriteAndReadUntilDone( singcom );

    pos1 := PositionSublist( out, "'\n" );
    if pos1 = fail  then
        Error( "output of Singular only partially retrieved\n" );
    fi;

    pos2 := PositionSublist( out, "\n'\n", pos1 );
    if pos2 = fail  then
        Error( "output of Singular only partially retrieved\n" );
    fi;

    # return the output, without the ''' and the "\n", 
    return out{[ pos1 + 2 .. pos2 - 1 ]};

end );



BindGlobal( "SingCommandInFileOutStream", function ( precommand, command )

    local file_in, out, pos1, pos2;

    if not IsStream( Sing_Proc ) or IsClosedStream( Sing_Proc )  then
        StartSingular();
    fi;

    # test the input
    if '@' in precommand or '@' in command  then
        Error( "please do not use '@' in the commands \n" );
    fi;
    if ''' in precommand or ''' in command  then
        Error( "please do not use ''' in the commands \n" );
    fi;

    # the input file
    file_in:= Filename( SingularTempDirectory, "sing.in" );

    # to be safe
    RemoveFile( file_in );

    # write the input for Singular in 'file_in'
    AppendStringToFile( file_in, Concatenation( precommand,
           ";\nGAP_Apostrophe();", command, ";GAP_Apostrophe();" ) );

    # tell Singular to read and execute 'file_in', and get the output
    out := SingWriteAndReadUntilDone( "< \"sing.in\";" );

    pos1 := PositionSublist( out, "'\n" );
    if pos1 = fail  then
        Error( "output of Singular only partially retrieved\n" );
    fi;

    pos2 := PositionSublist( out, "\n'\n", pos1 );
    if pos2 = fail  then
        Error( "output of Singular only partially retrieved\n" );
    fi;

    # the output, without the ''' and the "\n"
    out := out{[ pos1 + 2 .. pos2 - 1 ]};

    if InfoLevel( InfoSingular ) < 3 then
        RemoveFile( file_in );
    fi;

    return out;
end );



BindGlobal( "SingCommandInFileOutFile", function ( precommand, command )

    local file_in, file_out, string_in, out;


    if not IsStream( Sing_Proc ) or IsClosedStream( Sing_Proc )  then
        StartSingular();
    fi;

    # test the input
    if '@' in precommand or '@' in command  then
        Error( "please do not use '@' in the commands \n" );
    fi;

    # the input and output files
    file_in:= Filename( SingularTempDirectory, "sing.in" );
    file_out:= Filename( SingularTempDirectory, "sing.out" );

    # to be safe
    RemoveFile( file_in );
    RemoveFile( file_out );

    # write the input for Singular in 'file_in'
    string_in := precommand;
    Append( string_in, ";\n" );

    if command <> ""  then
        Append( string_in, "write( \"sing.out\", " );
        Append( string_in, WithoutEndingSemicolon( command ) );
        Append( string_in, " );\n" );
    fi;

    AppendStringToFile( file_in, string_in );

    # tell Singular to read and execute 'file_in', and get the output
    out := SingWriteAndReadUntilDone( "< \"sing.in\";" );

    if command <> ""  then
        if not IsExistingFile( file_out ) then
            Error( "Singular didn't write the output to the file\n" );
        fi;

        out := ReadStringFromFile( file_out );
    fi;

    if InfoLevel( InfoSingular ) < 3 then
        RemoveFile( file_in );
        RemoveFile( file_out );
    fi;

    if command <> ""  then
        return out;
    else
        return "";
    fi;

end );



BindGlobal( "SingCommandInStreamOutFile", function ( precommand, command )

    local file_out, out, singcom;

    if not IsStream( Sing_Proc ) or IsClosedStream( Sing_Proc )  then
        StartSingular();
    fi;

    # test the input
    if '@' in precommand or '@' in command  then
        Error( "please do not use '@' in the commands \n" );
    fi;

    # the output file
    file_out:= Filename( SingularTempDirectory, "sing.out" );

    # to be safe
    RemoveFile( file_out );

    # send the input to Singular, asking to write it in file_out

    out := SingWriteAndReadUntilDone( precommand );

    if command <> ""  then

        singcom := "write( \"sing.out\", ";
        Append( singcom, WithoutEndingSemicolon( command ) );
        Append( singcom, " );\n" );

        out := SingWriteAndReadUntilDone( singcom );

        if not IsExistingFile( file_out ) then
            Error( "Singular didn't write the output to the file\n" );
        fi;

        out := ReadStringFromFile( file_out );


        if InfoLevel( InfoSingular ) < 3 then
            RemoveFile( file_out );
        fi;

        return out;

    else
        return "";
    fi;

end );



# The following function doesn't use InputOutputLocalProcess,
# so it can be used under Windows with Gap version < 4.4.2

BindGlobal( "SingCommandUsingProcess", function ( precommand, command )

    local  _in, out, _out, opt, file_in, file_out;


    if not SingularExecutableAndTempDirAreOk  then
        CheckSingularExecutableAndTempDir(  );
    fi;

    # the input and output files
    file_in:= Filename( SingularTempDirectory, "sing.in" );
    file_out:= Filename( SingularTempDirectory, "sing.out" );

    # to be safe
    RemoveFile( file_in );
    RemoveFile( file_out );

    # write the input for Singular in 'file_in'
    AppendStringToFile( file_in, SingularLoadedLibraries );
    AppendStringToFile( file_in, ParseGapRingToSingRing( SingularBaseRing ) );
    AppendStringToFile( file_in, precommand );
    AppendStringToFile( file_in, ";\n" );

    if command <> ""  then
        AppendStringToFile( file_in, "write( \"sing.out\", " );
        AppendStringToFile( file_in, WithoutEndingSemicolon( command ) );
        AppendStringToFile( file_in, " );\n" );
    fi;


    _in := InputTextNone(  );
    _out := OutputTextNone(  );
    opt := Concatenation( "--execute=", "< \"sing.in\";", ";quit;" );

    Process( SingularTempDirectory, sing_exec, _in, _out, 
             Concatenation( sing_exec_options, [ opt ] ) );

    CloseStream( _in );
    CloseStream( _out );

    if command <> ""  then
        if not IsExistingFile( file_out ) then
            Error( "Singular didn't write the output to the file\n" );
        fi;

        out := ReadStringFromFile( file_out );

    fi;

    if InfoLevel( InfoSingular ) < 3 then
        RemoveFile( file_in );
        RemoveFile( file_out );
    fi;

    SingularNr.Process := SingularNr.Process + 1;

    if command <> ""  then

        return out;
    else
        return "";
    fi;

end );


# writing to a i/o stream is slow in windows (but fast in unix)
if ARCH_IS_WINDOWS(  ) then 
    # choose one
    #SingularCommand := SingCommandInStreamOutStream; # slow with windows
    SingularCommand := SingCommandInFileOutStream;
    #SingularCommand := SingCommandInFileOutFile;
    #SingularCommand := SingCommandInStreamOutFile; # slow with windows
    #SingularCommand := SingCommandUsingProcess; # not recommended!
else
    # choose one
    SingularCommand := SingCommandInStreamOutStream; # slow with windows
    #SingularCommand := SingCommandInFileOutStream;
    #SingularCommand := SingCommandInFileOutFile;
    #SingularCommand := SingCommandInStreamOutFile; # slow with windows
    #SingularCommand := SingCommandUsingProcess; # not recommended!
fi;


if SingularCommand = SingCommandUsingProcess  then
    SingCommandInStreamOutStream := ReturnFail;
    HasCurrentRingSingularIdentifier := ReturnFalse;
#    SingularVersion := Int( SingularCommand( "", "system(\"version\");" ) );
fi;



##############################################################################
##############################################################################

## PART 4. Parsing Gap --> Singular ##

# Some functions to convert Gap objects into strings that represent
# Singular objects.


# This function tells whether a Gap object corresponds to a Singular
# object of type "int"

BindGlobal( "IsSingularInt", function ( n )
    if IsInt( n )  then
        return - SingularLimitations.max_int <= n and
               n <= SingularLimitations.max_int;
    else
        return false;
    fi;
end );


# This function tells whether a Gap object corresponds to a Singular
# object of type "poly"

IsSingularPoly := p -> IsRationalFunction( p ) and IsPolynomial( p ) 
        and p in SingularBaseRing;



##############################################################################


BindGlobal( "ParseGapNumberToSingNumber", function ( n )

    local  eroo, str, i;

    if not n in CoefficientsRing( SingularBaseRing )  then
        Error( "the number ", n,
           " is not in the CoefficientsRing of the Singular Base Ring ",
               CoefficientsRing( SingularBaseRing ), "\n" );
    fi;


    if IsPrimeField( CoefficientsRing( SingularBaseRing ) ) or
       IsFFE( n ) and IsZero( n )  then # or DegreeFFE( n ) = 1

        if Characteristic( SingularBaseRing ) = 0  then
            return String( n );
        else
            # without the "number( ", Singular would interpret the
            # finite field element as an integer
            return Concatenation( "number( ", String( IntFFE( n ) ), " )" );
        fi;

    else

        if Characteristic( SingularBaseRing ) = 0 or
           IsAlgebraicExtension( CoefficientsRing( SingularBaseRing ) )  then

            if IsRat( n )  then
                return String( n );
            fi;

            if IsCyc( n ) then
                eroo := CoeffsCyc( n , Conductor( CoefficientsRing( 
                    SingularBaseRing ) ) );
            else
                eroo := ExtRepOfObj( n );
            fi;

            str := "( ";
            for i  in [ 1 .. Length( eroo ) ]  do
                if Characteristic( SingularBaseRing ) = 0  then
                    Append( str, String( eroo[i] ) );
                else
                    Append( str, String( IntFFE( eroo[i] ) ) );
                fi;
                Append( str, "*q^" );
                Append( str, String( i - 1 ) );
                if i < Length( eroo )  then
                    Append( str, "+" );
                fi;
            od;
            Append( str, " )" );
            return str;

        else

            return Concatenation( "q^", String( LogFFE( n,
        PrimitiveRoot( CoefficientsRing( SingularBaseRing ) ) ) ) );

        fi;

    fi;

end );



BindGlobal( "ParseGapPolyToSingPoly", function ( pol )

    # pol is a GAP polynomial, we parse it into a string representing
    # a Singular polynomial.

    local   varnums,  str,  mons,  k,  mon,  m,  len;

    if not pol in SingularBaseRing  then
        Error( "the polynomial ", pol, " is not in the Singular Base Ring ",
               SingularBaseRing, "\n" );
    fi;

    if IsZero( pol )  then
        return "poly(0)";
    fi;

    varnums:= IndeterminateNumbers( SingularBaseRing );
    # without the "poly(", Singular would interpret a degree 0
    # polynomial as a number
    str:= "poly(";
    mons:= ExtRepPolynomialRatFun( pol );
    k:= 1;

    len:= 0;

    while k <= Length( mons ) do

        # after 1000 chars we append a "\n", to avoid too long lines
        if Length( str )-len >= 1000 then
            Append( str, "\n" );
            len:= Length( str );
        fi;

        if k > 1 then Add( str, '+' ); fi;

        Append( str, ParseGapNumberToSingNumber( mons[k+1] ) );

        mon:= mons[k];
        m:= 1;
        while m <= Length( mon ) do
            Append( str, "*x_" );
            Append( str, String( Position( varnums, mon[m] ) ) );
            Append( str, "^" );
            if mon[m + 1] >= SingularLimitations.max_exp_ring_var  then
                Error( "Singular supports only exponents of a ring ",
                       "variables smaller than ",
                       SingularLimitations.max_exp_ring_var, "\n" );
            fi;
            Append( str, String( mon[m+1] ) );
            m:=m+2;
        od;
        k:= k+2;
    od;

    Append( str, ")" );
    return str;
end );



BindGlobal( "ParseGapIdealToSingIdeal", function ( I )

    local  str, pols, k;

    if LeftActingRingOfIdeal( I ) <> SingularBaseRing  then
        SingularSetBaseRing( LeftActingRingOfIdeal( I ) );
    fi;

    str := "ideal(\n";

    pols := GeneratorsOfTwoSidedIdeal( I );
    for k  in [ 1 .. Length( pols ) ]  do
        Append( str, ParseGapPolyToSingPoly( pols[k] ) );
        if k < Length( pols )  then
            Append( str, ",\n" );
        else
            Append( str, ")\n" );
        fi;
    od;

    return str;
end );



BindGlobal( "ParseGapIntmatToSingIntmat", function ( mat )
    local  str, dim, i, j;
    dim := DimensionsMat( mat );
    str := "intmat (intvec(";
    for i  in [ 1 .. dim[1] ]  do
        Append( str, "\n" );
        for j  in [ 1 .. dim[2] ]  do
            Append( str, String( mat[i][j] ) );
            if not (i = dim[1] and j = dim[2])  then
                Append( str, "," );
            fi;
            if j mod 50 = 0  then
                Append( str, "\n" );
            fi;
        od;
    od;
    Append( str, ")," );
    Append( str, String( dim[1] ) );
    Append( str, "," );
    Append( str, String( dim[2] ) );
    Append( str, ")" );
    return str;
end );



BindGlobal( "ParseGapIntmatToSingBigintmat", function ( mat )
    local  str, dim, i, j;
    dim := DimensionsMat( mat );
    str := "(x->{bigintmat m[";
    Append( str, String( dim[1] ) );
    Append( str, "][");
    Append( str, String( dim[2] ) );
    Append( str, "] = " );
    for i  in [ 1 .. dim[1] ]  do
        Append( str, "\n" );
        for j  in [ 1 .. dim[2] ]  do
            Append( str, String( mat[i][j] ) );
            if not (i = dim[1] and j = dim[2])  then
                Append( str, "," );
            fi;
            if j mod 50 = 0  then
                Append( str, "\n" );
            fi;
        od;
    od;
    Append( str, "; m})(0)" );
    return str;
end );



BindGlobal( "ParseGapIntvecToSingIntvec", function ( vec )
    local  str, dim, i;
    dim := Length( vec );
    str := "intvec(";
    for i  in [ 1 .. dim ]  do
        Append( str, String( vec[i] ) );
        if not i = dim  then
            Append( str, "," );
        fi;
        if i mod 50 = 0  then
            Append( str, "\n" );
        fi;
    od;
    Append( str, ")" );
    return str;
end );



BindGlobal( "ParseGapIntvecToSingBigintvec", function ( vec )
    local  str, dim, i;
    dim := Length( vec );
    str := "bigintvec(";
    for i  in [ 1 .. dim ]  do
        Append( str, String( vec[i] ) );
        if not i = dim  then
            Append( str, "," );
        fi;
        if i mod 50 = 0  then
            Append( str, "\n" );
        fi;
    od;
    Append( str, ")" );
    return str;
end );



BindGlobal( "ParseGapModuleToSingModule", function ( M )

    local  str, l_pols, k, k2;

    if LeftActingDomain( M ) <> SingularBaseRing  then
        SingularSetBaseRing( LeftActingDomain( M ) );
    fi;

    str:= "module(\n";

    l_pols := GeneratorsOfLeftOperatorAdditiveGroup( M );
    for k  in [ 1 .. Length( l_pols ) ]  do
        Append( str, "[ " );
        for k2  in [ 1 .. Length( l_pols[k] ) ]  do
            Append( str, ParseGapPolyToSingPoly( l_pols[k][k2] ) );
            if k2 < Length( l_pols[k] )  then
                Append( str, "," );
            fi;
        od;
        if k < Length( l_pols )  then
            Append( str, "],\n" );
        else
            Append( str, "])\n" );
        fi;
    od;

    return str;
end );




BindGlobal( "ParseGapOrderingToSingOrdering", function( tor )

    # A TermOrdering of a ring R is either a string ( "lp", "dp", "Dp" ),
    # meaning that the corresponding term ordering in Singular is
    # chosen,
    # or a list of the form (e.g.) [ "dp", 3, "lp", 2 ], meaning
    # that the first three indeterminates are ordered by dp, the
    # remaining two by lp.
    # If a weighted ordering is specified ( "wp", "Wp", "ws", "Ws" ),
    # then the next element in the list is not an integer, but the
    # weight vector.
    # A TermOrdering may also be a Gap MonomialOrdering.

    local  to, i, j, name;

    if IsString( tor )  then
        return tor;

    elif IsList( tor )  then
        to := "(";
        for i  in [ 1, 3 .. Length( tor ) - 1 ]  do
            if i <> 1  then
                Append( to, ", " );
            fi;
            Append( to, tor[i] );
            Append( to, "(" );
            if not tor[i] in [ "wp", "Wp", "ws", "Ws" ]  then
                Append( to, String( tor[i + 1] ) );
            else
                for j  in [ 1 .. Length( tor[i + 1] ) ]  do
                    if j <> 1  then
                        Append( to, "," );
                    fi;
                    Append( to, String( tor[i + 1][j] ) );
                od;
            fi;
            Append( to, ")" );
        od;
        Append( to, ")" );


    elif IsMonomialOrdering( tor )  then
        name := Name( tor );
        name := name{[ 1 .. Position( name, '(' ) - 1 ]};
        if name = "MonomialLexOrdering"  then
            to := "lp";
        elif name = "MonomialGrevlexOrdering"  then
            to := "dp";
        elif name = "MonomialGrlexOrdering"  then
            to := "Dp";
        else
            Error( "the ordering ", tor, " is not yet supported\n" );
        fi;

    else
        Error( "the term ordering ", tor,
               ",\nof the Singular base-ring, is not valid\n" );
    fi;

    return to;
end );



Unbind(ParseGapRingToSingRing);
BindGlobal( "ParseGapRingToSingRing", function ( R )

    local F, str, ipr, mcf, varnums, f, ef, i;

    F:= CoefficientsRing( R );


# Check that the field is supported by Singular

    if Characteristic( F ) > 0  then
        if IsPrimeField( F )  then

            if SingularVersion <= 2003  then
                if Characteristic( F ) > 32003 and Characteristic( F ) <=
                  SingularLimitations.max_char_prime_field  then
                    Error( "only prime fields of char <= 32003 are ",
                     "supported by your version of \nSingular: upgrade it ",
                     "to use prime fields of char <= ",
                     SingularLimitations.max_char_prime_field, ". \n" );
                elif Characteristic( F ) >
                  SingularLimitations.max_char_prime_field  then
                    Error( "only prime fields of char <= 32003 are ",
                     "supported by your version of \nSingular (or prime ",
                     "fields of char <= ",
                     SingularLimitations.max_char_prime_field,
                     " by the latest version.)\n" );
                fi;
            else
                if Characteristic( F ) >
                  SingularLimitations.max_char_prime_field  then
                    Error( "only prime fields of char <= ",
                     SingularLimitations.max_char_prime_field,
                     " are supported by Singular \n" );
                fi;
            fi;

        else

            if Size( F ) > SingularLimitations.max_size_nonprime_field  then
                Error( "Singular supports finite but non-prime fields ",
                 "only if \nof size <= ",
                 SingularLimitations.max_size_nonprime_field, "\n" );
            fi;

        fi;
    else

        if not (HasIsCyclotomicField( F ) and IsCyclotomicField( F ) or
    IsAlgebraicExtension( F ) and LeftActingDomain( F ) = Rationals)  then

           Error( "in Characteristic 0, only CyclotomicField's (including ",
             "Rationals) and\nAlgebraicExtension's of Rationals are ",
             "supported by the Singular interface \nand by Singular\n" );

        fi;

    fi;


# In Singular, a ring declaration is of the form
# ring name = (coefficient_field), (names_of_ring_variables), (ordering);
# possibly followed by a
# minpoly = (poly);


    str := "ring GAP_ring = ( ";


# Calculating "coefficient_field"

    Append( str, String( Characteristic( F ) ) );
    if not IsPrimeField( F )  then
        Append( str, ", q" );
    fi;


# Calculating "), (names_of_ring_variables), "

    ipr := ShallowCopy( IndeterminatesOfPolynomialRing( R ) );

    if HasTermOrdering( R ) and IsMonomialOrdering( TermOrdering( R ) )  then
        mcf := MonomialComparisonFunction( TermOrdering( R ) );
        Sort( ipr, mcf );
        ipr := Reversed( ipr );
    fi;

    varnums := List( ipr, x -> ExtRepPolynomialRatFun( x )[1][1] );
    SetIndeterminateNumbers( R, varnums );

    Append( str, " ), (" );

    for i in [1..Length(varnums)] do
        Append( str, "x_" );
        Append( str, String( i ) );
        if i<>Length(varnums) then Append( str, "," ); fi;
    od;

    Append( str, "), " );


# Calculating "(ordering);"

    if HasTermOrdering( R ) then
        Append( str, ParseGapOrderingToSingOrdering( TermOrdering( R ) ) );
    else
        # the default "dp" is used
        Append( str, "dp" );
    fi;

    Append( str, ";" );


# Calculating " minpoly = (poly);" if not IsPrimeField( F )

    if not IsPrimeField( F )  then

        # Compute a string representing the minimum polynomial of a
        # primitive element of F.

        if HasDefiningPolynomial( F ) and
           IsPrimeField( LeftActingDomain( F ) )  then
            f:= DefiningPolynomial( F );
        elif Characteristic( F ) > 0  then
            f:= MinimalPolynomial( PrimeField(F), PrimitiveRoot(F), 1 );
        elif HasIsCyclotomicField( F ) and IsCyclotomicField( F )  then
            f:= MinimalPolynomial( PrimeField(F), PrimitiveElement(F), 1 );
        fi;
        ef:= ExtRepPolynomialRatFun( f );

        Append( str, " minpoly = " );
        for i in [1,3..Length(ef)-1] do
            if i<>1 then Append( str, "+" ); fi;
            if Characteristic( F ) = 0  then
                Append( str, String( ef[i+1] ) );
            else
                Append( str, String( IntFFE( ef[i+1] ) ) );
            fi;
            if ef[i] <> [] then
                Append( str, "*q^" );
                Append( str, String( ef[i][2] ) );
            fi;
        od;
        Append( str, ";" );
    fi;


# Done

    Append( str, "\n" );
    return str;

end );




BindGlobal( "ParseGapVectorToSingVector", function ( vec )
    local  str, dim, i;
    dim := Length( vec );
    str := "[";
    for i  in [ 1 .. dim ]  do
        Append( str, ParseGapPolyToSingPoly( vec[i] ) );
        if not i = dim  then
            Append( str, "," );
        fi;
        if i mod 50 = 0  then
            Append( str, "\n" );
        fi;
    od;
    Append( str, "]" );
    return str;
end );


BindGlobal( "ParseGapListToSingList", function ( list )
    local  str, dim, i;
    dim := Length( list );
    str := "list( ";
    for i  in [ 1 .. dim ]  do
        Append( str, ConvertGapObjToSingObj( list[i] ) );
        if i < dim  then
            Append( str, ", " );
        fi;
        if i mod 50 = 0  then
            Append( str, "\n" );
        fi;
    od;
    Append( str, " )" );
    return str;
end );



##############################################################################

## PART 5. Parsing Singular --> Gap ##

# Some functions to convert strings that represent Singular
# objects into Gap objects




BindGlobal( "ParseSingNumberToGapNumber", function ( str )

    local   F,  len,  k,  coef,  cf,  exp,  res;

    F := CoefficientsRing( SingularBaseRing );

    if IsPrimeField( F )  then
        return Rat( str ) * One( F );
    fi;

    # get rid of the ()
    if str[1] = '(' and str[Length( str )] = ')'  then
        RemoveElmList( str, 1 );
        RemoveElmList( str, Length( str ) );
    fi;


    # We note that (at least for now) the primitive elements in Singular
    # are always called `q'. That is, for non-prime fields...

    # Here `str' is a string representing a field element of a non-prime
    # field in Singular. This is just a polynomial in `q' over the
    # Rationals. So this function more or less copies the parse function
    # for polynomials, only each time for `q' substituting the primitive
    # root of the ground field.

    res:= Zero( F );

    len:= Length( str );
    k:= 1;

    while k <= len do
        

        # we parse the coefficient of the monomial, and we first discard
        # a possible '+' sign of that coefficient.
    
        coef:="";
        if str[k]='+' then 
            k:=k+1;
        fi;

        # now we get the coefficient itself

        while k <= len and str[k] <> 'q' do
            if str[k] <> '*' then
                Add( coef, str[k] );
            fi;
            k:=k+1;
        od;

        # if the coefficient is 1, then nothing has been done in the
        # previous loop...

        if coef = ""  then
            coef := "1";
        elif coef = "-"  then
            coef := "-1";
        fi;


        cf:= Rat( coef );


        # note that if the monomial only consists of a coefficient
        # (i.e., constant monomial), then we will not enter the next
        # loop, and a [] will be added to mons, just as it should.

        exp:= 0;
        if k <= len and str[k] = 'q' then

            k:= k+1;

            # Now we get the exponent:

            if k <= len and str[k] = '^' then
                exp:= "";
                k:= k+1;
                while k <= len and str[k] in CHARS_DIGITS do
                    Add( exp, str[k] );
                    k:= k+1;
                od;
                exp:= Int( exp );
            else
                exp:= 1;
            fi;
        fi;

        if HasDefiningPolynomial( F ) and
           IsPrimeField( LeftActingDomain( F ) )  then
            res:= res + cf*RootOfDefiningPolynomial( F )^exp;
        elif Characteristic( F ) > 0 then
            res:= res + cf*PrimitiveRoot( F )^exp;
        elif HasIsCyclotomicField( F ) and IsCyclotomicField( F )  then
            res:= res + cf*PrimitiveElement( F )^exp;
        fi;

    od;

    return res;
end );



BindGlobal( "ParseSingPolyToGapPoly", function ( str )
    
    # Here `str' is a string representing a polynomial in Singular
    # format, and we parse it into a GAP polynomial. So a substring of
    # the form `x_21' in `str' means the 21st element from
    # `IndeterminateNumbers( SingularBaseRing )'. 

    local   len,  mons,  cfs,  k,  mon,  coef,  ind,  exp,  
            erep, fam;

    if str = "0"  then
        # we want '[  ]' as ExtRepPolynomialRatFun,
        # not '[ [  ], Zero( CoefficientsRing( SingularBaseRing ) ) ]',
        # as the algorithm would return.
        return Zero( SingularBaseRing );
    fi;

    mons:= [ ];
    cfs:= [ ];

    len:= Length( str );
    k:= 1;
    
    while k <= len do
        
        mon:= [ ];

        # we parse the coefficient of the monomial, and we first discard
        # a possible '+' sign of that coefficient.

        coef:="";
        if str[k]='+' then
            k:=k+1;
        fi;

        # now we get the coefficient itself

        while k <= len and str[k] <> 'x' do
            if str[k] <> '*' or str[k+1] <> 'x' then 
                Add( coef, str[k] );
            fi;  
            k:=k+1;
        od;

        # if the coefficient is 1, then nothing has been done in the
        # previous loop...

        if coef = ""  then
            coef := "1";
        elif coef = "-"  then
            coef := "-1";
        fi;


        Add( cfs, ParseSingNumberToGapNumber( coef ) );


        # note that if the monomial only consists of a coefficient
        # (i.e., constant monomial), then we will not enter the next
        # loop, and a [] will be added to mons, just as it should.

        while k <= len and not str[k] in ['-','+'] do

            # At this point we always have str[k] = 'x'.
            # We parse this piece of monomial and add it to mon.
            # Here str = x_!!, where !! is an index, so if we increase k
            # by 2 we jump to the index.

            k:=k+2;
            ind:= "";
            while k <= len and str[k] in CHARS_DIGITS  do
                Add( ind, str[k] );
                k:=k+1;
            od;

            # Now we get the exponent:

            if k <= len and str[k] = '^' then
                exp:= "";
                k:= k+1;
                while k <= len and str[k] in CHARS_DIGITS do
                    Add( exp, str[k] );
                    k:= k+1;
                od;
                exp:= Int( exp );
            else
                exp:= 1;
            fi;

            Add( mon, IndeterminateNumbers( SingularBaseRing )[Int(ind)] );
            Add( mon, exp );

            if k <= len and str[k]='*' then k:= k+1; fi;
        od;

        Add( mons, mon );
    od;

    fam:= ElementsFamily( FamilyObj( SingularBaseRing ) );

    SortParallel( mons, cfs, fam!.zippedSum[1] );

    # merge mons and cfs...

    erep:= [ ];
    for k in [1..Length(mons)] do
        Add( erep, mons[k] );
        Add( erep, cfs[k] );
    od;

    return PolynomialByExtRepNC( fam, erep );

end );



BindGlobal( "ParseSingProcToGapFunction", function ( string )

    local length, k, parameters, done, pos, pos2, precommand, func;

    length := Length( string );
    if length = 0  then
        return ( function (  ) return; end );
    fi;

    # determine in <string> what are the parameters or arguments, and
    # what is the body of the Singular function
    k := 1;
    parameters := " ";
    done := false;

    repeat
        while string[k] = ' '  do
            k := k + 1;
        od;

        if length > k + 11 and string{[ k .. k + 9 ]} = "parameter "  then
            pos := Position( string, ';' );
            Append( parameters, string{[ k + 10 .. pos - 1 ]} );
            Append( parameters, "," );
            string := string{[ pos + 1 .. length ]};
            length := Length( string );
            k := 1;
        else
            done := true;
        fi;

    until done;
    parameters{[ Length( parameters ) ]} := " ";

# remove Singular comments:
# // comment delimiter. Comment extends to end of line.
# These should not harm
# /* comment delimiter. Starts a comment which ends with */.
# */ comment delimiter. Ends a comment which starts with /*.
# */

    pos := PositionSublist( string, "//" );
    while pos <> fail  do
        pos2 := PositionSublist( string, "\n", pos );
        string := Concatenation( string{[ 1 .. pos - 1 ]}, " \n",
           string{[ pos2 + 1 .. Length( string ) ]} );
        pos := PositionSublist( string, "//" );
    od;

    string := NormalizedWhitespace( string );

    # the next two lines are necessary when the string is sent via a
    # stream
    string := ReplacedString( string, "\"", "\\\"" );
    string := ReplacedString( string, "\\", "\\\\" );
#    string := EscapeCharsInString( string );


    # the definition of the Singular function
    precommand := Concatenation( "proc GAP_proc (", parameters, ") {",
                                 string, "};" );

 if parameters <> " " then
    # the '#' of Singular correspond to the <arg> of Gap (but this may
    # give strange effect when there are both named and unnamed arguments)

    parameters := ReplacedString( parameters, "#", "arg" );

    # change the parameters like "def i, list arg" into "i, arg"
    parameters := SplitString( parameters, "," );
    parameters := List( parameters, x -> SplitString( x, " " ) );
    parameters := List( parameters, x ->Filtered( x, y -> not
                                                     IsEmptyString(y)));
    parameters := List( parameters, x -> x[Length( x )] );
    parameters := JoinStringsWithSeparator( parameters, ", " );
 fi;

    # the definition of the Gap function
    func := Concatenation(
       "function (", parameters, ") \n",
       "    SingularCommand( \"", precommand, "\", \"\" );\n",
       "    return SingularInterface( \"GAP_proc\", [", parameters,
       "] , \"def\" );\n",
       "end;\n" );

    return EvalString( func );

end );



# this function is under construction!
BindGlobal( "ParseSingRingToGapRing", function ( string )
    local p1, p2, char, variables, coeff, to, R;
    p1 := Position( string, '(' );
    p2 := Position( string, ')', p1 );
    char := Int( string{[ p1 + 1 .. p2 - 1 ]} );

    p1 := Position( string, '(', p2 );
    p2 := Position( string, ')', p1 );
    variables := string{[ p1 + 1 .. p2 - 1 ]};
    variables := SplitString( variables, ',' );

    if char = 0  then
        coeff := Rationals;
    else
        coeff := GF( char );
    fi;
    R := PolynomialRing( coeff, variables : old );

    p1 := Position( string, '(', p2 );
    p2 := Position( string, ')', p1 );
    p2 := Position( string, ')', p2 );
    to := string{[ p1 + 1 .. p2 - 1 ]};
    SetTermOrdering( R, to );

    Print( "The conversion of rings from Singular to Gap is under \
construction!\n" );
    return R;
end );




##############################################################################




# This list contains the data types of Singular in (almost) alphabetical
# order, and for each of then the function that check whether a Gap
# object is of that type.

SingularDataTypes := rec(


  def := [ "Objects may be defined without a specific type",
 ReturnFalse, # makes no sense in Gap
 ,
 ],


  ideal := [ "Ideal of a polynomial ring",
 IsPolynomialRingIdeal,
 ParseGapIdealToSingIdeal,
 ],


  int := [ "Variables of type int represent the machine integers and \
are, therefore, limited in their range (e.g., the range is between \
-2147483647 and 2147483647 on 32-bit machines).",
 IsSingularInt,
 String,
 Int
 ],


  intmat := [ "Integer matrices are matrices with integer entries.",
 obj -> IsMatrix( obj ) and
   ForAll( obj, x -> ForAll( x, IsSingularInt ) ),
 ParseGapIntmatToSingIntmat,
 ],


  intvec := [ "Variables of type intvec are lists of integers.",
 obj -> IsRowVector( obj ) and ForAll( obj, IsSingularInt ),
 ParseGapIntvecToSingIntvec,
 obj -> List( SplitString( obj, ',', ' ' ), Int ),
  ],


  bigint := [ "Variables of type bigint represent arbitrarily long integers.",
 obj -> IsInt( obj ) and ( SingularVersion >= 4000 or
  # because it may be still unknown
  SingularVersion = 0 ),
 String,
 Int
 ],


  bigintmat := [ "Variables of type bigintmat are matrices with arbitrarily \
long integer entries.",
 obj -> IsMatrix( obj ) and
   ForAll( obj, x -> ForAll( x, IsInt ) ) and
  ( SingularVersion >= 4000 or SingularVersion = 0 ),
 ParseGapIntmatToSingBigintmat,
 ],


  bigintvec := [ "Variables of type bigintvec are lists of arbitrarily long \
integers.",
 obj -> IsRowVector( obj ) and ForAll( obj, IsInt ) and
  ( SingularVersion >= 4400 or SingularVersion = 0 ),
 ParseGapIntvecToSingBigintvec,
 obj -> List( SplitString( obj, ',', ' ' ), Int ),
 ],


  link := [ "Links are the communication channels of SINGULAR, i.e., \
something SINGULAR can write to and/or read from.",
 ReturnFalse, # not implemented
 ,
 ],


  map := [ "Maps are ring maps from a preimage ring into the basering.",
 obj -> IsAlgebraGeneralMapping( obj )
  and HasSource( obj ) and IsPolynomialRing( Source( obj ) )
  and HasRange( obj ) and IsPolynomialRing( Range( obj ) )
  and HasMappingGeneratorsImages( obj ),

 function ( obj )
            Error( "sorry: the interface to Singular do not support yet ",
           "the type \"map\".\n(Your code to support it will be welcome!)\n");
            return fail;
 end,
 ],


  matrix := [ "Objects of type matrix are matrices with polynomial entries.",
 obj -> IsMatrix( obj ) and ForAll( obj, x ->
   ForAll( x, y -> IsSingularPoly( y ) ) ),

 function ( obj )
            local  module;
            module := LeftModuleByGenerators( SingularBaseRing,
               TransposedMat( obj ) );
            return
             Concatenation( "matrix(", ParseGapModuleToSingModule( module ),
               ")" );
 end,
 ],


  module := [ "Modules are submodules of a free module over the basering \
with basis gen(1), gen(2), ... .",
 obj -> HasIsRowModule( obj ) and IsRowModule( obj ) and
   ForAll( GeneratorsOfLeftOperatorAdditiveGroup( obj ),
   x -> ForAll( x, y -> IsPolynomial( y ) ) ),
 ParseGapModuleToSingModule,
 ],


  number := [ "Numbers are elements from the coefficient field (or \
ground field).",
 obj -> obj in CoefficientsRing( SingularBaseRing ),
 ParseGapNumberToSingNumber,
 ParseSingNumberToGapNumber
 ],


  poly := [ "Polynomials are the basic data for all main algorithms in \
SINGULAR.",
 IsSingularPoly,
 ParseGapPolyToSingPoly,
 ParseSingPolyToGapPoly
 ],


  proc := [ "Procedures are sequences of SINGULAR commands in a special \
format.",
 IsFunction,

 function( obj )
        Error( "sorry: the interface to Singular do not support ",
         "the type \"proc\".\n(Any idea to support it will be welcome!)\n" );
        return fail;
 end,
 ParseSingProcToGapFunction
 ],


  qring := [
 "SINGULAR offers the opportunity to calculate in quotient rings \
(factor rings), i.e., rings modulo an ideal.",
 ReturnFalse, # not supported by Gap
 ,
 ],


  resolution := [ "The resolution type is intended as an intermediate \
representation which internally retains additional information obtained \
during computation of resolutions.",
 ReturnFalse, # not supported by Gap
 ,
 ],


  ring := [ "Rings are used to describe properties of polynomials, ideals \
etc. Almost all computations in SINGULAR require a basering.",
 IsPolynomialRing,

 function( obj )
        if obj <> SingularBaseRing  then
            SingularSetBaseRing( obj );
        fi;
        return "GAP_ring";
 end,
# ParseSingRingToGapRing
 ],


  string := [ "string (7 bit clean)",
 IsString and IsStringRep,
 function(obj)
        # the next two lines are necessary when the string is sent via a
        # stream
        obj := ReplacedString( obj, "\\", "\\\\" );
        obj := ReplacedString( obj, "\"", "\\\"" );
#        obj := EscapeCharsInString( obj );
 return Concatenation("\"", obj,"\"");
 end,
 IdFunc
 ],


  vector := [ "Vectors are elements of a free module over the basering \
with basis gen(1), gen(2), ... .",
 obj -> IsRowVector( obj ) and ForAll( obj, y ->
   IsSingularPoly( y ) ),
 ParseGapVectorToSingVector,
 ],


# "list" must be done after intmat, intvec, matrix, string, vector
  list := [ "Lists are arrays whose elements can be of any type \
(including ring and qring).",
 obj -> IsDenseList( obj ) and
   ForAll( obj, y -> SingularType(y) <> fail ),
 ParseGapListToSingList,
 ],


# other or new types

  \?unknown\ type\? := [ "For internal use only is the type \
\"?unknown type?\".",
 ReturnFalse, # makes no sense in Gap
 ],


  none := [ "Functions without a return value are specified there to \
have a return type 'none', see \"3.5.1 General command syntax\".",
 ReturnFalse, # makes no sense in Gap
 ],


  package := [ "The data type package is used to group identifiers into \
collections. Introduced in Singular 3.0.0.",
 ReturnFalse, # makes no sense in Gap
 ]

);


# The SingularDataTypes record is traversed in ConvertGapObjToSingObj, and the
# order of the entries is important. We can't guarantee that GAP will present 
# the record entries in the order that we have given them (a change introduced 
# in GAP 4.5). So we here list the order in which they should be tested

SingularDataTypeTestOrder := [ "def", "ideal", "int", "intmat", "intvec",
  "bigint", "bigintvec", "bigintmat", "link", "map", "matrix", "module",
  "number", "poly", "proc", "qring", "resolution", "ring", "string", "vector",
  "list", "?unknown type?", "none", "package" ];

# And check for sanity that this set is same as the names in the record
if Set(SingularDataTypeTestOrder) <> Set(RecNames(SingularDataTypes)) then
  Error( "Singular<->GAP datatypes database error!\n" );
fi;






##############################################################################


# This function determines the Singular type of a Gap object
Unbind(SingularType);
BindGlobal( "SingularType", function ( obj )
    local  i;
    for i  in SingularDataTypeTestOrder  do
        if SingularDataTypes.(i)[2]( obj )  then
            return i;
        fi;
    od;
    return fail;
end );



##############################################################################


Unbind(ConvertGapObjToSingObj);
BindGlobal( "ConvertGapObjToSingObj", function ( obj )

    local type;

    if HasCurrentRingSingularIdentifier( obj )  then
        return SingularIdentifier( obj );
    fi;

    # Usually the interface determines the type, but this can be
    # overridden specifying it like the following example:
    # rec( Object := [ 1, 2 ], SingularType := "list" );
    # otherwise [ 1, 2 ] will be of type "intvec".

    if IsRecord( obj ) and IsBound( obj.SingularType ) and
       IsBound( obj.Object )  then
        type := obj.SingularType;
        obj := obj.Object;
    else
        type := SingularType( obj );
    fi;

    if type in RecNames(SingularDataTypes)  and
       IsBound( SingularDataTypes.(type)[3])  then
        return SingularDataTypes.(type)[3]( obj );
    else
       Error( "sorry: Singular, or the interface to Singular, or the ",
              "current \nSingularBaseRing, do not support the object " ,
              obj, ".\nDid you remember to use 'SingularSetBaseRing' ?\n" );
        return fail;
    fi;

end );




##############################################################################


# This function converts the string <obj> (that represent a Singular
# object of type <type_output>) into a Gap object. It may be necessary
# to ask Singular for more information about this object: <singname> is
# the name in Singular of this object.

ConvertSingObjToGapObj := function ( obj, type_output, singname )

    local command, ideal, idealno, module, moduleno, mat, name, list,
         nrows, ncols, r, length, type, string, i;

    if type_output in RecNames( SingularDataTypes ) and 
       IsBound( SingularDataTypes.(type_output)[4])  then
        if NumberArgumentsFunction( SingularDataTypes.(type_output)[4] ) = 2
             then
            return SingularDataTypes.(type_output)[4]( obj, singname );
        else
            return SingularDataTypes.(type_output)[4]( obj );
        fi;
    fi;


    # def
    if type_output = "def"  then
    # in this case ask Singular for the type
        command := Concatenation( "typeof( ", singname, " );" );
        type_output := SingCommandInStreamOutStream( "", command );
        Info( InfoSingular, 1, "Singular output of type \"", type_output,
              "\"" );
        return ConvertSingObjToGapObj( obj, type_output, singname );

    # ideal
    elif type_output = "ideal"  then
        ideal := Ideal( SingularBaseRing, List( SplitString( obj, ',' ),
                                      ParseSingPolyToGapPoly ) );

        if SingularCommand <> SingCommandUsingProcess  then

            # set the SingularIdentifier of the returned ideal
            idealno:= SingularNames.ideal+1;
            SingularNames.ideal:= idealno;
            name:= "GAP_ideal_"; Append( name, String( idealno ) );

            SetSingularIdentifier( ideal, name );

            command:= "ideal GAP_ideal_";
            Append( command, String( idealno ) );
            Append( command, " = " );
            Append( command, singname );
            SingCommandInStreamOutStream( command, "" );

        fi;

        return ideal;


    # intmat / bigintmat
    elif type_output = "intmat" or type_output = "bigintmat" then
        list:= List( SplitString( obj, ',' ,' '), Int );
        command := Concatenation( "nrows( ", singname, " );" );
        nrows := Int( SingCommandInStreamOutStream( "", command ) );
        command := Concatenation( "ncols( ", singname, " );" );
        ncols := Int( SingCommandInStreamOutStream( "", command ) );
        return List( [ 1 .. nrows ], x ->
                     list{[ (x - 1) * ncols + 1 .. x * ncols ]} );

    # link
    elif type_output = "link"  then
        r := rec( object := "link" );
        command := Concatenation( "status( ", singname, ", \"name\" );" );
--> --------------------

--> maximum size reached

--> --------------------

[ Dauer der Verarbeitung: 0.21 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge