Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Aqua test & fix all ambiguities and unbound args #115

Merged
merged 10 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.vscode
*.swp
*.jl.cov
*.jl.*.cov
Expand Down
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ Requires = "0.5.0, 1"
julia = "1"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[targets]
test = ["QuadGK", "SpecialFunctions", "Statistics", "Test", "Unitful"]
test = ["Aqua", "QuadGK", "SpecialFunctions", "Statistics", "Test", "Unitful"]
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Measurements.jl

| **Documentation** | **Build Status** | **Code Coverage** |
|:---------------------------------------:|:-----------------------------------:|:-------------------------------:|
| [![][docs-stable-img]][docs-stable-url] | [![Build Status][gha-img]][gha-url] | [![][coveral-img]][coveral-url] |
| **Documentation** | **Build Status** | **Code Coverage** | **Quality** |
|:---------------------------------------:|:-----------------------------------:|:-------------------------------:|:-----------:|
| [![][docs-stable-img]][docs-stable-url] | [![Build Status][gha-img]][gha-url] | [![][coveral-img]][coveral-url] |[![Aqua QA][aqua-img]](aqua-url)|
| [![][docs-latest-img]][docs-latest-url] | | [![][codecov-img]][codecov-url] |

Introduction
Expand Down Expand Up @@ -219,3 +219,6 @@ is provided in the [`CITATION.bib`](CITATION.bib) file.

[codecov-img]: https://codecov.io/gh/JuliaPhysics/Measurements.jl/branch/master/graph/badge.svg
[codecov-url]: https://codecov.io/gh/JuliaPhysics/Measurements.jl

