Other stuff

Macros

GAP.@gapMacro
@gap <expr>
@gap(<expr>)

Execute <expr> directly in GAP, as if GAP.evalstr("<expr>") was called. This can be used for creating GAP literals directly from Julia.

Examples

julia> @gap [1,2,3]
GAP: [ 1, 2, 3 ]

julia> @gap SymmetricGroup(3)
GAP: Sym( [ 1 .. 3 ] )

julia> @gap(SymmetricGroup)(3)
GAP: Sym( [ 1 .. 3 ] )

Note that the last two examples have a slight syntactical, and therefore also a semantical difference. The first one executes the string SymmetricGroup(3) directly inside GAP. The second example returns the function SymmetricGroup via @gap(SymmetricGroup), then calls that function with the argument 3.

Due to Julia's way of handing over arguments into the code of macros, not all expressions representing valid GAP code can be processed. For example, the GAP syntax of permutations consisting of more than one cycle cause problems, as well as the GAP syntax of non-dense lists.

julia> @gap (1,2,3)
GAP: (1,2,3)

julia> @gap (1,2)(3,4)
ERROR: LoadError: Error thrown by GAP: Error, no method found! For debugging hints type ?Recovery from NoMethodFound
[...]

julia> @gap [ 1,, 2 ]
ERROR: syntax: unexpected ","
[...]

Note also that a string argument gets evaluated with GAP.evalstr.

julia> @gap "\"abc\""
GAP: "abc"

julia> @gap "[1,,2]"
GAP: [ 1,, 2 ]

julia> @gap "(1,2)(3,4)"
GAP: (1,2)(3,4)
source
GAP.@g_strMacro
@g_str

Create a GAP string by typing g"content".

Examples

julia> g"foo"
GAP: "foo"

julia> g"ab\ncd\"ef\\gh"   # special characters are handled as in GAP
GAP: "ab\ncd\"ef\\gh"

Due to Julia's way of handing over arguments into the code of macros, not all strings representing valid GAP strings can be processed.

julia> g"\\"
ERROR: Error thrown by GAP: Syntax error: String must end with " before end of file in stream:1
[...]

Conversely, there are valid arguments for the macro that are not valid Julia strings.

julia> g"\c"
GAP: "\c"
source
GAP.@gapwrapMacro
@gapwrap

When applied to a method definition that involves access to entries of GAP.Globals, this macro rewrites the code such that the relevant GAP globals are cached, and need not be fetched again and again.

Examples

julia> @gapwrap isevenint(x) = GAP.Globals.IsEvenInt(x)::Bool;

julia> isevenint(1)
false

julia> isevenint(2)
true
source
GAP.@gapattributeMacro
@gapattribute

This macro is intended to be applied to a method definition for a unary function called attr, say, where the argument has the type T, say, the code contains exactly one call of the form GAP.Globals.Something(X), where Something is a GAP attribute such as Centre or IsSolvableGroup, and attr returns the corresponding attribute value for its argument.

The macro defines three functions attr, has_attr, and set_attr, where attr takes an argument of type T and returns what the given method definition says, has_attr takes an argument of type T and returns the result of GAP.Globals.HasSomething(X) (which is either true or false), set_attr takes an argument of type T and an object obj and calls GAP.Globals.SetSomething(X, obj).

In order to avoid runtime access via GAP.Globals.Something etc., the same modifications are applied in the construction of the three functions that are applied by @gapwrap.

The variables that are created by the macro belong to the Julia module in whose scope the macro is called.

Examples

julia> @gapattribute isstrictlysortedlist(obj::GapObj) = GAP.Globals.IsSSortedList(obj)::Bool;

julia> l = GapObj([ 1, 3, 7 ]);

julia> has_isstrictlysortedlist( l )
false

julia> isstrictlysortedlist( l )
true

julia> has_isstrictlysortedlist( l )
true

julia> l = GapObj([ 1, 3, 7 ]);

julia> has_isstrictlysortedlist( l )
false

julia> set_isstrictlysortedlist( l, true )

