From f279fb06a2139218d2636201520188e85adac43e Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Fri, 30 Aug 2024 11:07:32 +0200 Subject: [PATCH] rewrite `julia_to_gap` - 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 --- src/gap_to_julia.jl | 2 + src/julia_to_gap.jl | 135 ++++++++++++++++++++++++-------------------- 2 files changed, 77 insertions(+), 60 deletions(-) diff --git a/src/gap_to_julia.jl b/src/gap_to_julia.jl index 19e33a6b9..fef047cc7 100644 --- a/src/gap_to_julia.jl +++ b/src/gap_to_julia.jl @@ -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) diff --git a/src/julia_to_gap.jl b/src/julia_to_gap.jl index d9d3e0b12..3a5cdccd2 100644 --- a/src/julia_to_gap.jl +++ b/src/julia_to_gap.jl @@ -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 @@ -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 @@ -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 @@ -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) @@ -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 @@ -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)