r"""
Interface to GAP3
This module implements an interface to GAP3.
AUTHORS:
- Franco Saliola (February 2010)
.. WARNING::
GAP3 is not distrubuted with Sage. You need to install it
separately; see the section `Obtaining GAP3`_.
Obtaining GAP3
--------------
The GAP3 interface will only work if GAP3 is installed on your computer.
Here are some ways to obtain GAP3:
- There is an optional Sage package providing GAP3 pre-packaged
with several GAP3 packages:
http://trac.sagemath.org/sage_trac/ticket/8906
- Frank Luebeck maintains a GAP3 Linux executable, optimized
for i686 and statically linked for jobs of 2 GByte or more:
http://www.math.rwth-aachen.de/~Frank.Luebeck/gap/GAP3
- Jean Michel maintains a version of GAP3 pre-packaged with
CHEVIE and VKCURVE. It can be obtained here:
http://people.math.jussieu.fr/~jmichel/chevie/chevie.html
- Finally, you can download GAP3 from the GAP website below. Since GAP3
is no longer supported, it may not be easy to install this version.
http://www.gap-system.org/Gap3/Download3/download.html
Changing which GAP3 is used
---------------------------
.. WARNING::
There is a bug in the pexpect module (see trac ticket #8471) that
prevents the following from working correctly. For now, just make sure
that ``gap3`` is in your ``PATH``.
Sage assumes that GAP3 can be launched with the command ``gap3``; that is,
Sage assumes that the command ``gap3`` is in your ``PATH``. If this is not
the case, then you can start GAP3 using the following command::
sage: gap3 = Gap3(command='/usr/local/bin/gap3') #not tested
Functionality and Examples
--------------------------
The interface to GAP3 offers the following functionality.
#. ``gap3(expr)`` - Evaluation of arbitrary GAP3 expressions, with the
result returned as a Sage object wrapping the corresponding GAP3 element::
sage: a = gap3('3+2') #optional - gap3
sage: a #optional - gap3
5
sage: type(a) #optional - gap3
<class 'sage.interfaces.gap3.GAP3Element'>
::
sage: S5 = gap3('SymmetricGroup(5)') #optional - gap3
sage: S5 #optional - gap3
Group( (1,5), (2,5), (3,5), (4,5) )
sage: type(S5) #optional - gap3
<class 'sage.interfaces.gap3.GAP3Record'>
This provides a Pythonic interface to GAP3. If ``gap_function`` is the
name of a GAP3 function, then the syntax ``gap_element.gap_function()``
returns the ``gap_element`` obtained by evaluating the command
``gap_function(gap_element)`` in GAP3::
sage: S5.Size() #optional - gap3
120
sage: S5.CharTable() #optional - gap3
CharTable( Group( (1,5), (2,5), (3,5), (4,5) ) )
Alternatively, you can instead use the syntax
``gap3.gap_function(gap_element)``::
sage: gap3.DerivedSeries(S5) #optional - gap3
[ Group( (1,5), (2,5), (3,5), (4,5) ),
Subgroup( Group( (1,5), (2,5), (3,5), (4,5) ),
[ (1,2,5), (1,3,5), (1,4,5) ] ) ]
If ``gap_element`` corresponds to a GAP3 record, then
``gap_element.recfield`` provides a means to access the record element
corresponding to the field ``recfield``::
sage: S5.IsRec() #optional - gap3
true
sage: S5.recfields() #optional - gap3
['isDomain', 'isGroup', 'identity', 'generators', 'operations',
'isPermGroup', 'isFinite', '1', '2', '3', '4', 'degree']
sage: S5.identity #optional - gap3
()
sage: S5.degree #optional - gap3
5
sage: S5.1 #optional - gap3
(1,5)
sage: S5.2 #optional - gap3
(2,5)
#. By typing ``%gap3`` or ``gap3.interact()`` at the command-line, you can
interact directly with the underlying GAP3 session.
::
sage: gap3.interact() #not tested
--> Switching to Gap3 <--
gap3:
#. You can start a new GAP3 session as follows::
sage: gap3.console() #not tested
######## Lehrstuhl D fuer Mathematik
### #### RWTH Aachen
## ##
## # ####### #########
## # ## ## # ##
## # # ## # ##
#### ## ## # # ##
##### ### ## ## ## ##
######### # ######### #######
# #
## Version 3 #
### Release 4.4 #
## # 18 Apr 97 #
## #
## # Alice Niemeyer, Werner Nickel, Martin Schoenert
## # Johannes Meier, Alex Wegner, Thomas Bischops
## # Frank Celler, Juergen Mnich, Udo Polis
### ## Thomas Breuer, Goetz Pfeiffer, Hans U. Besche
###### Volkmar Felsch, Heiko Theissen, Alexander Hulpke
Ansgar Kaup, Akos Seress, Erzsebet Horvath
Bettina Eick
For help enter: ?<return>
gap>
#. The interface also has access to the GAP3 help system::
sage: gap3.help('help', pager=False) #not tested
Help _______________________________________________________...
This section describes together with the following sections the GAP
help system. The help system lets you read the manual interactively...
Common Pitfalls
---------------
#. If you want to pass a string to GAP3, then you need to wrap it in
single quotes as follows::
sage: gap3('"This is a GAP3 string"') #optional - gap3
"This is a GAP3 string"
This is particularly important when a GAP3 package is loaded via the
``RequirePackage`` method (note that one can instead use the
``load_package`` method)::
sage: gap3.RequirePackage('"chevie"') #optional - gap3chevie
W... to the CHEVIE package, ...
Examples
--------
Load a GAP3 package::
sage: gap3.load_package("chevie") #optional - gap3chevie
sage: gap3.version() # random #optional - gap3
'lib: v3r4p4 1997/04/18, src: v3r4p0 1994/07/10, sys: usg gcc ansi'
Working with GAP3 lists. Note that GAP3 lists are 1-indexed::
sage: L = gap3([1,2,3]) #optional - gap3
sage: L[1] #optional - gap3
1
sage: L[2] #optional - gap3
2
sage: 3 in L #optional - gap3
True
sage: 4 in L #optional - gap3
False
sage: m = gap3([[1,2],[3,4]]) #optional - gap3
sage: m[2,1] #optional - gap3
3
sage: [1,2] in m #optional - gap3
True
sage: [3,2] in m #optional - gap3
False
sage: gap3([1,2]) in m #optional - gap3
True
Controlling variable names used by GAP3::
sage: gap3('2', name='x') #optional - gap3
2
sage: gap3('x') #optional - gap3
2
sage: gap3.unbind('x') #optional - gap3
sage: gap3('x') #optional - gap3
Traceback (most recent call last):
...
TypeError: Gap3 produced error output
Error, Variable: 'x' must have a value
...
"""
from sage.interfaces.expect import Expect
from sage.interfaces.gap import Gap_generic, GapElement_generic
gap3_cmd = 'gap3'
class Gap3(Gap_generic):
r"""
A simple Expect interface to GAP3.
EXAMPLES::
sage: from sage.interfaces.gap3 import Gap3
sage: gap3 = Gap3(command='gap3')
TESTS::
sage: gap3(2) == gap3(3) #optional - gap3
False
sage: gap3(2) == gap3(2) #optional - gap3
True
sage: gap3.trait_names() #optional - gap3
[]
We test the interface behaves correctly after a keyboard interrupt::
sage: gap3(2) #optional - gap3
2
sage: try:
... gap3._keyboard_interrupt()
... except:
... pass #optional - gap3
Interrupting Gap3...
sage: gap3(2) #optional - gap3
2
We test that the interface busts out of GAP3's break loop correctly::
sage: f = gap3('function(L) return L[0]; end;;') #optional - gap3
sage: f([1,2,3]) #optional - gap3
Traceback (most recent call last):
...
RuntimeError: Gap3 produced error output
Error, List Element: <position> must be a positive integer at
return L[0] ...
AUTHORS:
- Franco Saliola (Feb 2010)
"""
def __init__(self, command=gap3_cmd):
r"""
Initialize the GAP3 interface and start a session.
INPUT:
- command - string (default "gap3"); points to the gap3
executable on your system; by default, it is assumed the
executable is in your path.
EXAMPLES::
sage: gap3 = Gap3() #optional - gap3
sage: gap3.is_running()
False
sage: gap3._start() #optional - gap3
sage: gap3.is_running() #optional - gap3
True
"""
self.__gap3_command_string = command
Expect.__init__(self,
name='gap3',
prompt='gap> ',
command=self.__gap3_command_string + " -p -y 500",
server=None,
ulimit=None,
maxread=100000,
script_subdirectory=None,
restart_on_ctrlc=True,
verbose_start=False,
init_code=[],
max_startup_time=None,
logfile=None,
eval_using_file_cutoff=100,
do_cleaner=True,
remote_cleaner=False,
path=None)
def _start(self):
r"""
EXAMPLES::
sage: gap3 = Gap3() #optional - gap3
sage: gap3.is_running()
False
sage: gap3._start() #optional - gap3
sage: gap3.is_running() #optional - gap3
True
sage: gap3.quit() #optional - gap3
"""
n = self._session_number
Expect._start(self)
self._compiled_full_pattern = self._expect.compile_pattern_list([
'@p\d+\.','@@','@[A-Z]','@[123456!"#$%&][^+]*\+', '@e','@c',
'@f','@h','@i','@m','@n','@r','@s\d','@w.*\+','@x','@z'])
self._compiled_small_pattern = self._expect.compile_pattern_list('@J')
def _object_class(self):
r"""
Return the class used for constructing GAP3 elements.
TESTS::
sage: gap3._object_class()
<class 'sage.interfaces.gap3.GAP3Element'>
"""
return GAP3Element
def _execute_line(self, line, wait_for_prompt=True, expect_eof=False):
r"""
Execute a line of code in GAP3 and parse the output.
TESTS:
Test that syntax errors are handled correctly::
sage: gap3('syntax error', name='x') #optional - gap3
Traceback (most recent call last):
...
TypeError: Gap3 produced error output
Syntax error: ; expected
x:=syntax error;
^
gap>
...
Test that error messages are detected and reported correctly::
sage: gap3.SymmetricGroup(5,3) #optional - gap3
Traceback (most recent call last):
...
RuntimeError: Gap3 produced error output
Error, Record: left operand must be a record at
return arg[1].operations.SymmetricGroup( arg[1], arg[2] ) ... in
SymmetricGroup( ..., ... ) called from
main loop
brk> quit;
...
Test that break loops are detected and exited properly::
sage: f = gap3('function(L) return L[0]; end;;') #optional - gap3
sage: f([1,2,3]) #optional - gap3
Traceback (most recent call last):
...
RuntimeError: Gap3 produced error output
Error, List Element: <position> must be a positive integer at
return L[0] ... in
... called from
main loop
brk> quit;
...
"""
normal_output, error_output = \
super(Gap3, self)._execute_line(line, wait_for_prompt=True, expect_eof=False)
if normal_output.startswith("Syntax error:"):
normal_output, error_output = "", normal_output
return (normal_output, error_output)
def help(self, topic, pager=True):
r"""
Print help on the given topic.
INPUT:
- ``topic`` -- string
EXAMPLES::
sage: gap3.help('help', pager=False) #optional - gap3
Help _______________________________________________________...
<BLANKLINE>
This section describes together with the following sectio...
help system. The help system lets you read the manual inter...
::
sage: gap3.help('SymmetricGroup', pager=False) #optional - gap3
no section with this name was found
TESTS::
sage: m = gap3([[1,2,3],[4,5,6]]); m #optional - gap3
[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
sage: gap3.help('help', pager=False) #optional - gap3
Help _______________________________________________________...
sage: m #optional - gap3
[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
sage: m.Print() #optional - gap3
[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
sage: gap3.help('Group', pager=False) #optional - gap3
Group ______________________________________________________...
sage: m #optional - gap3
[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
sage: m.Print() #optional - gap3
[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
"""
import pexpect
if self._expect is None:
self._start()
E = self._expect
helptext = []
E.sendline("? %s" % topic)
E.expect("\r\n")
E.expect("\r\n")
while True:
try:
x = E.expect_list(self._compiled_full_pattern, timeout=2)
except pexpect.TIMEOUT:
break
if x == 1:
helptext.append('@')
helptext.append(E.before)
elif x == 2:
helptext.append(chr(ord(E.after[1:2])-ord('A')+1))
helptext.append(E.before)
elif x == 10:
break
elif x==11:
E.expect_list(self._compiled_small_pattern)
helptext = "".join(helptext).strip()
if pager is True:
import IPython.genutils
IPython.genutils.page(helptext)
else:
print helptext
def cputime(self, t=None):
r"""
Returns the amount of CPU time that the GAP session has used in
seconds. If ``t`` is not None, then it returns the difference
between the current CPU time and ``t``.
EXAMPLES::
sage: t = gap3.cputime() #optional - gap3
sage: t #random #optional - gap3
0.02
sage: gap3.SymmetricGroup(5).Size() #optional - gap3
120
sage: gap3.cputime() #random #optional - gap3
0.14999999999999999
sage: gap3.cputime(t) #random #optional - gap3
0.13
"""
if t is not None:
return self.cputime() - t
else:
return eval(self.eval('Runtime();'))/1000.0
def console(self):
r"""
Spawn a new GAP3 command-line session.
EXAMPLES::
sage: gap3.console() #not tested
######## Lehrstuhl D fuer Mathematik
### #### RWTH Aachen
## ##
## # ####### #########
## # ## ## # ##
## # # ## # ##
#### ## ## # # ##
##### ### ## ## ## ##
######### # ######### #######
# #
## Version 3 #
### Release 4.4 #
## # 18 Apr 97 #
## #
## # Alice Niemeyer, Werner Nickel, Martin Schoenert
## # Johannes Meier, Alex Wegner, Thomas Bischops
## # Frank Celler, Juergen Mnich, Udo Polis
### ## Thomas Breuer, Goetz Pfeiffer, Hans U. Besche
###### Volkmar Felsch, Heiko Theissen, Alexander Hulpke
Ansgar Kaup, Akos Seress, Erzsebet Horvath
Bettina Eick
For help enter: ?<return>
gap>
"""
os.system(self.__gap3_command_string)
def _install_hints(self):
r"""
TESTS::
sage: gap3 = Gap3(command='/wrongpath/gap3')
sage: gap3('3+2')
Traceback (most recent call last):
...
TypeError: Unable to start gap3 because the command ... failed.
<BLANKLINE>
Your attempt to start GAP3 failed, either because you do not have
have GAP3 installed, or because it is not configured correctly.
<BLANKLINE>
- If you do not have GAP3 installed, then you must download ...
sage: print gap3._install_hints()
<BLANKLINE>
Your attempt to start GAP3 failed, either because you do not have
have GAP3 installed, or because it is not configured correctly.
<BLANKLINE>
- If you do not have GAP3 installed, then you must download ...
"""
return r"""
Your attempt to start GAP3 failed, either because you do not have
have GAP3 installed, or because it is not configured correctly.
- If you do not have GAP3 installed, then you must download and
install it yourself because it is not distrubuted with Sage.
Here are some ways to obtain GAP3:
- There is an optional Sage package providing GAP3 pre-packaged
with several GAP3 packages:
http://trac.sagemath.org/sage_trac/ticket/8906
- Frank Luebeck maintains a GAP3 Linux executable, optimized
for i686 and statically linked for jobs of 2 GByte or more:
http://www.math.rwth-aachen.de/~Frank.Luebeck/gap/GAP3
- Jean Michel maintains a version of GAP3 pre-packaged with
CHEVIE and VKCURVE. It can be obtained here:
http://people.math.jussieu.fr/~jmichel/chevie/chevie.html
- Finally, you can download GAP3 from the GAP website below. Since
GAP3 is no longer an officially supported distribution of GAP, it
may not be easy to install this version.
http://www.gap-system.org/Gap3/Download3/download.html
- If you have GAP3 installed, then perhaps it is not configured
correctly. Sage assumes that you can start GAP3 with the command
%s. Alternatively, you can use the following command
to point Sage to the correct command for your system.
gap3 = Gap3(command='/usr/local/bin/gap3')
""" % self.__gap3_command_string
gap3 = Gap3()
class GAP3Element(GapElement_generic):
r"""
A GAP3 element
.. NOTE::
If the corresponding GAP3 element is a GAP3 record,
then the class is changed to a ``GAP3Record``.
INPUT:
- ``parent`` -- the GAP3 session
- ``value`` -- the GAP3 command as a string
- ``is_name`` -- bool (default: False); if True, then ``value`` is
the variable name for the object
- ``name`` -- str (default: ``None``); the variable name to use for the
object. If ``None``, then a variable name is generated.
.. NOTE::
If you pass ``E``, ``X`` or ``Z`` for ``name``, then an error is
raised because these are sacred variable names in GAP3 that should
never be redefined. Sage raises an error because GAP3 does not!
EXAMPLES::
sage: from sage.interfaces.gap3 import GAP3Element #optional - gap3
sage: gap3 = Gap3() #optional - gap3
sage: GAP3Element(gap3, value='3+2') #optional - gap3
5
sage: GAP3Element(gap3, value='sage0', is_name=True) #optional - gap3
5
TESTS::
sage: GAP3Element(gap3, value='3+2', is_name=False, name='X') #optional - gap3
Traceback (most recent call last):
...
ValueError: you are attempting to redefine X; but you should never redefine E, X or Z in gap3 (because things will break!)
AUTHORS:
- Franco Saliola (Feb 2010)
"""
def __init__(self, parent, value, is_name=False, name=None):
r"""
See ``GAP3Element`` for full documentation.
EXAMPLES::
sage: from sage.interfaces.gap3 import GAP3Element #optional - gap3
sage: gap3 = Gap3() #optional - gap3
sage: GAP3Element(gap3, value='3+2') #optional - gap3
5
sage: GAP3Element(gap3, value='sage0', is_name=True) #optional - gap3
5
TESTS::
sage: GAP3Element(gap3, value='3+2', is_name=False, name='X') #optional - gap3
Traceback (most recent call last):
...
ValueError: you are attempting to redefine X; but you should never redefine E, X or Z in gap3 (because things will break!)
"""
if name in ["E","X","Z"]:
raise ValueError, "you are attempting to redefine %s; but you should never redefine E, X or Z in gap3 (because things will break!)" % name
super(GAP3Element, self).__init__(parent, value, is_name, name)
if parent.eval("IsRec(%s)" % self._name) == "true":
self.__class__ = GAP3Record
def __getitem__(self, n):
r"""
EXAMPLES::
sage: l = gap3('[1,2,3]') #optional - gap3
sage: l[1] #optional - gap3
1
sage: a = gap3([1,2,3]) #optional - gap3
sage: a[1] #optional - gap3
1
sage: m = gap3([[1,2,3],[4,5,6],[7,8,9]]) #optional - gap3
sage: m[1,3] #optional - gap3
3
sage: m[2][1] #optional - gap3
4
"""
gap3_session = self._check_valid()
if not isinstance(n, tuple):
return gap3_session.new('%s[%s]'%(self.name(), n))
else:
return gap3_session.new('%s%s'%(self.name(), ''.join(['[%s]'%x for x in n])))
def _latex_(self):
r"""
EXAMPLES::
sage: s = gap("[[1,2], [3/4, 5/6]]")
sage: s._latex_()
'\\left(\\begin{array}{rr} 1&2\\\\ 3/4&\\frac{5}{6}\\\\ \\end{array}\\right)'
sage: latex(s)
\left(\begin{array}{rr} 1&2\\ 3/4&\frac{5}{6}\\ \end{array}\right)
"""
gap3_session = self._check_valid()
try:
s = gap3_session.eval('FormatLaTeX(%s)'%self.name())
s = s.replace('\\\\','\\').replace('"','')
s = s.replace('%\\n',' ')
return s
except RuntimeError:
return str(self)
class GAP3Record(GAP3Element):
r"""
A GAP3 record
.. NOTE::
This class should not be called directly, use GAP3Element instead.
If the corresponding GAP3 element is a GAP3 record, then the class
is changed to a ``GAP3Record``.
AUTHORS:
- Franco Saliola (Feb 2010)
"""
def recfields(self):
r"""
Return a list of the fields for the record. (Record fields are akin
to object attributes in Sage.)
OUTPUT:
- list of strings - the field records
EXAMPLES::
sage: S5 = gap3.SymmetricGroup(5) #optional - gap3
sage: S5.recfields() #optional - gap3
['isDomain', 'isGroup', 'identity', 'generators',
'operations', 'isPermGroup', 'isFinite', '1', '2',
'3', '4', 'degree']
sage: S5.degree #optional - gap3
5
"""
gap3_session = self._check_valid()
if not hasattr(self, "_gap_recfields"):
s = str(gap3_session.eval("RecFields(%s)" % self._name))
s = s.strip('[] ').replace('\n','')
self._gap_recfields = [ss.strip('" ') for ss in s.split(',')]
return getattr(self,"_gap_recfields")
def operations(self):
r"""
Return a list of the GAP3 operations for the record.
OUTPUT:
- list of strings - operations of the record
EXAMPLES::
sage: S5 = gap3.SymmetricGroup(5) #optional - gap3
sage: S5.operations() #optional - gap3
[..., 'NormalClosure', 'NormalIntersection', 'Normalizer',
'NumberConjugacyClasses', 'PCore', 'Radical', 'SylowSubgroup',
'TrivialSubgroup', 'FusionConjugacyClasses', 'DerivedSeries', ...]
sage: S5.DerivedSeries() #optional - gap3
[ Group( (1,5), (2,5), (3,5), (4,5) ),
Subgroup( Group( (1,5), (2,5), (3,5), (4,5) ),
[ (1,2,5), (1,3,5), (1,4,5) ] ) ]
"""
gap3_session = self._check_valid()
if not hasattr(self,"_gap_operations"):
s = str(gap3_session.eval("RecFields(%s.operations)" % self._name))
s = s.strip('[] ').replace('\n','')
self._gap_operations = [ss.strip('" ') for ss in s.split(',')]
return getattr(self,"_gap_operations")
def __getattr__(self, attrname):
r"""
OUTPUT:
- ``GAP3Record`` -- if ``attrname`` is a field of the GAP record
- ``ExpectFunction`` -- if ``attrname`` is the name of a GAP3 function
EXAMPLES::
sage: S5 = gap3.SymmetricGroup(5) #optional - gap3
sage: S5.__getattr__('Size') #optional - gap3
Size
sage: gap3.IsFunc(S5.__getattr__('Size')) #optional - gap3
true
sage: S5.__getattr__('generators') #optional - gap3
[ (1,5), (2,5), (3,5), (4,5) ]
"""
gap3_session = self._check_valid()
if attrname[:1] == "_":
raise AttributeError
if attrname in self.recfields():
return gap3_session.new('%s.%s' % (self.name(), attrname))
return gap3_session._function_element_class()(self, attrname)
def trait_names(self):
r"""
Defines the list of methods and attributes that will appear for tab
completion.
OUTPUT:
- list of strings -- the available fields and operations of the
record
EXAMPLES::
sage: S5 = gap3.SymmetricGroup(5) #optional - gap3
sage: S5.trait_names() #optional - gap3
[..., 'ConjugacyClassesTry', 'ConjugateSubgroup', 'ConjugateSubgroups',
'Core', 'DegreeOperation', 'DerivedSeries', 'DerivedSubgroup',
'Difference', 'DimensionsLoewyFactors', 'DirectProduct', ...]
"""
if not hasattr(self, "__trait_names"):
self.__trait_names = self.recfields() + self.operations()
self.__trait_names.sort()
return self.__trait_names
import os
def gap3_console():
r"""
Spawn a new GAP3 command-line session.
EXAMPLES::
sage: gap3.console() #not tested
######## Lehrstuhl D fuer Mathematik
### #### RWTH Aachen
## ##
## # ####### #########
## # ## ## # ##
## # # ## # ##
#### ## ## # # ##
##### ### ## ## ## ##
######### # ######### #######
# #
## Version 3 #
### Release 4.4 #
## # 18 Apr 97 #
## #
## # Alice Niemeyer, Werner Nickel, Martin Schoenert
## # Johannes Meier, Alex Wegner, Thomas Bischops
## # Frank Celler, Juergen Mnich, Udo Polis
### ## Thomas Breuer, Goetz Pfeiffer, Hans U. Besche
###### Volkmar Felsch, Heiko Theissen, Alexander Hulpke
Ansgar Kaup, Akos Seress, Erzsebet Horvath
Bettina Eick
For help enter: ?<return>
gap>
"""
os.system(gap3_cmd)
def gap3_version():
r"""
Return the version of GAP3 that you have in your PATH on your computer.
EXAMPLES::
sage: gap3_version() # random, optional - gap3
'lib: v3r4p4 1997/04/18, src: v3r4p0 1994/07/10, sys: usg gcc ansi'
"""
return gap3.eval('VERSION')[1:-1]