julia> has_isstrictlysortedlist( l )
true

julia> isstrictlysortedlist( l )
true
source
GAP.@wrapMacro
@wrap funcdecl

When applied to a function declaration of the form NAME(a::T) or NAME(a::T)::S, this macro generates a function which behaves equivalently to NAME(a::T) = GAP.Globals.NAME(a) resp. NAME(a::T) = GAP.Globals.NAME(a)::S, assuming that GAP.Globals.NAME references a GAP function. Function declarations with more than one argument or zero arguments are also supported.

However, the generated function actually caches the GAP object GAP.Globals.NAME. This minimizes the call overhead. So @wrap typically is used to provide an optimized way to call certain GAP functions.

If an argument is annotated as ::GapObj (respectively ::GAP.Obj or ::GapInt), the resulting function accepts arguments of any type and attempts to convert them to GapObj (respectively GAP.Obj or GapInt) before passing them to the GAP function.

Another use case for this macro is to improve type stability of code calling into GAP, via the type annotations for the arguments and return value contained in the function declaration.

Be advised, though, that if the value of GAP.Globals.NAME is changed later on, the function generated by this macro will not be updated, i.e., it will still reference the original GAP object.

Examples

julia> GAP.@wrap IsString(x::GapObj)::Bool
IsString (generic function with 1 method)

julia> IsString("abc")
true

julia> GAP.@wrap Jacobi(x::GapInt, y::GapInt)::Int
Jacobi (generic function with 1 method)

julia> Jacobi(11,35)
1

julia> Jacobi(big(35)^100+11, 35)
1
source
GAP.@installMacro
@install

When applied to a unary method definition for the function GapObj, with argument of type T, this macro installs instead a three argument method for GAP.GapObj_internal, with second argument of type GAP.GapCacheDict and third argument of type Bool.

This way, the intended GapObj(x::T) method becomes available, and additionally its code is applicable in recursive calls, for example when GapObj is called with a vector of objects of type T.

Since the GapObj method does not support a dictionary for tracking identical subobjects, the type T is marked as "not needing recursion", by automatically installing a method for _needs_tracking_julia_to_gap that returns false.

The calls of the macro have the form GAP.@install GapObj(x::T) = f(x) or GAP.@install function GapObj(x::T) ... end.

source

Convenience adapters

This section describes how one can manipulate GAP objects from the Julia side, using Julia syntax features.

In particular, the following is available on the Julia side in order to support special GAP syntax beyond function calls with arguments.

  • Call functions with global options via call_gap_func or using Julia's keyword argument syntax. For example, Cyc(1.41421356 : bits:=20) in GAP translates to GAP.Globals.Cyc(GAP.Obj(1.41421356); bits=20) in Julia.

  • Access list/matrix entries via getindex and setindex! respectively the corresponding Julia syntax (described there).

  • Access record components via getproperty and setproperty! respectively the corresponding Julia syntax (described there).

  • Check for bound record components via hasproperty.

  • Access entries of a positional object via getbangindex, equivalent to GAP's ![] operator.

  • Access components of a component object via getbangproperty, equivalent to GAP's !. operator.

GAP.call_gap_funcFunction
call_gap_func(func::GapObj, args...; kwargs...)

Call the GAP object func as a function, with arguments args... and global GAP options kwargs..., and return the result if there is one, and nothing otherwise.

There is no argument number checking here, all checks on the arguments are done by GAP itself.

For convenience, one can use the syntax func(args...; kwargs...).

Examples

julia> GAP.Globals.Factors( 12 )
GAP: [ 2, 2, 3 ]

julia> GAP.Globals.Cyc(GAP.Obj(1.41421356))
GAP: 35355339/25000000

julia> GAP.Globals.Cyc(GAP.Obj(1.41421356); bits=20)
GAP: E(8)-E(8)^3
source
GAP.call_with_catchFunction
call_with_catch(func, args::Vector)
call_with_catch(func, args::Vector, kwargs::Dict{Symbol,T}) where T

