diff --git a/src/abstract.jl b/src/abstract.jl index 36fca53..4939ab6 100644 --- a/src/abstract.jl +++ b/src/abstract.jl @@ -25,6 +25,32 @@ All subtypes `C` of `AbstractCliffordNumber{Q}` must implement the following fun abstract type AbstractCliffordNumber{Q,T<:BaseNumber} <: Number end +#---AbstractCliffordNumber passthrough constructors------------------------------------------------# + +AbstractCliffordNumber(x::AbstractCliffordNumber) = x + +function AbstractCliffordNumber(::BaseNumber) + throw( + ArgumentError( + "When constructing a CliffordNumber from a Real or Complex instance, " * + "the algebra type parameter must be specified." + ) + ) +end + +function (::Type{<:AbstractCliffordNumber})(::Number) + throw( + ArgumentError( + "Clifford numbers can only be constructed from Real or Complex arguments.\n" * + "Support for other types may be provided by a package extension." + ) + ) +end + +AbstractCliffordNumber{Q}(x::AbstractCliffordNumber{Q}) where Q = x +# UNDERSTAND: The type constraint on T is required. Why? +AbstractCliffordNumber{Q,T}(x::AbstractCliffordNumber{Q,T}) where {Q,T<:BaseNumber} = x + #---Default varargs constructors for types---------------------------------------------------------# (::Type{T})(x::Vararg{BaseNumber}) where {Q,T<:AbstractCliffordNumber{Q}} = T(x) @@ -138,6 +164,12 @@ similar_type(x, Q::Val) = similar_type(x, scalar_type(x), Q) similar(C::Type{<:AbstractCliffordNumber}, args...) = zero(similar_type(C, args...)) similar(x::AbstractCliffordNumber, args...) = zero(similar_type(x, args...)) +# Perform conversion of the scalar type +# UNDERSTAND: The type constraint on T is required. Why? +function AbstractCliffordNumber{Q,T}(x::AbstractCliffordNumber{Q}) where {Q,T<:BaseNumber} + return similar_type(x, T)(x) +end + """ CliffordNumbers.complement_type(C::Type{<:AbstractCliffordNumber}) CliffordNumbers.complement_type(x::AbstractCliffordNumber) diff --git a/src/convert.jl b/src/convert.jl index a4493f9..c90821a 100644 --- a/src/convert.jl +++ b/src/convert.jl @@ -1,5 +1,6 @@ # Default conversion should check for exact representability -function convert(T::Type{<:AbstractCliffordNumber}, x::AbstractCliffordNumber) +function convert(::Type{T}, x::AbstractCliffordNumber) where T<:AbstractCliffordNumber + x isa T && return x result = T(x)::T return (has_grades_of(x, result) ? result : throw(InexactError(:convert, T, x))) end diff --git a/src/kvector.jl b/src/kvector.jl index 15f3580..e17dde4 100644 --- a/src/kvector.jl +++ b/src/kvector.jl @@ -23,6 +23,11 @@ KVector{K,Q}(x::Tuple{Vararg{T}}) where {K,Q,T<:BaseNumber} = KVector{K,Q,T}(x) # Automatically convert arguments to a common type KVector{K,Q}(x::Tuple{Vararg{BaseNumber}}) where {K,Q} = KVector{K,Q}(promote(x...)) +# AbstractCliffordNumber{Q} constructor converts to a `KVector{0,Q}` +AbstractCliffordNumber{Q}(x::BaseNumber) where Q = KVector{0,Q}(x) +# UNDERSTAND: The type constraint on T is required. Why? +AbstractCliffordNumber{Q,T}(x::BaseNumber) where {Q,T<:BaseNumber} = KVector{0,Q,T}(x) + #---Number of elements-----------------------------------------------------------------------------# nblades(::Type{<:KVector{K,Q}}) where {K,Q} = binomial(dimension(Q), K) diff --git a/test/construction.jl b/test/construction.jl index cf7786d..9ee0bd1 100644 --- a/test/construction.jl +++ b/test/construction.jl @@ -79,6 +79,21 @@ end @test CliffordNumbers.Z2CliffordNumber(k2) === EvenCliffordNumber{VGA(3)}(0, 1, 2, 3) end +@testset "Abstract constructors" begin + k = KVector{1,VGA(3)}(4, 2, 0) + # Things that should work + @test AbstractCliffordNumber(k) === k + @test AbstractCliffordNumber{VGA(3)}(k) === k + @test AbstractCliffordNumber{VGA(3),scalar_type(k)}(k) === k + @test AbstractCliffordNumber{VGA(3),Float64}(k) === float(k) + @test AbstractCliffordNumber{VGA(3)}(1) === one(KVector{0,VGA(3),Int}) + @test AbstractCliffordNumber{VGA(3),Float64}(1) === one(KVector{0,VGA(3),Float64}) + # Things that shouldn't work + @test_throws ArgumentError AbstractCliffordNumber(1) + @test_throws ArgumentError AbstractCliffordNumber(MockNumber()) + @test_throws ArgumentError AbstractCliffordNumber{VGA(3)}(MockNumber()) +end + @testset "Similar types" begin import CliffordNumbers.similar_type @test similar_type(EvenCliffordNumber{VGA(3),Int}, Val(STA)) === EvenCliffordNumber{STA,Int,8} diff --git a/test/conversion.jl b/test/conversion.jl index fbc242d..afa1ba6 100644 --- a/test/conversion.jl +++ b/test/conversion.jl @@ -24,6 +24,21 @@ @test scalar_convert(Float32, 2) === Float32(2) end +@testset "Abstract conversion" begin + k = KVector{1,VGA(3)}(4, 2, 0) + # Things that should work + @test convert(AbstractCliffordNumber, k) === k + @test convert(AbstractCliffordNumber{VGA(3)}, k) === k + @test convert(AbstractCliffordNumber{VGA(3),scalar_type(k)}, k) === k + @test convert(AbstractCliffordNumber{VGA(3),Float64}, k) === float(k) + @test convert(AbstractCliffordNumber{VGA(3)}, 1) === one(KVector{0,VGA(3),Int}) + @test convert(AbstractCliffordNumber{VGA(3),Float64}, 1) === one(KVector{0,VGA(3),Float64}) + # Things that shouldn't + @test_throws ArgumentError convert(AbstractCliffordNumber, 1) + @test_throws ArgumentError convert(AbstractCliffordNumber, MockNumber()) + @test_throws ArgumentError convert(AbstractCliffordNumber{VGA(3)}, MockNumber()) +end + @testset "Promotion" begin @test promote_type(Int, CliffordNumber{VGA(3)}) === CliffordNumber{VGA(3)} @test promote_type(Int, CliffordNumber{VGA(3),Float64}) === CliffordNumber{VGA(3),Float64,8} diff --git a/test/runtests.jl b/test/runtests.jl index 1d0b014..1f0f0ca 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,6 +9,10 @@ Aqua.test_all(CliffordNumbers; unbound_args = false) @basis_vars(PGA(3), Int) @basis_vars(STA) +# A subtype of Number that does not subtype Real or Complex +struct MockNumber <: Number +end + @testset "CliffordNumbers.jl" begin include("internals.jl") include("metrics.jl")