Skip to content

Commit

Permalink
Change the way we do Int64 conversion to/from GAP
Browse files Browse the repository at this point in the history
Previously, any Int64 too big to fit into a GAP immediate integer was put into
a T_JULIA wrapper when passed to GAP, and unwrapped later. At the same time,
`julia_to_gap` relied completely (and incorrectly) on the C kernel functions
`julia_gap` and `gap_julia` to take care of *all* Int64 values.

The result was that passing such an integer as argument to a GAP function did
not work, not even if one explicitly used `julia_to_gap`.

To fix this we teach the C kernel function `gap_julia` to convert "large"
Int64 values to GAP big integers; this ensures that the many adapter functions
we install for Int64 arguments work.

This means that we violate the principle of roundtrip type fidelity (Int64 -> GAP large
int -> MPtr), but that is a very minor concern.

For really efficient conversions of e.g. Array{Int64,1} to GAP plists, we need
dedicated conversion functions which directly copy over bits. These can be
added transparently in the future.

Finally, we add some minor optimization to julia_to_gap for UInt64, Int128, UInt128
  • Loading branch information
fingolfin committed Mar 5, 2020
1 parent b56c4ad commit e48981a
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 24 deletions.
2 changes: 1 addition & 1 deletion pkg/GAPJulia/JuliaInterface/gap/convert.gd
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
#! <List>
#! <Item>
#! <C>Int64</C> to immediate integer when it fits,
#! otherwise to &Julia; object wrapper,
#! otherwise to a &GAP; large integer,
#! </Item>
#! <Item>
#! <C>GapFFE</C> to immediate FFE,
Expand Down
2 changes: 1 addition & 1 deletion pkg/GAPJulia/JuliaInterface/src/convert.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Obj gap_julia(jl_value_t * julia_obj)
if (INT_INTOBJ_MIN <= v && v <= INT_INTOBJ_MAX) {
return INTOBJ_INT(v);
}
return NewJuliaObj(julia_obj);
return ObjInt_Int8(v);
}
if (is_gapobj(julia_obj)) {
return (Obj)julia_obj;
Expand Down
8 changes: 0 additions & 8 deletions src/ccalls.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,6 @@ function NewPlist(length :: Int64)
return o
end

function MakeObjInt(x::BigInt)
o = ccall( :MakeObjInt,
Ptr{Cvoid},
(Ptr{UInt64},Cint),
x.d, x.size )
return RAW_GAP_TO_JULIA( o )
end

function NEW_MACFLOAT(x::Float64)
o = ccall( :NEW_MACFLOAT,
Any,
Expand Down
38 changes: 25 additions & 13 deletions src/julia_to_gap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,40 @@ of input data, by converting egal data to identical objects in GAP.
"""


## Default
## Default for actual GAP objects is to do nothing
julia_to_gap(x::GapObj) = x
julia_to_gap(x::FFE) = x
julia_to_gap(x::Bool) = x

## 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)
# if it fits into a GAP immediate integer, convert x to Int64
if x in -1<<60:(1<<60-1)
return Int64(x)
end
# for the general case, fall back to BigInt
return julia_to_gap(BigInt(x))
end

## Integers
julia_to_gap(x::Int128) = MakeObjInt(BigInt(x)) # FIXME: inefficient hack
## 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)

## Unsigned Integers
julia_to_gap(x::UInt128) = MakeObjInt(BigInt(x)) # FIXME: inefficient hack
julia_to_gap(x::UInt64) = MakeObjInt(BigInt(x)) # FIXME: inefficient hack
julia_to_gap(x::UInt32) = Int64(x)
julia_to_gap(x::UInt16) = Int64(x)
julia_to_gap(x::UInt8) = Int64(x)

## BigInts
julia_to_gap(x::BigInt) = MakeObjInt(x)
julia_to_gap(x::UInt32) = Int64(x)
julia_to_gap(x::UInt16) = Int64(x)
julia_to_gap(x::UInt8) = Int64(x)

## BigInts are converted via a ccall
function julia_to_gap(x::BigInt)
o = ccall(:MakeObjInt, Ptr{Cvoid}, (Ptr{UInt64},Cint), x.d, x.size)
return RAW_GAP_TO_JULIA(o)
end

## Rationals
function julia_to_gap(x::Rational{T}) where T <: Integer
Expand Down
2 changes: 1 addition & 1 deletion test/basics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ end
# passed and then extracted again
@test f( (1,2,3) )[1] == (1,2,3)

@test GAP.Globals.IdFunc(2^62) isa Int64
@test !(GAP.Globals.IdFunc(2^62) isa Int64)

x = GAP.Globals.Indeterminate(GAP.Globals.Rationals)
f = x^4+1
Expand Down
15 changes: 15 additions & 0 deletions test/conversion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,21 @@ end
@test GAP.julia_to_gap(Int16(1)) == 1
@test GAP.julia_to_gap(Int8(1)) == 1

## Int64 corner cases
@test GAP.julia_to_gap(-2^60 ) === -2^60
@test GAP.julia_to_gap( 2^60-1) === 2^60 - 1

@test GAP.Globals.IsInt(GAP.julia_to_gap(-2^60-1))
@test GAP.Globals.IsInt(GAP.julia_to_gap( 2^60))

@test GAP.Globals.IsInt(GAP.julia_to_gap(-2^63-1))
@test GAP.Globals.IsInt(GAP.julia_to_gap(-2^63))
@test GAP.Globals.IsInt(GAP.julia_to_gap( 2^63-1))
@test GAP.Globals.IsInt(GAP.julia_to_gap( 2^63))

# see issue https://github.com/oscar-system/GAP.jl/issues/332
@test 2^60 * GAP.Globals.Factorial(20) == GAP.EvalString("2^60 * Factorial(20)")

## Unsigned integers
@test GAP.julia_to_gap(UInt128(1)) == 1
@test GAP.julia_to_gap(UInt64(1)) == 1
Expand Down

0 comments on commit e48981a

Please sign in to comment.