Return a tuple (ok, val) where ok is either true, meaning that calling func with arguments args (and optionally with keyword arguments given by the keys and values of kwargs) returns the value val, or false, meaning that the function call runs into an error; in the latter case, val is set to the string of the error message.

This function is used on the GAP side.

Examples

julia> GAP.call_with_catch(sqrt, [2])
(true, 1.4142135623730951)

julia> flag, res = GAP.call_with_catch(sqrt, [-2]);

julia> flag
false

julia> startswith(res, "DomainError")
true

julia> GAP.call_with_catch(range, [2, 10], Dict(:step => 2))
(true, 2:2:10)

julia> flag, res = GAP.call_with_catch(range, [2, 10], Dict(:step => "a"));

julia> flag
false

julia> startswith(res, "MethodError")
true
source
Base.getindexFunction
getindex(x::GapObj, i::Int64)
getindex(x::GapObj, i::Int64, j::Int64)
getindex(x::GapObj, l::Union{Vector{T},AbstractRange{T}}) where {T<:Integer}

Return the entry at position i or at position (i,j) in x, or the list of entries in x at the positions described by l, provided that x is a GAP object supporting this, such as a GAP list or matrix object.

Examples

julia> l = GapObj([ 1, 2, 3, 5, 8, 13 ])
GAP: [ 1, 2, 3, 5, 8, 13 ]

julia> l[4]
5

julia> l[end]
13

julia> l[2:4]
GAP: [ 2, 3, 5 ]

julia> l[[1,4,4]]
GAP: [ 1, 5, 5 ]

julia> m = GapObj([ 1 2 ; 3 4 ])
GAP: [ [ 1, 2 ], [ 3, 4 ] ]

julia> m[1,1]
1

julia> m[1,2]
2

julia> m[2,1]
3
source
Base.setindex!Function
setindex!(x::GapObj, v::Any, i::Int64)
setindex!(x::GapObj, v::Any, i::Int64, j::Int64)
setindex!(x::GapObj, v::Any, l::Union{Vector{T},AbstractRange{T}}) where {T<:Integer}

Set the entry at position i or (i,j) in x to v, or set the entries at the positions in x that are described by l to the entries in v, provided that x is a GAP object supporting this, such as a GAP list or matrix object.

Examples

julia> l = GapObj([ 1, 2, 3, 5, 8, 13 ])
GAP: [ 1, 2, 3, 5, 8, 13 ]

julia> l[1] = 0
0

julia> l[8] = -1
-1

julia> l[2:4] = [ 7, 7, 7 ]
3-element Vector{Int64}:
 7
 7
 7

julia> l
GAP: [ 0, 7, 7, 7, 8, 13,, -1 ]

julia> m = GapObj([ 1 2 ; 3 4 ])
GAP: [ [ 1, 2 ], [ 3, 4 ] ]

julia> m[1,2] = 0
0

julia> m
GAP: [ [ 1, 0 ], [ 3, 4 ] ]
source
GAP.getbangindexFunction
getbangindex(x::GapObj, i::Int64)

Return the entry at position i in the positional object x.

Examples

julia> x = GAP.Globals.ZmodnZObj(1, 6)
GAP: ZmodnZObj( 1, 6 )

julia> GAP.Globals.IsPositionalObjectRep(x)
true

julia> getbangindex(x, 1)
1
source
GAP.hasbangindexFunction
hasbangindex(x::GapObj, i::Int64)

Return whether the entry at position i exists in the positional object x.

Examples

julia> x = GAP.Globals.ZmodnZObj(1, 6)
GAP: ZmodnZObj( 1, 6 )

julia> GAP.Globals.IsPositionalObjectRep(x)
true

julia> hasbangindex(x, 1)
true

julia> hasbangindex(x, 2)
false
source
GAP.setbangindex!Function
setbangindex!(x::GapObj, v::Any, i::Int64)

Set the entry at position i in the positional object x to v, and return x.

Examples

julia> x = GAP.Globals.ZmodnZObj(1, 6)
GAP: ZmodnZObj( 1, 6 )

