Skip to content

Commit

Permalink
rewrite julia_to_gap
Browse files Browse the repository at this point in the history
- introduce `julia_to_gap_internal`,
  which takes always three arguments:
  the Julia object to be converted, the auxiliary cache object,
  and a Boolean indicating whether recursive conversion is wanted

- turn all `julia_to_gap` methods into `julia_to_gap_internal` methods

- define default `julia_to_gap` methods (with 1 to 3 arguments) that
  call `julia_to_gap_internal`

- admit `nothing` as an allowed value for the auxiliary cache object,
  and create dictionary objects only inside the methods in question
  where they are needed;
  introduce the type `GapCacheDict` for that
  • Loading branch information
ThomasBreuer committed Aug 30, 2024
1 parent a8674da commit f279fb0
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 60 deletions.
2 changes: 2 additions & 0 deletions src/gap_to_julia.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ An internal type of GAP.jl used for tracking conversion results in `gap_to_julia
"""
const RecDict = IdDict{Any,Any}

const GapCacheDict = Union{Nothing,RecDict}

## Conversion from GAP to Julia
"""
gap_to_julia(type, x, recursion_dict::Union{Nothing,RecDict}=nothing; recursive::Bool=true)
Expand Down
135 changes: 75 additions & 60 deletions src/julia_to_gap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,44 +46,49 @@ The following `julia_to_gap` conversions are supported by GAP.jl.
| `UnitRange{T}`, `StepRange{T, S}` | `IsRange` |
| `Function` | `IsFunction` |
"""
julia_to_gap(x::FFE) = x # Default for actual GAP objects is to do nothing
julia_to_gap(x::Bool) = x # Default for actual GAP objects is to do nothing
function julia_to_gap end

# default
julia_to_gap(x, cache::GapCacheDict = nothing; recursive::Bool = false) = julia_to_gap_internal(x, cache, recursive)

julia_to_gap_internal(x::FFE, cache::GapCacheDict, recursive::Bool) = x # Default for actual GAP objects is to do nothing
julia_to_gap_internal(x::Bool, cache::GapCacheDict, recursive::Bool) = x # Default for actual GAP objects is to do nothing

## Integers: general case first deal with things that fit into immediate
## integers, then falls back to converting to BigInt and calling into the GAP
## kernel API.
## TODO: we could provide more efficient conversion for UInt64, Int128, UInt128
## which avoids the conversion to BigInt, if we wanted to.
function julia_to_gap(x::Integer)
function julia_to_gap_internal(x::Integer, cache::GapCacheDict, recursive::Bool)
# if it fits into a GAP immediate integer, convert x to Int64
x in -1<<60:(1<<60-1) && return Int64(x)
# for the general case, fall back to BigInt
return julia_to_gap(BigInt(x))
return julia_to_gap_internal(BigInt(x), cache, recursive)
end

## Small integers types always fit into GAP immediate integers, and thus are
## represented by Int64 on the Julia side.
julia_to_gap(x::Int64) = x
julia_to_gap(x::Int32) = Int64(x)
julia_to_gap(x::Int16) = Int64(x)
julia_to_gap(x::Int8) = Int64(x)
julia_to_gap(x::UInt32) = Int64(x)
julia_to_gap(x::UInt16) = Int64(x)
julia_to_gap(x::UInt8) = Int64(x)

function julia_to_gap(x::UInt)
julia_to_gap_internal(x::Int64, cache::GapCacheDict, recursive::Bool) = x
julia_to_gap_internal(x::Int32, cache::GapCacheDict, recursive::Bool) = Int64(x)
julia_to_gap_internal(x::Int16, cache::GapCacheDict, recursive::Bool) = Int64(x)
julia_to_gap_internal(x::Int8, cache::GapCacheDict, recursive::Bool) = Int64(x)
julia_to_gap_internal(x::UInt32, cache::GapCacheDict, recursive::Bool) = Int64(x)
julia_to_gap_internal(x::UInt16, cache::GapCacheDict, recursive::Bool) = Int64(x)
julia_to_gap_internal(x::UInt8, cache::GapCacheDict, recursive::Bool) = Int64(x)

function julia_to_gap_internal(x::UInt, cache::GapCacheDict, recursive::Bool)
x < (1<<60) && return Int64(x)
return ccall((:ObjInt_UInt, libgap), GapObj, (UInt64, ), x)
end

## BigInts are converted via a ccall
function julia_to_gap(x::BigInt)
function julia_to_gap_internal(x::BigInt, cache::GapCacheDict, recursive::Bool)
x in -1<<60:(1<<60-1) && return Int64(x)
return GC.@preserve x ccall((:MakeObjInt, libgap), GapObj, (Ptr{UInt64}, Cint), x.d, x.size)
end

## Rationals
function julia_to_gap(x::Rational{T}) where {T<:Integer}
function julia_to_gap_internal(x::Rational{T}, cache::GapCacheDict, recursive::Bool) where {T<:Integer}
denom_julia = denominator(x)
numer_julia = numerator(x)
if denom_julia == 0
Expand All @@ -93,37 +98,43 @@ function julia_to_gap(x::Rational{T}) where {T<:Integer}
return -Globals.infinity
end
end
numer = julia_to_gap(numer_julia)
denom = julia_to_gap(denom_julia)
numer = julia_to_gap_internal(numer_julia, cache, recursive)
denom = julia_to_gap_internal(denom_julia, cache, recursive)
return Wrappers.QUO(numer, denom)
end

## Floats
julia_to_gap(x::Float64) = NEW_MACFLOAT(x)
julia_to_gap(x::Float32) = NEW_MACFLOAT(Float64(x))
julia_to_gap(x::Float16) = NEW_MACFLOAT(Float64(x))
julia_to_gap_internal(x::Float64, cache::GapCacheDict, recursive::Bool) = NEW_MACFLOAT(x)
julia_to_gap_internal(x::Float32, cache::GapCacheDict, recursive::Bool) = NEW_MACFLOAT(Float64(x))
julia_to_gap_internal(x::Float16, cache::GapCacheDict, recursive::Bool) = NEW_MACFLOAT(Float64(x))

## Chars
julia_to_gap(x::Char) = CharWithValue(Cuchar(x))
julia_to_gap_internal(x::Char, cache::GapCacheDict, recursive::Bool) = CharWithValue(Cuchar(x))

## Strings and symbols
julia_to_gap(x::AbstractString) = MakeString(string(x))
julia_to_gap(x::Symbol) = MakeString(string(x))
julia_to_gap_internal(x::AbstractString, cache::GapCacheDict, recursive::Bool) = MakeString(string(x))
julia_to_gap_internal(x::Symbol, cache::GapCacheDict, recursive::Bool) = MakeString(string(x))

## Generic caller for optional arguments
julia_to_gap(obj::Any; recursive::Bool) = julia_to_gap(obj)
julia_to_gap(obj::Any, recursion_dict::IdDict{Any,Any}; recursive::Bool = true) = julia_to_gap(obj)
# ## Generic caller for optional arguments
# julia_to_gap(obj::Any; recursive::Bool) = julia_to_gap(obj)
# julia_to_gap(obj::Any, recursion_dict::IdDict{Any,Any}; recursive::Bool = true) = julia_to_gap(obj)

## Arrays (including BitVector)
function julia_to_gap(
function julia_to_gap_internal(
obj::AbstractVector{T},
recursion_dict::IdDict{Any,Any} = IdDict();
recursive::Bool = false,
recursion_dict::GapCacheDict,
recursive::Bool,
) where {T}

if recursion_dict !== nothing && haskey(recursion_dict, obj)
return recursion_dict[obj]
end
len = length(obj)
ret_val = NewPlist(len)
if recursive
if recursion_dict === nothing
recursion_dict = RecDict()
end
recursion_dict[obj] = ret_val
end
for i = 1:len
Expand All @@ -133,7 +144,7 @@ function julia_to_gap(
end
if recursive
x = get!(recursion_dict, x) do
julia_to_gap(x, recursion_dict; recursive)
julia_to_gap_internal(x, recursion_dict, recursive)
end
end
ret_val[i] = x
Expand All @@ -142,66 +153,64 @@ function julia_to_gap(
end

## Convert two dimensional arrays
function julia_to_gap(
function julia_to_gap_internal(
obj::Matrix{T},
recursion_dict::IdDict{Any,Any} = IdDict();
recursive::Bool = false,
recursion_dict::GapCacheDict,
recursive::Bool,
) where {T}
(rows, cols) = size(obj)
if haskey(recursion_dict, obj)
return recursion_dict[obj]
if recursion_dict !== nothing && haskey(recursion_dict, obj)
return recursion_dict[obj]
end
(rows, cols) = size(obj)
ret_val = NewPlist(rows)
if recursive
if recursion_dict === nothing
recursion_dict = RecDict()
end
recursion_dict[obj] = ret_val
end
for i = 1:rows
ret_val[i] = julia_to_gap(obj[i, :], recursion_dict; recursive)
ret_val[i] = julia_to_gap_internal(obj[i, :], recursion_dict, recursive)
end
return ret_val
end

## Tuples
function julia_to_gap(
function julia_to_gap_internal(
obj::Tuple,
recursion_dict::IdDict{Any,Any} = IdDict();
recursive::Bool = false,
recursion_dict::GapCacheDict,
recursive::Bool,
)
array = collect(Any, obj)
return julia_to_gap(array, recursion_dict; recursive)
return julia_to_gap_internal(array, recursion_dict, recursive)
end

## Ranges
function julia_to_gap(r::AbstractRange{<:Integer}, recursive::Bool = false)
function julia_to_gap_internal(r::AbstractRange{<:Integer}, recursion_dict::GapCacheDict, recursive::Bool)
res = NewRange(length(r), first(r), step(r))
Wrappers.IsRange(res) || throw(ConversionError(r, GapObj))
return res
end

function julia_to_gap(
r::AbstractRange{<:Integer},
recursion_dict::IdDict{Any,Any};
recursive::Bool = false)

return julia_to_gap(r)
end

## Dictionaries
function julia_to_gap(
function julia_to_gap_internal(
obj::Dict{T,S},
recursion_dict::IdDict{Any,Any} = IdDict();
recursive::Bool = false,
recursion_dict::GapCacheDict,
recursive::Bool,
) where {S} where {T<:Union{Symbol,AbstractString}}

record = NewPrecord(0)
if recursive
if recursion_dict === nothing
recursion_dict = RecDict()
end
recursion_dict[obj] = record
end
for (x, y) in obj
x = Wrappers.RNamObj(MakeString(string(x)))
if recursive
y = get!(recursion_dict, y) do
julia_to_gap(y, recursion_dict; recursive)
julia_to_gap_internal(y, recursion_dict, recursive)
end
end
Wrappers.ASS_REC(record, x, y)
Expand All @@ -215,26 +224,32 @@ end
## and if `obj` contains Julia subobjects;
## in this case, `obj` is a GAP list or record.
## An example of such an `obj` is `GAP.julia_to_gap([[1]])`.
function julia_to_gap(
function julia_to_gap_internal(
obj::GapObj,
recursion_dict::IdDict{Any,Any} = IdDict();
recursive::Bool = false,
recursion_dict::GapCacheDict,
recursive::Bool,
)
if ! recursive
ret_val = obj
elseif Wrappers.IsList(obj)
len = length(obj)
ret_val = NewPlist(len)
if recursion_dict === nothing
recursion_dict = RecDict()
end
recursion_dict[obj] = ret_val
for i = 1:len
ret_val[i] = julia_to_gap(obj[i], recursion_dict; recursive)
ret_val[i] = julia_to_gap_internal(obj[i], recursion_dict, recursive)
end
elseif Wrappers.IsRecord(obj)
ret_val = NewPrecord(0)
if recursion_dict === nothing
recursion_dict = RecDict()
end
recursion_dict[obj] = ret_val
for xx in Wrappers.RecNames(obj)::GapObj
x = Wrappers.RNamObj(xx)
Wrappers.ASS_REC(ret_val, x, julia_to_gap(Wrappers.ELM_REC(obj, x), recursion_dict; recursive = true))
Wrappers.ASS_REC(ret_val, x, julia_to_gap_internal(Wrappers.ELM_REC(obj, x), recursion_dict, recursive))
end
else
ret_val = obj
Expand All @@ -243,4 +258,4 @@ function julia_to_gap(
return ret_val
end

julia_to_gap(func::Function) = WrapJuliaFunc(func)
julia_to_gap_internal(func::Function, recursion_dict::GapCacheDict, recursive::Bool) = WrapJuliaFunc(func)

0 comments on commit f279fb0

Please sign in to comment.