Skip to content

Commit 268cee0

Browse files
authored
Implement more base methods (#121)
* Add methods implemented in complex.jl * Add tests * Add round to docs * Increment patch version number * Avoid line wrapping
1 parent 3be75e4 commit 268cee0

File tree

4 files changed

+119
-1
lines changed

4 files changed

+119
-1
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "Quaternions"
22
uuid = "94ee1d12-ae83-5a48-8b1c-48b8ff168ae0"
3-
version = "0.7.1"
3+
version = "0.7.2"
44

55
[deps]
66
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

docs/src/api.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ real(::Type{Quaternion{T}}) where {T}
1818
imag_part
1919
```
2020

21+
```@docs
22+
round(::Quaternion)
23+
```
24+
2125
```@docs
2226
conj
2327
```

src/Quaternion.jl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ Base.isfinite(q::Quaternion) = isfinite(q.s) & isfinite(q.v1) & isfinite(q.v2) &
147147
Base.iszero(q::Quaternion) = iszero(real(q)) & iszero(q.v1) & iszero(q.v2) & iszero(q.v3)
148148
Base.isnan(q::Quaternion) = isnan(real(q)) | isnan(q.v1) | isnan(q.v2) | isnan(q.v3)
149149
Base.isinf(q::Quaternion) = isinf(q.s) | isinf(q.v1) | isinf(q.v2) | isinf(q.v3)
150+
Base.isinteger(q::Quaternion) = isinteger(real(q)) & isreal(q)
150151

151152
# included strictly for documentation; the base implementation is sufficient
152153
"""
@@ -384,3 +385,59 @@ LinearAlgebra.lyap(a::Quaternion, c::Quaternion) = lyap(promote(a, c)...)
384385
# if a commutes with c, use a simpler expression
385386
LinearAlgebra.lyap(a::Real, c::Quaternion) = c / -2a
386387
LinearAlgebra.lyap(a::Quaternion, c::Real) = c / -2real(a)
388+
389+
Base.widen(::Type{Quaternion{T}}) where {T} = Quaternion{widen(T)}
390+
391+
Base.flipsign(x::Quaternion, y::Real) = ifelse(signbit(y), -x, x)
392+
393+
function Base.read(io::IO, ::Type{Quaternion{T}}) where T<:Real
394+
return Quaternion{T}(ntuple(_ -> read(io, T), Val(4))...)
395+
end
396+
Base.write(io::IO, q::Quaternion) = write(io, real(q), imag_part(q)...)
397+
398+
Base.big(::Type{Quaternion{T}}) where {T<:Real} = Quaternion{big(T)}
399+
Base.big(z::Quaternion{T}) where {T<:Real} = Quaternion{big(T)}(z)
400+
401+
"""
402+
round(q::Quaternion[, RoundingModeReal, [RoundingModeImaginary]]; kwargs...)
403+
round(q::Quaternion, RoundingModeReal,
404+
RoundingModeImaginary1, RoundingModeImaginary2, RoundingModeImaginary3; kwargs...)
405+
406+
Return the nearest integral value of the same type as the quaternion-valued `q` to `q`,
407+
breaking ties using the specified `RoundingMode`s.
408+
409+
The first `RoundingMode` is used for rounding the real part while the second is used
410+
for rounding the imaginary parts. Alternatively, a `RoundingMode` may be provided for each
411+
part.
412+
413+
The `kwargs` are the same as those for `round(::Real[, RoundingMode]; kwargs...)`.
414+
415+
# Example
416+
```jldoctest
417+
julia> round(quat(3.14, 4.5, 8.3, -2.8))
418+
QuaternionF64(3.0, 4.0, 8.0, -3.0)
419+
```
420+
"""
421+
function Base.round(
422+
q::Quaternion,
423+
rs::RoundingMode=RoundNearest,
424+
rv::RoundingMode=rs;
425+
kwargs...,
426+
)
427+
return round(q, rs, rv, rv, rv; kwargs...)
428+
end
429+
function Base.round(
430+
q::Quaternion,
431+
rs::RoundingMode,
432+
rv1::RoundingMode,
433+
rv2::RoundingMode,
434+
rv3::RoundingMode;
435+
kwargs...,
436+
)
437+
return Quaternion(
438+
round(real(q), rs; kwargs...),
439+
round(q.v1, rv1; kwargs...),
440+
round(q.v2, rv2; kwargs...),
441+
round(q.v3, rv3; kwargs...),
442+
)
443+
end

test/Quaternion.jl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,14 @@ end
227227
@test isnan(Quaternion(1, 2, 3, NaN))
228228
end
229229

230+
@testset "isinteger" begin
231+
@test isinteger(quat(3))
232+
@test isinteger(quat(4.0))
233+
@test !isinteger(quat(4.1))
234+
@test !isinteger(quat(3, 1, 2, 3))
235+
@test !isinteger(quat(4, 0, 1, 0))
236+
end
237+
230238
@testset "*" begin
231239
# verify basic correctness
232240
q1 = Quaternion(1,0,0,0)
@@ -571,4 +579,53 @@ end
571579
@test_throws DivideError lyap(null, null)
572580
end
573581
end
582+
583+
@testset "widen" begin
584+
@test widen(Quaternion{Int}) === Quaternion{Int128}
585+
@test widen(QuaternionF32) === QuaternionF64
586+
@test widen(QuaternionF64) === Quaternion{BigFloat}
587+
@test widen(quat(1, 2, 3, 4)) === Quaternion{Int128}(1, 2, 3, 4)
588+
q = rand(QuaternionF32)
589+
@test widen(q) == convert(QuaternionF64, q)
590+
q = rand(QuaternionF64)
591+
@test widen(q) == convert(Quaternion{BigFloat}, q)
592+
end
593+
594+
@testset "flipsign" begin
595+
q = rand(QuaternionF64)
596+
@test flipsign(q, 2) == q
597+
@test flipsign(q, -3) == -q
598+
end
599+
600+
@testset "read/write" begin
601+
@testset "$T" for T in (Int16, Float32, Float64)
602+
io = IOBuffer(; read=true, write=true)
603+
q = rand(Quaternion{T})
604+
write(io, q)
605+
seek(io, 0)
606+
q2 = read(io, Quaternion{T})
607+
@test q == q2
608+
end
609+
end
610+
611+
@testset "big" begin
612+
@test big(Quaternion{Int}) === Quaternion{BigInt}
613+
@test big(QuaternionF64) === Quaternion{BigFloat}
614+
@test big(quat(1, 2, 3, 4)) == Quaternion{BigInt}(1, 2, 3, 4)
615+
q = rand(QuaternionF64)
616+
@test big(q) == convert(Quaternion{BigFloat}, q)
617+
end
618+
619+
@testset "round" begin
620+
q = quat(1.1, 2.5, -3.5, 2.3)
621+
@test round(q) == quat(1.0, 2.0, -4.0, 2.0)
622+
@test round(q; digits=1) == q
623+
@test round(q, RoundUp) == quat(2.0, 3.0, -3.0, 3.0)
624+
@test round(q, RoundUp; digits=1) == q
625+
@test round(q, RoundUp, RoundToZero) == quat(2.0, 2.0, -3.0, 2.0)
626+
@test round(q, RoundUp, RoundToZero; digits=1) == q
627+
rmodes = (RoundUp, RoundDown, RoundNearestTiesAway, RoundToZero)
628+
@test round(q, rmodes...) == quat(2.0, 2.0, -4.0, 2.0)
629+
@test round(q, rmodes...; digits=1) == q
630+
end
574631
end

0 commit comments

Comments
 (0)