At long last I got around to bringing this code up to date. Thank you, @mschauer! @KZiemian, the call(::Type{GF{p}}, x::Integer)
function seems to be superfluous in julia-1.6. Instead I moved the check isprime(p)
to the GF{p,T}(x::Integer)
inner constructor.
using Primes
# Type definition: Galois fields GF(p), where p is prime modulus, T is integer type
struct GF{p,T} <: Number where {p,T<:Integer}
rep::T # representative integer which holds the value of a GF(p) variable
function GF{p,T}(x::Integer) where {p,T<:Integer}
if !isprime(p)
throw(ArgumentError("p must be prime"))
end
return new(mod(x, p))
end
end
GF{p}(x::T) where {p,T<:Integer} = GF{p,T}(x)
# Define some basic methods for GF(p) that all Julia Numbers must have
import Base: convert, inv, one, promote_rule, show, zero
convert(::Type{GF{p,T}}, x::Integer) where {p,T} = GF{p}(x)
convert(::Type{GF{p}}, x::Integer) where p = GF{p}(x)
convert(::Type{GF{p,T}}, x::GF{p}) where {p,T} = GF{p,T}(x.rep)
promote_rule(::Type{GF{p,T1}}, ::Type{T2}) where {p,T1,T2<:Integer} = GF{p,promote_type(T1,T2)}
show(io::IO, x::GF) = show(io, x.rep);
# Define arithmetic operations on GF(p)
import Base: +, -, *, /, abs, conj, isless
for op in (:+, :-, :*) # loop over ops, defining each in terms of integer ops mod p
@eval begin
($op)(x::GF{p,T}, y::GF{p,T}) where {p,T} = GF{p,T}($(op)(x.rep, y.rep))
end
end
# Define inverse and ÷. Requires more care than +, -, * because dividing by zero throws error
function inv(x::GF{p,T}) where {p,T}
if x == zero(x)
throw(DivideError())
end
r, u, v = gcdx(x.rep, p)
GF{p,T}(u)
end
(/)(x::GF{p}, y::GF{p}) where p = x*inv(y)
abs(x::GF{p,T}) where {p,T} = x
conj(x::GF{p,T}) where {p,T} = x
isless(x::GF{p,T}, y::GF{p,T}) where {p,T} = isless(x.rep, y.rep);
Now the examples work:
@show subtypes(Number)
# Create some GF(7) variables and do arithmetic
x = GF{7}(9) # x = 9 mod 7 = 2
y = GF{7}(12) # y = 12 mod 7 = 5
@show x
@show y
@show x + y # 2 + 5 mod 7 = 0
@show x - y # 2 - 5 mod 7 = 4
@show x * y # 2 * 5 mod 7 = 3
@show x / y # 2 ÷ 5 mod 7 = 6, because 2 = 6*5 mod 7
p = 17
A = [GF{p}(rand(0:p-1)) for i = 1:4, j = 1:4] # matrix of random GF(17) elems
x = [GF{p}(rand(0:p-1)) for i = 1:4]
b = A*x
@show A
@show b
@show x
@show A\b
println("Try to produce a GF with modulus p=4")
@show GF{4}(1)
produces output
subtypes(Number) = Any[Complex, GF, Real]
x = 2
y = 5
x + y = 0
x - y = 4
x * y = 3
x / y = 6
A = GF{17, Int64}[7 12 15 9; 15 11 16 1; 0 15 15 10; 9 15 7 5]
b = GF{17, Int64}[9, 11, 6, 14]
x = GF{17, Int64}[7, 10, 16, 16]
A \ b = GF{17, Int64}[7, 10, 16, 16]
Try to produce a GF with modulus p=4
ArgumentError: p must be prime
Stacktrace:
[1] GF{4, Int64}(x::Int64)
@ Main ./In[1]:8
[2] (GF{4, T} where T)(x::Int64)
@ Main ./In[1]:13
[3] top-level scope
@ show.jl:955
[4] eval
@ ./boot.jl:360 [inlined]
[5] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
@ Base ./loading.jl:1094