# Other stuff

## Macros

`GAP.@gap`

— Macro```
@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)
```

`GAP.@g_str`

— Macro`@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: LoadError: 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"
```

`GAP.@gapwrap`

— Macro`@gapwrap`

When applied to a method definition that involves access to entries of `GAP.Globals`

, this macro rewrites the code (using `@generated`

) such that the relevant entries are cached at compile time, and need not be fetched again and again at runtime.

**Examples**

```
julia> @gapwrap isevenint(x) = GAP.Globals.IsEvenInt(x)::Bool;
julia> isevenint(1)
false
julia> isevenint(2)
true
```

`GAP.@gapattribute`

— Macro`@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`

, `hasattr`

, and `setattr`

, where `attr`

takes an argument of type `T`

and returns what the given method definition says, `hasattr`

takes an argument of type `T`

and returns the result of `GAP.Globals.HasSomething(X)`

(which is either `true`

or `false`

), `setattr`

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::GAP.GapObj) = GAP.Globals.IsSSortedList(obj)::Bool;
julia> l = GapObj([ 1, 3, 7 ]);
julia> hasisstrictlysortedlist( l )
false
julia> isstrictlysortedlist( l )
true
julia> hasisstrictlysortedlist( l )
true
julia> l = GapObj([ 1, 3, 7 ]);
julia> hasisstrictlysortedlist( l )
false
julia> setisstrictlysortedlist( l, true )
julia> hasisstrictlysortedlist( l )
true
julia> isstrictlysortedlist( l )
true
```

`GAP.@wrap`

— Macro`@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 is implemented using the `@generated`

macro and caches the GAP object `GAP.Globals.NAME`

and then produce optimal code to invoke that GAP function. This minimizes the call overhead. So @wrap typically is used to provide an optimized way to call certain GAP functions.

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 Jacobi(x::GapInt, y::GapInt)::Int
Jacobi (generic function with 1 method)
julia> Jacobi(11,35)
1
```

## Convenience adapters

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

`GAP.call_gap_func`

— Function`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> g = GAP.Globals.SylowSubgroup( GAP.Globals.SymmetricGroup( 6 ), 2 )
GAP: Group([ (1,2), (3,4), (1,3)(2,4), (5,6) ])
julia> GAP.Globals.StructureDescription( g )
GAP: "C2 x D8"
julia> g = GAP.Globals.SylowSubgroup( GAP.Globals.SymmetricGroup( 6 ), 2 );
julia> GAP.Globals.StructureDescription( g, short = true )
GAP: "2xD8"
```

`GAP.call_with_catch`

— Function`call_with_catch( juliafunc, arguments )`

Return a tuple `( ok, val )`

where `ok`

is either `true`

, meaning that calling the function `juliafunc`

with `arguments`

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.

**Examples**

```
julia> GAP.call_with_catch( sqrt, 2 )
(true, 1.4142135623730951)
julia> GAP.call_with_catch( sqrt, -2 )
(false, "DomainError(-2.0, \"sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).\")")
```

`Base.getindex`

— Function```
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
```

`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 ] ]
```

`Base.getproperty`

— Function```
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 = GAP.evalstr( "rec( a:= 1 )" )
GAP: rec( a := 1 )
julia> r.a
1
```

`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 = GAP.evalstr( "rec( a:= 1 )" )
GAP: rec( a := 1 )
julia> r.b = 0
0
julia> r
GAP: rec( a := 1, b := 0 )
```

`Base.hasproperty`

— Function```
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 = GAP.evalstr( "rec( 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 )
```

`GAP.wrap_rng`

— Function`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, GAP.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>
```

`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.

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.

Julia | GAP |
---|---|

`length` | `Length` |

`in` | `\in` |

`zero` | `ZERO` |

`one` | `ONE` |

`-` (unary) | `AINV` |

`+` | `SUM` |

`-` (binary) | `DIFF` |

`*` | `PROD` |

`/` | `QUO` |

`\` | `LQUO` |

`^` | `POW` |

`mod` | `MOD` |

`<` | `LT` |

`==` | `EQ` |

```
julia> l = GAP.julia_to_gap( [ 1, 3, 7, 15 ] )
GAP: [ 1, 3, 7, 15 ]
julia> m = GAP.julia_to_gap( [ 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_help`

— Function`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
```