julia> GAP.Globals.IsPositionalObjectRep(x)
true

julia> setbangindex!(x, 0, 1)
GAP: ZmodnZObj( 0, 6 )
source
Base.getpropertyFunction
getproperty(x::GapObj, f::Symbol)
getproperty(x::GapObj, f::Union{AbstractString,Int64})

Return the record component of the GAP record x that is described by f.

Examples

julia> r = GapObj(Dict(:a => 1))
GAP: rec( a := 1 )

julia> r.a
1
source
Base.setproperty!Function
setproperty!(x::GapObj, f::Symbol, v)
setproperty!(x::GapObj, f::Union{AbstractString,Int64}, v)

Set the record component of the GAP record x that is described by f to the value v.

Examples

julia> r = GapObj(Dict(:a => 1))
GAP: rec( a := 1 )

julia> r.b = 0
0

julia> r
GAP: rec( a := 1, b := 0 )
source
Base.haspropertyFunction
hasproperty(x::GapObj, f::Symbol)
hasproperty(x::GapObj, f::Union{AbstractString,Int64})

Return true if the GAP record x has a component that is described by f, and false otherwise.

Examples

julia> r = GapObj(Dict(:a => 1))
GAP: rec( a := 1 )

julia> hasproperty( r, :a )
true

julia> hasproperty( r, :b )
false

julia> r.b = 2
2

julia> hasproperty( r, :b )
true

julia> r
GAP: rec( a := 1, b := 2 )
source
GAP.getbangpropertyFunction
getbangproperty(x::GapObj, f::Union{AbstractString,Int64,Symbol})

Return the value of the component f in the component object x.

Examples

julia> x = GAP.Globals.Iterator(GAP.Globals.Integers)
GAP: <iterator of Integers at 0>

julia> GAP.Globals.IsComponentObjectRep(x)
true

julia> getbangproperty(x, :counter)
0
source
GAP.hasbangpropertyFunction
hasbangproperty(x::GapObj, f::Union{AbstractString,Int64,Symbol})

Return whether the component object x has the component f.

Examples

julia> x = GAP.Globals.Iterator(GAP.Globals.Integers)
GAP: <iterator of Integers at 0>

julia> GAP.Globals.IsComponentObjectRep(x)
true

julia> hasbangproperty(x, :counter)
true

julia> hasbangproperty(x, :x)
false
source
GAP.setbangproperty!Function
setbangproperty!(x::GapObj, f::Union{AbstractString,Int64,Symbol}, v)

Set the value of the component f in the component object x to v, and return x.

Examples

julia> x = GAP.Globals.Iterator(GAP.Globals.Integers)
GAP: <iterator of Integers at 0>

julia> GAP.Globals.IsComponentObjectRep(x)
true

julia> setbangproperty!(x, :counter, 3)
GAP: <iterator of Integers at -1>

julia> getbangproperty(x, :counter)
3
source
GAP.wrap_rngFunction
wrap_rng(rng::Random.AbstractRNG)

Return a GAP object in the filter IsRandomSource that uses rng in calls to GAP's Random function. The idea is that GAP's Random methods for high level objects will just hand over the given random source to subfunctions until Random gets called for a list or the bounds of a range, and then Base.rand gets called with rng.

Examples

julia> rng1 = Random.default_rng();

julia> rng2 = copy(rng1);

julia> rng1 == rng2
true

julia> rng1 === rng2
false

julia> gap_rng1 = GAP.wrap_rng(rng1)
GAP: <RandomSource in IsRandomSourceJulia>

julia> gap_rng2 = GAP.wrap_rng(rng2)
GAP: <RandomSource in IsRandomSourceJulia>

julia> res1 = GAP.Globals.Random(gap_rng1, 1, 10);

julia> rng1 == rng2   # the two rngs have diverged
false

julia> res1 == GAP.Globals.Random(gap_rng2, GapObj(1:10))
true

julia> rng1 == rng2   # now the two rngs are again in sync
true

julia> g = GAP.Globals.SymmetricGroup(10);

