From 4b57a839419d8ab852caaa30e0466e2e0793b525 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Sat, 16 Jul 2022 15:34:28 +0200 Subject: [PATCH 1/6] Add functional variant of threefry --- src/threefry.jl | 40 ++++++++++++++++++++++++++-------------- test/runtests.jl | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/src/threefry.jl b/src/threefry.jl index 2032b45..9be9448 100644 --- a/src/threefry.jl +++ b/src/threefry.jl @@ -117,12 +117,24 @@ copy(src::Threefry2x{T, R}) where {T, R} = Threefry2x{T, R}(src.x1, src.x2, src. ==(r1::Threefry2x{T, R}, r2::Threefry2x{T, R}) where {T, R} = unsafe_compare(r1, r2, T, 6) && r1.p == r2.p @inline function random123_r(r::Threefry2x{T, R}) where {T <: Union{UInt32, UInt64}, R} + key = (r.key1, r.key2) + ctr = (r.ctr1, r.ctr2) + r.x1, r.x2 = threefry(key, ctr, Val(R)) +end + +""" + threefry(key::NTuple{2,T}, ctr::NTuple{2,T}, ::Val{R})::NTuple{2,T} + threefry(key::NTuple{4,T}, ctr::NTuple{4,T}, ::Val{R})::NTuple{4,T} + +Functional variant of [`Threefry2x`](@ref) and [`Threefry4x`](@ref). +Produces a pseudorandom output of type `T = UInt64` or `T = UInt32` from the inputs. +This function if free of mutability and side effects. +""" +@inline function threefry(key::NTuple{2,T}, ctr::NTuple{2,T}, ::Val{R})::NTuple{2,T} where {T <: Union{UInt32, UInt64}, R} ks2 = SKEIN_KS_PARITY(T) - ks0 = r.key1 - x0 = r.ctr1 + x0,x1 = ctr + ks0,ks1 = key ks2 ⊻= ks0 - ks1 = r.key2 - x1 = r.ctr2 ks2 ⊻= ks1 x0 += ks0 x1 += ks1 @@ -191,7 +203,7 @@ copy(src::Threefry2x{T, R}) where {T, R} = Threefry2x{T, R}(src.x1, src.x2, src. x0 += ks2; x1 += ks0; x1 += 8 % T; end - r.x1, r.x2 = x0, x1 + x0, x1 end """ @@ -258,18 +270,18 @@ copy(src::Threefry4x{T, R}) where {T, R} = Threefry4x{T, R}(src.x1, src.x2, src. ==(r1::Threefry4x{T, R}, r2::Threefry4x{T, R}) where {T, R} = unsafe_compare(r1, r2, T, 12) && r1.p == r2.p @inline function random123_r(r::Threefry4x{T, R}) where {T <: Union{UInt32, UInt64}, R} + key = (r.key1, r.key2, r.key3, r.key4) + ctr = (r.ctr1, r.ctr2, r.ctr3, r.ctr4) + r.x1, r.x2, r.x3, r.x4 = threefry(key, ctr, Val(R)) +end + +@inline function threefry(key::NTuple{4,T},ctr::NTuple{4,T}, rounds::Val{R})::NTuple{4,T} where {T <: Union{UInt32, UInt64}, R} ks4 = SKEIN_KS_PARITY(T) - ks0 = r.key1 - x0 = r.ctr1 + ks0,ks1,ks2,ks3 = key + x0,x1,x2,x3 = ctr ks4 ⊻= ks0 - ks1 = r.key2 - x1 = r.ctr2 ks4 ⊻= ks1 - ks2 = r.key3 - x2 = r.ctr3 ks4 ⊻= ks2 - ks3 = r.key4 - x3 = r.ctr4 ks4 ⊻= ks3 x0 += ks0; x1 += ks1; x2 += ks2; x3 += ks3; @@ -633,5 +645,5 @@ copy(src::Threefry4x{T, R}) where {T, R} = Threefry4x{T, R}(src.x1, src.x2, src. x0 += ks3; x1 += ks4; x2 += ks0; x3 += ks1; x3 += 18 % T; end - r.x1, r.x2, r.x3, r.x4 = x0, x1, x2, x3 + x0, x1, x2, x3 end diff --git a/test/runtests.jl b/test/runtests.jl index 9d1d785..b1f001d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,9 +1,55 @@ using RandomNumbers using Random123 import Random: seed! -using Test: @test, @test_throws +using Test: @test, @test_throws, @testset using Printf: @printf +@testset "functional" begin + threefry = Random123.threefry + for T in [UInt32, UInt64] + key = (T(123), T(456)) + rng = Threefry2x(T, key) + x1 = rand(rng, T) + x2 = rand(rng, T) + x3 = rand(rng, T) + x4 = rand(rng, T) + x5 = rand(rng, T) + y1,y0 = threefry(key, (T(0), T(0)), Val(20)) + y3,y2 = threefry(key, (T(1), T(0)), Val(20)) + y5,y4 = threefry(key, (T(2), T(0)), Val(20)) + @test x1 === y1 + @test x2 === y2 + @test x3 === y3 + @test x4 === y4 + @test x5 === y5 + end + for T in [UInt32, UInt64] + key = (T(123), T(456), T(7), T(8)) + rng = Threefry4x(T, key) + x1 = rand(rng, T) + x2 = rand(rng, T) + x3 = rand(rng, T) + x4 = rand(rng, T) + x5 = rand(rng, T) + x6 = rand(rng, T) + x7 = rand(rng, T) + x8 = rand(rng, T) + x9 = rand(rng, T) + y1,y2,y3,y4 = threefry(key, (T(0), T(0), T(0), T(0)), Val(20)) + y5,y6,y7,y8 = threefry(key, (T(1), T(0), T(0), T(0)), Val(20)) + y9,_,_,_ = threefry(key, (T(2), T(0), T(0), T(0)), Val(20)) + @test x1 === y1 + @test x2 === y2 + @test x3 === y3 + @test x4 === y4 + @test x5 === y5 + @test x6 === y6 + @test x7 === y7 + @test x8 === y8 + @test x9 === y9 + end +end + function compare_dirs(dir1::AbstractString, dir2::AbstractString) files1 = readdir(dir1) files2 = readdir(dir2) From 6cb806a437fa7964b9fc82721273047b89271964 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Fri, 22 Jul 2022 12:27:36 +0200 Subject: [PATCH 2/6] add philox --- src/philox.jl | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/philox.jl b/src/philox.jl index 7f97ce2..fa17864 100644 --- a/src/philox.jl +++ b/src/philox.jl @@ -90,7 +90,22 @@ end end @inline function random123_r(r::Philox2x{T, R}) where {T <: Union{UInt32, UInt64}, R} - ctr1, ctr2, key = r.ctr1, r.ctr2, r.key + key = (r.key,) + ctr = (r.ctr1, r.ctr2) + r.x1, r.x2 = philox(key, ctr, Val(R)) +end + +""" + philox(key::NTuple{1,T}, ctr::NTuple{2,T}, ::Val{R})::NTuple{2,T} + philox(key::NTuple{2,T}, ctr::NTuple{4,T}, ::Val{R})::NTuple{4,T} + +Functional variant of [`Philox2x`](@ref) and [`Philox4x`](@ref). +Produces a pseudorandom output of type `T = UInt64` or `T = UInt32` from the inputs. +This function if free of mutability and side effects. +""" +@inline function philox(key_::Tuple{T}, ctr::NTuple{2,T}, ::Val{R}) where {T,R} + key = first(key_) + ctr1, ctr2 = ctr if R > 0 ctr1, ctr2 = philox2x_round(ctr1, ctr2, key); end if R > 1 key = philox2x_bumpkey(key); ctr1, ctr2 = philox2x_round(ctr1, ctr2, key); end if R > 2 key = philox2x_bumpkey(key); ctr1, ctr2 = philox2x_round(ctr1, ctr2, key); end @@ -107,7 +122,7 @@ end if R > 13 key = philox2x_bumpkey(key); ctr1, ctr2 = philox2x_round(ctr1, ctr2, key); end if R > 14 key = philox2x_bumpkey(key); ctr1, ctr2 = philox2x_round(ctr1, ctr2, key); end if R > 15 key = philox2x_bumpkey(key); ctr1, ctr2 = philox2x_round(ctr1, ctr2, key); end - r.x1, r.x2 = ctr1, ctr2 + ctr1, ctr2 end """ @@ -184,8 +199,14 @@ end end @inline function random123_r(r::Philox4x{T, R}) where {T <: Union{UInt32, UInt64}, R} - ctr1, ctr2, ctr3, ctr4 = r.ctr1, r.ctr2, r.ctr3, r.ctr4 - key1, key2 = r.key1, r.key2 + ctr = r.ctr1, r.ctr2, r.ctr3, r.ctr4 + key = r.key1, r.key2 + r.x1, r.x2, r.x3, r.x4 = philox(key, ctr, Val(R)) +end + +@inline function philox(key::NTuple{2,T}, ctr::NTuple{4,T}, ::Val{R}) where {T <:Union{UInt32, UInt64}, R} + ctr1, ctr2, ctr3, ctr4 = ctr + key1, key2 = key if R > 0 ctr1, ctr2, ctr3, ctr4 = philox4x_round(ctr1, ctr2, ctr3, ctr4, key1, key2); end @@ -249,5 +270,5 @@ end key1, key2 = philox4x_bumpkey(key1, key2); ctr1, ctr2, ctr3, ctr4 = philox4x_round(ctr1, ctr2, ctr3, ctr4, key1, key2); end - r.x1, r.x2, r.x3, r.x4 = ctr1, ctr2, ctr3, ctr4 + ctr1, ctr2, ctr3, ctr4 end From 1919861d1b2fc9ead16ebea6e687abaafc97c54e Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Wed, 27 Jul 2022 09:20:20 +0200 Subject: [PATCH 3/6] add aesni --- src/aesni.jl | 42 ++++++++++++++++++++---------- src/philox.jl | 13 ++++----- src/threefry.jl | 13 ++++----- test/runtests.jl | 68 ++++++++++++++++++++++++++++++++++++------------ 4 files changed, 93 insertions(+), 43 deletions(-) diff --git a/src/aesni.jl b/src/aesni.jl index 7c8da04..ac18b42 100644 --- a/src/aesni.jl +++ b/src/aesni.jl @@ -201,26 +201,40 @@ copy(src::AESNI4x) = copyto!(AESNI4x(), src) ==(r1::AESNI4x, r2::AESNI4x) = unsafe_compare(r1, r2, UInt128, 2) && r1.key == r2.key && r1.p == r2.p -function aesni1xm128i(input::__m128i, key::AESNIKey) - x = key.key1 ⊻ input - x = _aes_enc(x, key.key2) - x = _aes_enc(x, key.key3) - x = _aes_enc(x, key.key4) - x = _aes_enc(x, key.key5) - x = _aes_enc(x, key.key6) - x = _aes_enc(x, key.key7) - x = _aes_enc(x, key.key8) - x = _aes_enc(x, key.key9) - x = _aes_enc(x, key.key10) - x = _aes_enc_last(x, key.key11) +function get_key(o::Union{AESNI1x, AESNI4x})::NTuple{11, __m128i} + k = o.key + (k.key1,k.key2,k.key3,k.key4,k.key5,k.key6,k.key7,k.key8,k.key9,k.key10,k.key11) +end +function get_ctr(o::AESNI4x)::Tuple{__m128i} + (o.ctr1,) +end +function get_ctr(o::AESNI1x)::Tuple{__m128i} + (o.ctr,) +end + +@inline function aesni(key::NTuple{11,__m128i}, ctr::Tuple{__m128i})::Tuple{__m128i} + key1, key2, key3, key4, key5, key6, key7, key8, key9, key10, key11 = key + ctr1 = only(ctr) + x = key1 ⊻ ctr1 + x = _aes_enc(x, key2) + x = _aes_enc(x, key3) + x = _aes_enc(x, key4) + x = _aes_enc(x, key5) + x = _aes_enc(x, key6) + x = _aes_enc(x, key7) + x = _aes_enc(x, key8) + x = _aes_enc(x, key9) + x = _aes_enc(x, key10) + x = _aes_enc_last(x, key11) + (x,) end @inline function random123_r(r::AESNI1x) - r.x = aesni1xm128i(r.ctr, r.key) + r.x = only(aesni(get_key(r), get_ctr(r))) (UInt128(r.x),) end @inline function random123_r(r::AESNI4x) - r.x = aesni1xm128i(r.ctr1, r.key) + r.x = only(aesni(get_key(r), get_ctr(r))) split_uint(UInt128(r.x), UInt32) end diff --git a/src/philox.jl b/src/philox.jl index fa17864..e799d99 100644 --- a/src/philox.jl +++ b/src/philox.jl @@ -89,10 +89,11 @@ end key + PHILOX_W_0(T) end +@inline get_key(r::Philox2x) = (r.key,) +@inline get_ctr(r::Philox2x) = (r.ctr1, r.ctr2) + @inline function random123_r(r::Philox2x{T, R}) where {T <: Union{UInt32, UInt64}, R} - key = (r.key,) - ctr = (r.ctr1, r.ctr2) - r.x1, r.x2 = philox(key, ctr, Val(R)) + r.x1, r.x2 = philox(get_key(r), get_ctr(r), Val(R)) end """ @@ -198,10 +199,10 @@ end key1 + PHILOX_W_0(T), key2 + PHILOX_W_1(T) end +@inline get_ctr(r::Philox4x) = (r.ctr1, r.ctr2, r.ctr3, r.ctr4) +@inline get_key(r::Philox4x) = (r.key1, r.key2) @inline function random123_r(r::Philox4x{T, R}) where {T <: Union{UInt32, UInt64}, R} - ctr = r.ctr1, r.ctr2, r.ctr3, r.ctr4 - key = r.key1, r.key2 - r.x1, r.x2, r.x3, r.x4 = philox(key, ctr, Val(R)) + r.x1, r.x2, r.x3, r.x4 = philox(get_key(r), get_ctr(r), Val(R)) end @inline function philox(key::NTuple{2,T}, ctr::NTuple{4,T}, ::Val{R}) where {T <:Union{UInt32, UInt64}, R} diff --git a/src/threefry.jl b/src/threefry.jl index 9be9448..34e8985 100644 --- a/src/threefry.jl +++ b/src/threefry.jl @@ -116,10 +116,10 @@ copy(src::Threefry2x{T, R}) where {T, R} = Threefry2x{T, R}(src.x1, src.x2, src. ==(r1::Threefry2x{T, R}, r2::Threefry2x{T, R}) where {T, R} = unsafe_compare(r1, r2, T, 6) && r1.p == r2.p +@inline get_key(r::Threefry2x) = (r.key1, r.key2) +@inline get_ctr(r::Threefry2x) = (r.ctr1, r.ctr2) @inline function random123_r(r::Threefry2x{T, R}) where {T <: Union{UInt32, UInt64}, R} - key = (r.key1, r.key2) - ctr = (r.ctr1, r.ctr2) - r.x1, r.x2 = threefry(key, ctr, Val(R)) + r.x1, r.x2 = threefry(get_key(r), get_ctr(r), Val(R)) end """ @@ -269,10 +269,11 @@ copy(src::Threefry4x{T, R}) where {T, R} = Threefry4x{T, R}(src.x1, src.x2, src. ==(r1::Threefry4x{T, R}, r2::Threefry4x{T, R}) where {T, R} = unsafe_compare(r1, r2, T, 12) && r1.p == r2.p +@inline get_key(r::Threefry4x) = (r.key1, r.key2, r.key3, r.key4) +@inline get_ctr(r::Threefry4x) = (r.ctr1, r.ctr2, r.ctr3, r.ctr4) + @inline function random123_r(r::Threefry4x{T, R}) where {T <: Union{UInt32, UInt64}, R} - key = (r.key1, r.key2, r.key3, r.key4) - ctr = (r.ctr1, r.ctr2, r.ctr3, r.ctr4) - r.x1, r.x2, r.x3, r.x4 = threefry(key, ctr, Val(R)) + r.x1, r.x2, r.x3, r.x4 = threefry(get_key(r), get_ctr(r), Val(R)) end @inline function threefry(key::NTuple{4,T},ctr::NTuple{4,T}, rounds::Val{R})::NTuple{4,T} where {T <: Union{UInt32, UInt64}, R} diff --git a/test/runtests.jl b/test/runtests.jl index b1f001d..92b6311 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,27 +1,60 @@ using RandomNumbers using Random123 import Random: seed! -using Test: @test, @test_throws, @testset +using Test: @test, @test_throws, @testset, @inferred using Printf: @printf -@testset "functional" begin +@testset "functional API" begin threefry = Random123.threefry + philox = Random123.philox + aesni = Random123.aesni + get_key = Random123.get_key + get_ctr = Random123.get_ctr + seed1 = 1 + seed2 = (1,2) + seed4 = (1,2,3,4) + for (rng, alg, options) in [ + (Threefry2x(UInt32, seed2) , threefry, (Val(20),)) , + (Threefry2x(UInt64, seed2) , threefry, (Val(20),)) , + (Threefry4x(UInt32, seed4) , threefry, (Val(20),)) , + (Threefry4x(UInt64, seed4) , threefry, (Val(20),)) , + (Philox2x(UInt32 , seed1) , philox , (Val(10),)) , + (Philox2x(UInt64 , seed1) , philox , (Val(10),)) , + (Philox4x(UInt32 , seed2) , philox , (Val(10),)) , + (Philox4x(UInt64 , seed2) , philox , (Val(10),)) , + (AESNI1x(seed1) , aesni , () ) , + (AESNI4x(seed4) , aesni , () ) , + ] + key = @inferred get_key(rng) + ctr = @inferred get_ctr(rng) + @test isbitstype(typeof(key)) + @test isbitstype(typeof(ctr)) + @test key isa Tuple + @test ctr isa Tuple + val1 = @inferred alg(key, ctr, options...) + val2 = @inferred alg(key, ctr, options...) + @test val1 === val2 + @test val1 isa Tuple + @test isbitstype(typeof(val1)) + end + for T in [UInt32, UInt64] - key = (T(123), T(456)) - rng = Threefry2x(T, key) - x1 = rand(rng, T) - x2 = rand(rng, T) - x3 = rand(rng, T) - x4 = rand(rng, T) - x5 = rand(rng, T) - y1,y0 = threefry(key, (T(0), T(0)), Val(20)) - y3,y2 = threefry(key, (T(1), T(0)), Val(20)) - y5,y4 = threefry(key, (T(2), T(0)), Val(20)) - @test x1 === y1 - @test x2 === y2 - @test x3 === y3 - @test x4 === y4 - @test x5 === y5 + for (rng, alg, option) in [ + (Threefry2x(T, (T(123), T(456))), threefry, Val(20)), + (Philox2x(T, 456), philox, Val(10)), + ] + + key = @inferred get_key(rng) + x1 = rand(rng, T) + y1,y0 = alg(key, get_ctr(rng), option) + @test x1 === y1 + x2 = rand(rng, T) + x3 = rand(rng, T) + ctr = get_ctr(rng) + y3,y2 = alg(key, get_ctr(rng), option) + @test x2 === y2 + @test x3 === y3 + end end for T in [UInt32, UInt64] key = (T(123), T(456), T(7), T(8)) @@ -50,6 +83,7 @@ using Printf: @printf end end + function compare_dirs(dir1::AbstractString, dir2::AbstractString) files1 = readdir(dir1) files2 = readdir(dir2) From e90a917c406be48974189de7cde17a259bc81cca Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Wed, 27 Jul 2022 09:38:07 +0200 Subject: [PATCH 4/6] add ars --- src/ars.jl | 32 ++++++++++++++++++++++++-------- test/runtests.jl | 9 +++++++-- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/ars.jl b/src/ars.jl index b145c28..0b817e1 100644 --- a/src/ars.jl +++ b/src/ars.jl @@ -98,19 +98,15 @@ copy(src::ARS4x{R}) where R = ARS4x{R}(src.x, src.ctr1, src.key, src.p) ==(r1::ARS4x{R}, r2::ARS4x{R}) where R = unsafe_compare(r1, r2, UInt128, 3) && r1.p ≡ r2.p -@generated function ars1xm128i(r::Union{ARS1x{R}, ARS4x{R}}) where R +function expr_ars1xm128i(expr_key::Expr, expr_ctr::Expr, R) @assert R isa Int && 1 ≤ R ≤ 10 rounds = [quote kk += kweyl v = _aes_enc(v, kk) end for _ in 2:R] - ctr = :(r.ctr) - if r <: ARS4x - ctr.args[2] = :(:ctr1) - end quote - ctr = $ctr - key = r.key + ctr = $(expr_ctr) + key = $(expr_key) kweyl = __m128i(0xbb67ae8584caa73b, 0x9e3779b97f4a7c15) kk = key v = ctr ⊻ kk @@ -122,12 +118,32 @@ copy(src::ARS4x{R}) where R = ARS4x{R}(src.x, src.ctr1, src.key, src.p) end end +@generated function ars1xm128i(r::Union{ARS1x{R}, ARS4x{R}}) where R + expr_ctr = if r <: ARS1x + :(r.ctr) + elseif r <: ARS4x + :(r.ctr1) + else + :(error("Unreachable")) + end + expr_key = :(r.key) + expr_ars1xm128i(expr_key, expr_ctr, R) +end + +@generated function ars(key, ctr, ::Val{R}) where {R} + expr_key = :(only(key)) + expr_ctr = :(only(ctr)) + expr_ars1xm128i(expr_key, expr_ctr, R) +end +get_key(r::Union{ARS1x, ARS4x})::Tuple{__m128i} = (r.key,) +get_ctr(r::ARS1x)::Tuple{__m128i} = (r.ctr,) +get_ctr(r::ARS4x)::Tuple{__m128i} = (r.ctr1,) + @inline function random123_r(r::ARS1x{R}) where R r.x = ars1xm128i(r) (UInt128(r.x),) end - @inline function random123_r(r::ARS4x{R}) where R r.x = ars1xm128i(r) split_uint(UInt128(r.x), UInt32) diff --git a/test/runtests.jl b/test/runtests.jl index 92b6311..b59fbd5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,10 +4,13 @@ import Random: seed! using Test: @test, @test_throws, @testset, @inferred using Printf: @printf +@info "Testing Random123" + @testset "functional API" begin threefry = Random123.threefry philox = Random123.philox aesni = Random123.aesni + ars = Random123.ars get_key = Random123.get_key get_ctr = Random123.get_ctr seed1 = 1 @@ -24,6 +27,8 @@ using Printf: @printf (Philox4x(UInt64 , seed2) , philox , (Val(10),)) , (AESNI1x(seed1) , aesni , () ) , (AESNI4x(seed4) , aesni , () ) , + (ARS1x(seed1) , ars , (Val(7),) ) , + (ARS4x(seed4) , ars , (Val(7),) ) , ] key = @inferred get_key(rng) ctr = @inferred get_ctr(rng) @@ -37,7 +42,8 @@ using Printf: @printf @test val1 isa Tuple @test isbitstype(typeof(val1)) end - +end +@testset "functional consistency" begin for T in [UInt32, UInt64] for (rng, alg, option) in [ (Threefry2x(T, (T(123), T(456))), threefry, Val(20)), @@ -100,7 +106,6 @@ end strip_cr(line::String) = replace(line, r"\r\n$" => "\n") -@info "Testing Random123" stdout_ = stdout pwd_ = pwd() cd(dirname(@__FILE__)) From c9c5f3d0ce1e5cf9786f42082b281fbc5082add4 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Thu, 28 Jul 2022 10:27:29 +0200 Subject: [PATCH 5/6] make aesni and ars operate on public types --- src/aesni.jl | 29 ++++++++++++++++++++--------- src/ars.jl | 27 +++++++++++++++++++-------- test/runtests.jl | 22 ++++++++++++++++++++++ 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/aesni.jl b/src/aesni.jl index ac18b42..0ecad5d 100644 --- a/src/aesni.jl +++ b/src/aesni.jl @@ -201,16 +201,14 @@ copy(src::AESNI4x) = copyto!(AESNI4x(), src) ==(r1::AESNI4x, r2::AESNI4x) = unsafe_compare(r1, r2, UInt128, 2) && r1.key == r2.key && r1.p == r2.p -function get_key(o::Union{AESNI1x, AESNI4x})::NTuple{11, __m128i} +function get_key__m128i(o::Union{AESNI1x, AESNI4x})::NTuple{11, __m128i} k = o.key (k.key1,k.key2,k.key3,k.key4,k.key5,k.key6,k.key7,k.key8,k.key9,k.key10,k.key11) end -function get_ctr(o::AESNI4x)::Tuple{__m128i} - (o.ctr1,) -end -function get_ctr(o::AESNI1x)::Tuple{__m128i} - (o.ctr,) -end +get_ctr__m128i(o::AESNI4x)::Tuple{__m128i} = (o.ctr1,) +get_ctr__m128i(o::AESNI1x)::Tuple{__m128i} = (o.ctr,) +get_key(o::Union{AESNI1x, AESNI4x})::NTuple{11,UInt128} = map(UInt128, get_key__m128i(o)) +get_ctr(o::Union{AESNI1x, AESNI4x})::Tuple{UInt128} = map(UInt128, get_ctr__m128i(o)) @inline function aesni(key::NTuple{11,__m128i}, ctr::Tuple{__m128i})::Tuple{__m128i} key1, key2, key3, key4, key5, key6, key7, key8, key9, key10, key11 = key @@ -229,12 +227,25 @@ end (x,) end +""" + aesni(key::NTuple{11,UInt128}, ctr::Tuple{UInt128})::Tuple{UInt128} + +Functional variant of [`AESNI1x`](@ref) and [`AESNI4x`](@ref). +This function if free of mutability and side effects. +""" +@inline function aesni(key::NTuple{11,UInt128}, ctr::Tuple{UInt128})::Tuple{UInt128} + k = map(__m128i, key) + c = map(__m128i, ctr) + map(UInt128,aesni(k,c)) +end + + @inline function random123_r(r::AESNI1x) - r.x = only(aesni(get_key(r), get_ctr(r))) + r.x = only(aesni(get_key__m128i(r), get_ctr__m128i(r))) (UInt128(r.x),) end @inline function random123_r(r::AESNI4x) - r.x = only(aesni(get_key(r), get_ctr(r))) + r.x = only(aesni(get_key__m128i(r), get_ctr__m128i(r))) split_uint(UInt128(r.x), UInt32) end diff --git a/src/ars.jl b/src/ars.jl index 0b817e1..f18cde0 100644 --- a/src/ars.jl +++ b/src/ars.jl @@ -98,7 +98,7 @@ copy(src::ARS4x{R}) where R = ARS4x{R}(src.x, src.ctr1, src.key, src.p) ==(r1::ARS4x{R}, r2::ARS4x{R}) where R = unsafe_compare(r1, r2, UInt128, 3) && r1.p ≡ r2.p -function expr_ars1xm128i(expr_key::Expr, expr_ctr::Expr, R) +function expr_ars1xm128i(expr_key, expr_ctr, R) @assert R isa Int && 1 ≤ R ≤ 10 rounds = [quote kk += kweyl @@ -130,14 +130,25 @@ end expr_ars1xm128i(expr_key, expr_ctr, R) end -@generated function ars(key, ctr, ::Val{R}) where {R} - expr_key = :(only(key)) - expr_ctr = :(only(ctr)) - expr_ars1xm128i(expr_key, expr_ctr, R) +@generated function ars(key::Tuple{__m128i}, ctr::Tuple{__m128i}, ::Val{R})::Tuple{__m128i} where {R} + :(($(expr_ars1xm128i(:(only(key)), :(only(ctr)), R)),)) end -get_key(r::Union{ARS1x, ARS4x})::Tuple{__m128i} = (r.key,) -get_ctr(r::ARS1x)::Tuple{__m128i} = (r.ctr,) -get_ctr(r::ARS4x)::Tuple{__m128i} = (r.ctr1,) + +""" + ars(key::Tuple{UInt128}, ctr::Tuple{UInt128}, rounds::Val{R})::Tuple{UInt128} where {R} + +Functional variant of [`ARS1x`](@ref) and [`ARS4x`](@ref). +This function if free of mutability and side effects. +""" +function ars(key::Tuple{UInt128}, ctr::Tuple{UInt128}, rounds::Val{R})::Tuple{UInt128} where {R} + k = map(__m128i, key) + c = map(__m128i, ctr) + map(UInt128,ars(k,c,rounds)) +end + +get_key(r::Union{ARS1x, ARS4x}) = (UInt128(r.key),) +get_ctr(r::ARS1x) = (UInt128(r.ctr),) +get_ctr(r::ARS4x) = (UInt128(r.ctr1),) @inline function random123_r(r::ARS1x{R}) where R r.x = ars1xm128i(r) diff --git a/test/runtests.jl b/test/runtests.jl index b59fbd5..81a3313 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -36,14 +36,23 @@ using Printf: @printf @test isbitstype(typeof(ctr)) @test key isa Tuple @test ctr isa Tuple + @test eltype(key) <: Union{UInt32, UInt64, UInt128} + @test eltype(ctr) <: Union{UInt32, UInt64, UInt128} val1 = @inferred alg(key, ctr, options...) val2 = @inferred alg(key, ctr, options...) @test val1 === val2 @test val1 isa Tuple @test isbitstype(typeof(val1)) + @test eltype(val1) <: Union{UInt32, UInt64, UInt128} end end @testset "functional consistency" begin + threefry = Random123.threefry + philox = Random123.philox + aesni = Random123.aesni + ars = Random123.ars + get_key = Random123.get_key + get_ctr = Random123.get_ctr for T in [UInt32, UInt64] for (rng, alg, option) in [ (Threefry2x(T, (T(123), T(456))), threefry, Val(20)), @@ -87,6 +96,19 @@ end @test x8 === y8 @test x9 === y9 end + + rng = ARS1x(1) + @test (rand(rng, UInt128),) === ars(get_key(rng), get_ctr(rng), Val(7)) + @test (rand(rng, UInt128),) === ars(get_key(rng), get_ctr(rng), Val(7)) + @test (rand(rng, UInt128),) === ars(get_key(rng), get_ctr(rng), Val(7)) + @test (rand(rng, UInt128),) === ars(get_key(rng), get_ctr(rng), Val(7)) + + rng = AESNI1x(1) + @test (rand(rng, UInt128),) === aesni(get_key(rng), get_ctr(rng)) + @test (rand(rng, UInt128),) === aesni(get_key(rng), get_ctr(rng)) + @test (rand(rng, UInt128),) === aesni(get_key(rng), get_ctr(rng)) + @test (rand(rng, UInt128),) === aesni(get_key(rng), get_ctr(rng)) + end From f49cdfca3918d35c605a256e748e75c683cdb812 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Thu, 28 Jul 2022 19:39:51 +0200 Subject: [PATCH 6/6] export threefry, philox, aesni, ars --- src/Random123.jl | 8 ++++---- test/runtests.jl | 8 -------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/Random123.jl b/src/Random123.jl index e383e79..8d09158 100644 --- a/src/Random123.jl +++ b/src/Random123.jl @@ -21,10 +21,10 @@ using RandomNumbers export set_counter! include("common.jl") -export Threefry2x, Threefry4x +export Threefry2x, Threefry4x, threefry include("threefry.jl") -export Philox2x, Philox4x +export Philox2x, Philox4x, philox include("philox.jl") export R123_USE_AESNI @@ -46,8 +46,8 @@ catch e end @static if R123_USE_AESNI - export AESNI1x, AESNI4x - export ARS1x, ARS4x + export AESNI1x, AESNI4x, aesni + export ARS1x, ARS4x, ars include("./aesni_common.jl") include("./aesni.jl") include("./ars.jl") diff --git a/test/runtests.jl b/test/runtests.jl index 81a3313..17000d3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -7,10 +7,6 @@ using Printf: @printf @info "Testing Random123" @testset "functional API" begin - threefry = Random123.threefry - philox = Random123.philox - aesni = Random123.aesni - ars = Random123.ars get_key = Random123.get_key get_ctr = Random123.get_ctr seed1 = 1 @@ -47,10 +43,6 @@ using Printf: @printf end end @testset "functional consistency" begin - threefry = Random123.threefry - philox = Random123.philox - aesni = Random123.aesni - ars = Random123.ars get_key = Random123.get_key get_ctr = Random123.get_ctr for T in [UInt32, UInt64]