[aqua-img]: https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg
[aqua-url]: https://github.com/JuliaTesting/Aqua.jl
14 changes: 13 additions & 1 deletion src/Measurements.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,19 @@ function Measurement(val::V, err::E, tag::UInt64,
end
Measurement{T}(x::Measurement{S}) where {T,S} = convert(Measurement{T}, x)
Measurement{T}(x::S) where {T,S} = convert(Measurement{T}, x)
Measurement{T}(x::S) where {T,S<:Rational} = convert(Measurement{T}, x)

# disambiguities
Measurement{T}(x::S) where {T, S<:Rational} = convert(Measurement{T}, x)
Measurement{T}(x::S) where {T, S<:Complex} = convert(Measurement{T}, x)
Measurement{T}(x::S) where {T, S<:Base.TwicePrecision} = convert(Measurement{T}, x)
Measurement{T}(x::S) where {P, T, S<:Rational{P}} = convert(Measurement{T}, x)


function Measurement{T}(::S) where {T, S<:AbstractChar}
throw(ArgumentError("cannot convert `$S` to `Measurement{$T}`"))
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised this is needed at all. Or anyway AbstractChar looks oddly specific, why that type and not anything else? Perhaps S shouldn't have any constraint (or S<:Any, which is the same) and this would be the generic fallback for non Real types?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you comment out this function, then you will see the following

1 ambiguities found
Ambiguity #1
Measurements.Measurement{T}(x::S) where {T, S} in Measurements at /home/roger/code/julia/Measurements/src/Measurements.jl:63
(::Type{T})(x::AbstractChar) where T<:Union{AbstractChar, Number} in Base at char.jl:50

Possible fix, define
  Measurements.Measurement{T}(::S) where {T, S<:AbstractChar}

I think this is due to the fact you can convert a Char to a Number defined in Base. And because Base uses AbstractChar so we have to use AbstractChar, why S <: Any is the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, should it work for converting things to Measurement? since the following works

Float64('a')

it is probably strange that meanwhile

Measurement{Float64}('a')

doesn't work. I'm not sure this is useful anyway tho, so either is fine

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yes, I'm looking forward to character arithmetic with uncertainties: 'z' ± 'a'! 😁




# Functions to quickly create an empty Derivatives object.
@generated empty_der1(x::Measurement{T}) where {T<:AbstractFloat} = Derivatives{T}()
Expand Down
4 changes: 4 additions & 0 deletions src/comparisons-tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ Base.:(==)(a::Measurement, b::Measurement) = (a.val==b.val && a.err==b.err)
Base.:(==)(a::Measurement, b::Irrational) = false
Base.:(==)(a::Measurement, b::Rational) = (a.val==b && iszero(a.err))
Base.:(==)(a::Measurement, b::Real) = (a.val==b && iszero(a.err))
Base.:(==)(a::Measurement, b::AbstractIrrational) = (a.val==b && iszero(b.err))
Base.:(==)(a::Irrational, b::Measurement) = false
Base.:(==)(a::Rational, b::Measurement) = (a==b.val && iszero(b.err))
Base.:(==)(a::Real, b::Measurement) = (a==b.val && iszero(b.err))
Base.:(==)(a::AbstractIrrational, b::Measurement) = (a==b.val && iszero(b.err))

# Create a hashing function that matches the same behaviour as `==`: only the
# `val` and `err` fields matter.
Expand All @@ -39,8 +41,10 @@ for cmp in (:<, :<=)
Base.$cmp(a::Measurement, b::Measurement) = ($cmp)(a.val, b.val)
Base.$cmp(a::Measurement, b::Rational) = ($cmp)(a.val, b)
Base.$cmp(a::Measurement, b::Real) = ($cmp)(a.val, b)
Base.$cmp(a::Measurement, b::AbstractIrrational) = ($cmp)(a.val, b)
Base.$cmp(a::Rational, b::Measurement) = ($cmp)(a, b.val)
Base.$cmp(a::Real, b::Measurement) = ($cmp)(a, b.val)
Base.$cmp(a::AbstractIrrational, b::Measurement) = ($cmp)(a, b.val)
end
end

Expand Down
10 changes: 10 additions & 0 deletions src/conversions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ Base.convert(::Type{Measurement{T}}, a::Rational{<:Integer}) where {T<:AbstractF
measurement(T(a))::Measurement{T}
Base.convert(::Type{Measurement{T}}, a::Real) where {T<:AbstractFloat} =
measurement(T(a))::Measurement{T}
Base.convert(::Type{Measurement{T}}, a::Base.TwicePrecision) where {T<:AbstractFloat} =
measurement(T(a))::Measurement{T}

function Base.convert(::Type{Measurement{T}}, a::Complex) where {T}
if isreal(a)
measurement(T(real(a)))
else
throw(InexactError(:convert, Measurement{T}, a))
end
end

Base.convert(::Type{Measurement{T}}, a::Measurement{T}) where {T<:AbstractFloat} = a
function Base.convert(::Type{Measurement{T}},
Expand Down
17 changes: 15 additions & 2 deletions src/math.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ end
# Get the common type parameter of a collection of Measurement objects. The first two
# methods are for the trivial cases of homogeneous tuples and arrays, the last, inefficient,
# method is for inhomogeneous collections (probably the least common case).
gettype(::Tuple{Vararg{Measurement{T}}}) where {T<:AbstractFloat} = T
gettype(::Tuple{Measurement{T}, Vararg{Measurement{T}}}) where {T<:AbstractFloat} = T
gettype(::AbstractArray{Measurement{T}}) where {T<:AbstractFloat} = T
_eltype(::Measurement{T}) where {T<:AbstractFloat} = T
gettype(collection) = promote_type(_eltype.(collection)...)
Expand Down Expand Up @@ -174,6 +174,7 @@ end
# Addition: +
Base.:+(a::Measurement, b::Measurement) = result(a.val + b.val, (1, 1), (a, b))
Base.:+(a::Real, b::Measurement) = result(a + b.val, 1, b)
Base.:+(a::Bool, b::Measurement) = result(a + b.val, 1, b)
Base.:+(a::Measurement, b::Bool) = result(a.val + b, 1, a)
Base.:+(a::Measurement, b::Real) = result(a.val + b, 1, a)

Expand Down Expand Up @@ -685,9 +686,12 @@ Base.sign(a::Measurement) = result(sign(a.val), 0, a)

Base.copysign(a::Measurement, b::Measurement) = ifelse(signbit(a)!=signbit(b), -a, a)
Base.copysign(a::Measurement, b::Real) = ifelse(signbit(a)!=signbit(b), -a, a)
Base.copysign(a::Measurement, b::Unsigned) = ifelse(signbit(a)!=signbit(b), -a, a)

Base.flipsign(a::Measurement, b::Measurement) = ifelse(signbit(b), -a, a)
Base.flipsign(a::Measurement, b::Real) = ifelse(signbit(b), -a, a)
for T in (Signed, Rational, Float32, Float64, Real)
Base.flipsign(a::Measurement, b::Unsigned) = ifelse(signbit(b), -a, a)
for T in (Signed, Unsigned, Rational, Float32, Float64, Real)
@eval Base.copysign(a::$T, b::Measurement) = copysign(a, b.val)
@eval Base.flipsign(a::$T, b::Measurement) = flipsign(a, b.val)
end
Expand Down Expand Up @@ -734,6 +738,15 @@ Base.round(a::Measurement, r::RoundingMode=RoundNearest; kwargs...) =
measurement(round(value(a), r; kwargs...), round(uncertainty(a); kwargs...))
Base.round(::Type{T}, a::Measurement, r::RoundingMode=RoundNearest) where {T<:Integer} =
round(T, a.val, r)

# disambiguities
Base.round(a::Measurement, r::RoundingMode{:NearestTiesAway}; kwargs...) =
measurement(round(value(a), r; kwargs...))
Base.round(a::Measurement, r::RoundingMode{:NearestTiesUp}; kwargs...) =
measurement(round(value(a), r; kwargs...))
Base.round(::Type{T}, a::Measurement, r::RoundingMode{:ToZero}) where {T<:Integer} =
measurement(round(T, value(a), r))

Base.floor(a::Measurement) = measurement(floor(a.val))
Base.floor(::Type{T}, a::Measurement) where {T<:Integer} = floor(T, a.val)
Base.ceil(a::Measurement) = measurement(ceil(a.val))
Expand Down
7 changes: 6 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Measurements, SpecialFunctions, QuadGK, Calculus
using Test, LinearAlgebra, Statistics, Unitful, Printf
using Test, LinearAlgebra, Statistics, Unitful, Printf, Aqua
Aqua.test_all(Measurements)

import Base: isapprox
import Measurements: value, uncertainty
Expand Down Expand Up @@ -104,6 +105,10 @@ end
Measurement{Float64}
@test promote_type(Measurement{BigFloat}, Measurement{Float64}) ==
Measurement{BigFloat}

@test convert(Measurement{Float64}, 1+0im) ≈ 1.0±0.0
@test_throws InexactError convert(Measurement{Float64}, 1+1im)
@test convert(Measurement{Float64}, Base.TwicePrecision(1.0, 0.0)) ≈ 1.0±0.0
end

@testset "Comparisons and Tests" begin
Expand Down