julia> p = GAP.Globals.Random(gap_rng1, g);

julia> p in g
true

julia> GAP.Globals.Random(gap_rng1, GAP.Globals.GF(2)^10)
GAP: <a GF2 vector of length 10>
source
GAP.randseed!Function
GAP.randseed!([seed::Integer])

Reseed GAP's global RNG with seed.

The given seed must be a non-negative integer. When seed is not specified, a random seed is generated from Julia's global RNG.

For a fixed seed, the stream of generated numbers is allowed to change between different versions of GAP.

source

For the following Julia functions, methods are provided that deal with the case that the arguments are GAP objects; they delegate to the corresponding GAP operations.

JuliaGAP
lengthLength
in\in
zeroZeroSameMutability
oneOneSameMutability
- (unary)AdditiveInverseSameMutability
invInverseSameMutability
+SUM
- (binary)DIFF
*PROD
/QUO
\LQUO
^POW
modMOD
<LT
==EQ

The reason why four SameMutability operations are chosen in this list is as follows. In GAP, binary arithmetic operations return immutable results if and only if the two arguments are immutable. Thus it is consistent if unary arithmetic operations return a result with the same mutability as the argument. Note that GAP provides several variants of these unary operations, regarding the mutability of the result (ZeroMutable, ZeroImmutable, ZeroSameMutability, etc.), but here we have to choose one behaviour for the Julia function.

julia> l = GapObj( [ 1, 3, 7, 15 ] )
GAP: [ 1, 3, 7, 15 ]

julia> m = GapObj( [ 1 2; 3 4 ] )
GAP: [ [ 1, 2 ], [ 3, 4 ] ]

julia> length( l )
4

julia> length( m )  # different from Julia's behaviour
2

julia> 1 in l
true

julia> 2 in l
false

julia> zero( l )
GAP: [ 0, 0, 0, 0 ]

julia> one( m )
GAP: [ [ 1, 0 ], [ 0, 1 ] ]

julia> - l
GAP: [ -1, -3, -7, -15 ]

julia> l + 1
GAP: [ 2, 4, 8, 16 ]

julia> l + l
GAP: [ 2, 6, 14, 30 ]

julia> m + m
GAP: [ [ 2, 4 ], [ 6, 8 ] ]

julia> 1 - m
GAP: [ [ 0, -1 ], [ -2, -3 ] ]

julia> l * l
284

julia> l * m
GAP: [ 10, 14 ]

julia> m * m
GAP: [ [ 7, 10 ], [ 15, 22 ] ]

julia> 1 / m
GAP: [ [ -2, 1 ], [ 3/2, -1/2 ] ]

julia> m / 2
GAP: [ [ 1/2, 1 ], [ 3/2, 2 ] ]

julia> 2 \ m
GAP: [ [ 1/2, 1 ], [ 3/2, 2 ] ]

julia> m ^ 2
GAP: [ [ 7, 10 ], [ 15, 22 ] ]

julia> m ^ -1
GAP: [ [ -2, 1 ], [ 3/2, -1/2 ] ]

julia> mod( l, 3 )
GAP: [ 1, 0, 1, 0 ]

julia> m < 2 * m
true

julia> m^2 - 5 * m == 2 * one( m )
true

Access to the GAP help system

GAP.show_gap_helpFunction
show_gap_help(topic::String, onlyexact::Bool = false)

Print the information from the GAP help system about topic to the screen. If onlyexact is true then only exact matches are shown, otherwise all matches. For example, GAP.show_gap_help("Size") shows also documentation for SizeScreen and SizesPerfectGroups, whereas GAP.show_gap_help("Size", true) shows only documentation for Size.

For the variant showing all matches, one can also enter ?GAP.Globals.Size at the Julia prompt instead of calling show_gap_help.

Examples

julia> GAP.show_gap_help( "Size" )
[...]  # more than 50 entries from GAP manuals

help?> GAP.Globals.Size
[...]  # the same

julia> GAP.show_gap_help( "Size", true )
[...]  # about 15 entries from GAP manuals
source