Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/methods.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# Unwrap to the storage value. The macro installs a `storagetypeof(::Type{T}) = <storage_type>` constant-returning method per emulated type, so this folds to a single `reinterpret`. On Julia >= 1.13, `@emulate` additionally installs a per-type, `range`-annotated `getindex` (see `emulate.jl`) that is strictly more specific and wins dispatch; this abstract method then only serves as the fallback on older Julia (LLVM < 19, where the `range` attribute is unavailable).
Base.getindex(x::EmulatedInteger) = reinterpret(x |> typeof |> storagetypeof, x)

Base.broadcastable(x::EmulatedInteger) = Ref(x)

# Value form returns a wider value (the unwrapped storage int); type form returns the storage type. Both routed through the per-type `storagetypeof` trait.
Base.widen(x::EmulatedInteger) = x[]
Base.widen(::Type{T}) where T<:EmulatedInteger = storagetypeof(T)
Expand Down
35 changes: 35 additions & 0 deletions test/broadcasting.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@testset "scalar broadcasting" begin
@emulate Int3 UInt3 Int3_128 UInt3_128

@test Int3(2) * Int3[2] == Int3[-4]
@test UInt3(3) * UInt3[3] == UInt3[1]

for Element in (Int3, UInt3, Int3_128, UInt3_128)
for scalar in (Element(2), typemin(Element), typemax(Element))
vector = Element[2, 3]
expected = map(value -> scalar * value, vector)
@test Base.broadcastable(scalar)[] === scalar
@test identity.(scalar) === scalar
@test scalar .* scalar === scalar * scalar
@test scalar * vector == expected
@test vector * scalar == expected
@test scalar .* vector == expected
@test vector .* scalar == expected
@test eltype(scalar * vector) === Element
@test eltype(vector * scalar) === Element
@test eltype(scalar .* vector) === Element
@test scalar .+ vector == map(value -> scalar + value, vector)
@test scalar .* (vector .+ scalar) == map(value -> scalar * (value + scalar), vector)
destination = similar(vector)
destination .= scalar .* vector
@test destination == expected
matrix = reshape(vector, 1, 2)
@test scalar * matrix == reshape(expected, 1, 2)
@test matrix * scalar == reshape(expected, 1, 2)
@test scalar * fill(Element(2)) == fill(scalar * Element(2))
@test eltype(scalar * fill(Element(2))) === Element
@test isempty(scalar * Element[])
@test eltype(scalar * Element[]) === Element
end
end
end
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ values(x) = x |> fieldvalues |> collect

include("comparisons.jl")
include("sums.jl")
include("broadcasting.jl")

# ============================================================================
# Static analysis
Expand Down