You are right! The following looks slightly better to me, but certainly can be improvedā¦
# Vector elements are generators for modular arithmetic
struct PrimeGenerator
val::BigInt
mod::BigInt
end
value(x::PrimeGenerator) = x.val
modulus(x::PrimeGenerator) = x.mod
import Base.*
function *(x::PrimeGenerator, y::PrimeGenerator)
@assert x.mod == y.mod "Groups must be equal"
mod = modulus(x)
val = mod(value(x) * value(y), mod)
PrimeGenerator(val, mod)
end
import Base.^
^(g::PrimeGenerator, n::Integer) = PrimeGenerator(powermod(value(g), n, modulus(g)), modulus(g))
# Definition of the vector
struct GVector{G, T} <: AbstractVector{G}
x::Vector{T}
g::G
end
GVector(gs::Vector{G}, g::G) where {G} = GVector(value.(gs), g)
Base.IndexStyle(::Type{<:GVector}) = IndexLinear()
Base.setindex!(š ::GVector, val::PrimeGenerator, i::Int) = š .x[i] = value(val)
Base.getindex(š ::GVector, i::Int) = PrimeGenerator(š .x[i], modulus(š .g))
Base.length(g::GVector) = length(g.x)
Base.size(g::GVector) = size(g.x)
Base.similar(g::GVector) = GVector(similar(g.x), g.g)
Base.BroadcastStyle(::Type{<:GVector{G, T}}) where {G, T} = Broadcast.ArrayStyle{GVector{G, T}}()
function Base.similar(bc::Broadcast.Broadcasted{Broadcast.ArrayStyle{GVector{G, T}}}, ::Type{G}) where {G, T}
GVector(similar(Vector{T}, axes(bc)), find(bc).g)
end
find(bc::Base.Broadcast.Broadcasted) = find(bc.args)
find(args::Tuple) = find(find(args[1]), Base.tail(args))
find(x) = x
find(::Tuple{}) = nothing
find(a::GVector, rest) = a
find(::Any, rest) = find(rest)
a = PrimeGenerator(3, 23)
gv = GVector([a, a^2, a^3], a)
s = similar(gv)
s .= gv .^ 2