Paul Dydyshko
Paul Dydyshko

Reputation: 386

Using structs in parametric types, not only primitive types

The problem arose while I was working with Mods.jl. This package does not support arbitrary-precision arithmetic because only primitive types can be passed as parameters, i.e. Mod{17} works fine, but Mod{big"17"} not.

Due to this I decided to implement something like BigMod with two BigInt fields

struct BigMod <: AbstractMod
    value::BigInt
    modulus::BigInt
end

Then I had to solve system of linear comparisons, and whilst it worked pretty good for Mod{N} (A \ b with some additional magic) it didn't worked for BigMod at all.

The reason was that LinearAlgebra package uses oneunit function to inference types, but there's no way to define oneunit(::Type{BigMod}), because we don't know modulus in this case (but we do know it for Mod{N}). However this function could be defined for variable of the type BigMod, i.e. oneunit(x::BigMod).

This problem can be solved using Base.return_types inside of LinearAlgebra for type inference, however it seems to be type unstable as well as it returns a collection of possible return types.

I can see only three ways to solve this:

  1. One day one could use any (at least immutable) types in parameters. In this case no additional code is required, original should be generic enough for everything to work well.
  2. One day type inference would be improved enough so that no kludges like typeof(oneunit(eltype(B)) / oneunit(eltype(F))) are needed, one can just say return_type(/, eltype.((B,F))) which is much more generic.
  3. Fight through the pain and rewrite all necessary things from LinearAlgebra specially for BigMod

Are there any other ways to reach the goal and what way is the best?

Upvotes: 4

Views: 85

Answers (1)

Przemyslaw Szufel
Przemyslaw Szufel

Reputation: 42214

The parametric types can only contain isbits or Symbols. Hence, you could use a very easy to implement, yet extremely dirty and inefficient workaround.

struct MyBigMod{S} <: AbstractMod
    value::BigInt
end

Base.oneunit(::MyBigMod{S}) where S = parse(BigInt, string(S))

And now you could use it as:

julia> oneunit(MyBigMod{Symbol("55555555555555555555555")}(big"222222"))
55555555555555555555555

Perhaps your big ints are not that big after all and you could consider using BitIntegers? Than you could do something like:

julia> MyBigMod{int1024"55555555555555555555555"}
MyBigMod{55555555555555555555555}

Upvotes: 3

Related Questions