|
#############################################################################
##
#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)
]
|