diff --git a/base/Base.jl b/base/Base.jl index 081426fa94d67..7312e285475ce 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -305,6 +305,7 @@ end # numeric operations include("hashing.jl") include("rounding.jl") +include("typedomainintegers.jl"); using .TypeDomainIntegers include("div.jl") include("rawbigints.jl") include("float.jl") @@ -521,6 +522,8 @@ include("irrationals.jl") include("mathconstants.jl") using .MathConstants: ℯ, π, pi +include("typedomainintegers_irrationals.jl") + # Stack frames and traces include("stacktraces.jl") using .StackTraces diff --git a/base/complex.jl b/base/complex.jl index 095c842795d38..ec28350744209 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -86,7 +86,7 @@ julia> imag(1 + 3im) """ imag(z::Complex) = z.im real(x::Real) = x -imag(x::Real) = zero(x) +imag(@nospecialize x::Real) = zero(NonnegativeInteger) """ reim(z) @@ -613,6 +613,14 @@ julia> cispi(0.25 + 1im) """ function cispi end cispi(theta::Real) = Complex(reverse(sincospi(theta))...) +function cispi(@nospecialize n::TypeDomainInteger) + o = one(TypeDomainInteger) + if iseven(n) + o + else + -o + end +end function cispi(z::Complex) v = exp(-(pi*imag(z))) diff --git a/base/gmp.jl b/base/gmp.jl index 1eaa20d6baecf..6cbb0f7cd3dac 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -13,6 +13,8 @@ import .Base: *, +, -, /, <, <<, >>, >>>, <=, ==, >, >=, ^, (~), (&), (|), xor, sign, hastypemax, isodd, iseven, digits!, hash, hash_integer, top_set_bit, clamp +using ..TypeDomainIntegers + if Clong == Int32 const ClongMax = Union{Int8, Int16, Int32} const CulongMax = Union{UInt8, UInt16, UInt32} @@ -389,6 +391,18 @@ function (::Type{T})(x::BigInt) where T<:Base.BitSigned end end +function convert(::Type{NonnegativeInteger}, x::BigInt) + TypeDomainIntegers.Conversion.tdnn_from_x(x) +end +function convert(::Type{TypeDomainInteger}, x::BigInt) + TypeDomainIntegers.Conversion.tdi_from_x(x) +end +function NonnegativeInteger(x::BigInt) + TypeDomainIntegers.Conversion.tdnn_from_x(x) +end +function TypeDomainInteger(x::BigInt) + TypeDomainIntegers.Conversion.tdi_from_x(x) +end Float64(n::BigInt, ::RoundingMode{:ToZero}) = MPZ.get_d(n) @@ -566,6 +580,17 @@ end /(x::BigInt, y::Union{ClongMax,CulongMax}) = float(x)/y /(x::Union{ClongMax,CulongMax}, y::BigInt) = x/float(y) +for func ∈ (:+, :-, :*, :(==)) + @eval begin + function Base.$func((@nospecialize l::BigInt), (@nospecialize r::TypeDomainInteger)) + TypeDomainIntegers.BaseHelpers.apply_n_t($func, l, r) + end + function Base.$func((@nospecialize l::TypeDomainInteger), (@nospecialize r::BigInt)) + TypeDomainIntegers.BaseHelpers.apply_t_n($func, l, r) + end + end +end + # unary ops (-)(x::BigInt) = MPZ.neg(x) (~)(x::BigInt) = MPZ.com(x) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 8d46fcffa3ad5..38381d6ee2664 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -299,6 +299,7 @@ end # ^ for any x supporting * to_power_type(x) = convert(Base._return_type(*, Tuple{typeof(x), typeof(x)}), x) +to_power_type(@nospecialize x::TypeDomainInteger) = x @noinline throw_domerr_powbysq(::Any, p) = throw(DomainError(p, LazyString( "Cannot raise an integer x to a negative power ", p, ".", "\nConvert input to float."))) @@ -1110,6 +1111,20 @@ function isqrt(x::Union{Int64,UInt64,Int128,UInt128}) s*s > x ? s-1 : s end +function isqrt(@nospecialize n::Union{ + typeof(NonnegativeInteger(0)), + typeof(NonnegativeInteger(1)), + typeof(NonnegativeInteger(2)), + typeof(NonnegativeInteger(3)), +}) + z = zero(NonnegativeInteger) + if n isa PositiveIntegerUpperBound + natural_successor(z) + else + z + end +end + """ factorial(n::Integer) @@ -1145,6 +1160,19 @@ function factorial(n::Integer) return f end +function factorial(@nospecialize n::Union{ + typeof(NonnegativeInteger(0)), + typeof(NonnegativeInteger(1)), + typeof(NonnegativeInteger(2)), +}) + two = NonnegativeInteger(2) + if n === two + two + else + one(NonnegativeInteger) + end +end + """ binomial(n::Integer, k::Integer) diff --git a/base/irrationals.jl b/base/irrationals.jl index eafe388162353..cafb356bd1f5c 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -109,6 +109,17 @@ end <=(x::AbstractIrrational, y::AbstractFloat) = x < y <=(x::AbstractFloat, y::AbstractIrrational) = x < y +for func ∈ (:+, :-, :*, :(==)) + @eval begin + function Base.$func((@nospecialize l::Irrational), (@nospecialize r::TypeDomainInteger)) + TypeDomainIntegers.BaseHelpers.apply_n_t($func, l, r) + end + function Base.$func((@nospecialize l::TypeDomainInteger), (@nospecialize r::Irrational)) + TypeDomainIntegers.BaseHelpers.apply_t_n($func, l, r) + end + end +end + # Irrational vs Rational @assume_effects :total function rationalize(::Type{T}, x::AbstractIrrational; tol::Real=0) where T return rationalize(T, big(x), tol=tol) @@ -151,13 +162,20 @@ hash(x::Irrational, h::UInt) = 3*objectid(x) - h widen(::Type{T}) where {T<:Irrational} = T -zero(::AbstractIrrational) = false -zero(::Type{<:AbstractIrrational}) = false +zero(::AbstractIrrational) = zero(TypeDomainInteger) +zero(::Type{<:AbstractIrrational}) = zero(TypeDomainInteger) -one(::AbstractIrrational) = true -one(::Type{<:AbstractIrrational}) = true +one(::AbstractIrrational) = one(TypeDomainInteger) +one(::Type{<:AbstractIrrational}) = one(TypeDomainInteger) -sign(x::AbstractIrrational) = ifelse(x < zero(x), -1.0, 1.0) +function sign(x::AbstractIrrational) + o = one(TypeDomainInteger) + if signbit(x) + -o + else + o + end +end -(x::AbstractIrrational) = -Float64(x) for op in Symbol[:+, :-, :*, :/, :^] diff --git a/base/math.jl b/base/math.jl index da51ab3a17bd0..3b8c8c454f6d3 100644 --- a/base/math.jl +++ b/base/math.jl @@ -29,6 +29,8 @@ using Core.Intrinsics: sqrt_llvm using .Base: IEEEFloat +using ..TypeDomainIntegers + @noinline function throw_complex_domainerror(f::Symbol, x) throw(DomainError(x, LazyString(f," was called with a negative real argument but will only return a complex result if called with a complex argument. Try ", f,"(Complex(x))."))) @@ -262,6 +264,13 @@ deg2rad(z::Real) = deg2rad(float(z)) rad2deg(z::Number) = (z/pi)*180 deg2rad(z::Number) = (z*pi)/180 +function deg2rad(@nospecialize z::typeof(zero(TypeDomainInteger))) + z +end +function rad2deg(@nospecialize z::typeof(zero(TypeDomainInteger))) + z +end + log(b::T, x::T) where {T<:Number} = log(x)/log(b) """ @@ -679,6 +688,16 @@ Return the fourth root of `x` by applying `sqrt` twice successively. """ fourthroot(x::Number) = sqrt(sqrt(x)) +function sqrt(@nospecialize n::Union{typeof(NonnegativeInteger(0)),typeof(NonnegativeInteger(1))}) + n +end +function cbrt(@nospecialize n::Union{typeof(NonnegativeInteger(0)),typeof(NonnegativeInteger(1))}) + n +end +function fourthroot(@nospecialize n::Union{typeof(NonnegativeInteger(0)),typeof(NonnegativeInteger(1))}) + n +end + """ hypot(x, y) @@ -823,6 +842,8 @@ function _hypot(x::NTuple{N,<:IEEEFloat}) where {N} return scale * sqrt(mapreduce(y -> abs2(y * invscale), add_fast, x)) end +hypot(x::TypeDomainInteger) = abs(x) + atan(y::Real, x::Real) = atan(promote(float(y),float(x))...) atan(y::T, x::T) where {T<:AbstractFloat} = Base.no_op_err("atan", T) @@ -1594,4 +1615,74 @@ exp2(x::AbstractFloat) = 2^x exp10(x::AbstractFloat) = 10^x fourthroot(::Missing) = missing +function exp2(@nospecialize n::Union{ + typeof(NonnegativeInteger(0)), + typeof(NonnegativeInteger(1)), +}) + natural_successor(n) +end +function exp10(n::typeof(NonnegativeInteger(0))) + natural_successor(n) +end +function expm1(n::typeof(NonnegativeInteger(0))) + n +end + +function sinh(n::typeof(NonnegativeInteger(0))) + n +end +function cosh(n::typeof(NonnegativeInteger(0))) + natural_successor(n) +end +function tanh(n::typeof(NonnegativeInteger(0))) + n +end +function sech(n::typeof(NonnegativeInteger(0))) + natural_successor(n) +end +function asinh(n::typeof(NonnegativeInteger(0))) + n +end +function acosh(n::typeof(NonnegativeInteger(1))) + natural_predecessor(n) +end +function atanh(n::typeof(NonnegativeInteger(0))) + n +end +function asech(n::typeof(NonnegativeInteger(1))) + natural_predecessor(n) +end + +function log2(@nospecialize n::Union{typeof(NonnegativeInteger(1)),typeof(NonnegativeInteger(2))}) + natural_predecessor(n) +end +function log10(@nospecialize n::typeof(NonnegativeInteger(1))) + zero(NonnegativeInteger) +end +function log(@nospecialize n::typeof(NonnegativeInteger(1))) + zero(NonnegativeInteger) +end +function log1p(@nospecialize n::typeof(NonnegativeInteger(0))) + zero(NonnegativeInteger) +end + +function sind(n::typeof(NonnegativeInteger(0))) + n +end +function asind(n::typeof(NonnegativeInteger(0))) + n +end +function cosd(n::typeof(NonnegativeInteger(0))) + natural_successor(n) +end +function acosd(n::typeof(NonnegativeInteger(1))) + natural_predecessor(n) +end +function secd(n::typeof(NonnegativeInteger(0))) + natural_successor(n) +end +function asecd(n::typeof(NonnegativeInteger(1))) + natural_predecessor(n) +end + end # module diff --git a/base/mathconstants.jl b/base/mathconstants.jl index 4bb8c409acf00..e9291530689b3 100644 --- a/base/mathconstants.jl +++ b/base/mathconstants.jl @@ -8,6 +8,8 @@ See [`π`](@ref), [`ℯ`](@ref), [`γ`](@ref), [`φ`](@ref) and [`catalan`](@ref """ module MathConstants +using ..TypeDomainIntegers + export π, pi, ℯ, e, γ, eulergamma, catalan, φ, golden Base.@irrational π pi @@ -120,13 +122,13 @@ for T in (AbstractIrrational, Rational, Integer, Number, Complex) end Base.literal_pow(::typeof(^), ::Irrational{:ℯ}, ::Val{p}) where {p} = exp(p) -Base.log(::Irrational{:ℯ}) = 1 # use 1 to correctly promote expressions like log(x)/log(ℯ) +Base.log(::Irrational{:ℯ}) = one(TypeDomainInteger) Base.log(::Irrational{:ℯ}, x::Number) = log(x) -Base.sin(::Irrational{:π}) = 0.0 -Base.cos(::Irrational{:π}) = -1.0 -Base.sincos(::Irrational{:π}) = (0.0, -1.0) -Base.tan(::Irrational{:π}) = 0.0 +Base.sin(::Irrational{:π}) = zero(TypeDomainInteger) +Base.cos(::Irrational{:π}) = -one(TypeDomainInteger) +Base.sincos(::Irrational{:π}) = (zero(TypeDomainInteger), -one(TypeDomainInteger)) +Base.tan(::Irrational{:π}) = zero(TypeDomainInteger) Base.cot(::Irrational{:π}) = -1/0 end # module diff --git a/base/mpfr.jl b/base/mpfr.jl index d393469aa26a1..50b257cb5576d 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -22,7 +22,7 @@ import RawBigIntRoundingIncrementHelper, truncated, RawBigInt -using .Base.Libc +using .Base.Libc, ..TypeDomainIntegers import ..Rounding: rounding_raw, setrounding_raw, rounds_to_nearest, rounds_away_from_zero, tie_breaker_is_to_even, correct_rounding_requires_increment @@ -402,6 +402,22 @@ function Bool(x::BigFloat) isone(x) && return true throw(InexactError(:Bool, Bool, x)) end +function convert(::Type{NonnegativeInteger}, x::BigFloat) + isinteger(x) || throw(InexactError(:NonnegativeInteger, NonnegativeInteger, x)) + TypeDomainIntegers.Conversion.tdnn_from_x(x) +end +function convert(::Type{TypeDomainInteger}, x::BigFloat) + isinteger(x) || throw(InexactError(:TypeDomainInteger, TypeDomainInteger, x)) + TypeDomainIntegers.Conversion.tdi_from_x(x) +end +function NonnegativeInteger(x::BigFloat) + isinteger(x) || throw(InexactError(:NonnegativeInteger, NonnegativeInteger, x)) + TypeDomainIntegers.Conversion.tdnn_from_x(x) +end +function TypeDomainInteger(x::BigFloat) + isinteger(x) || throw(InexactError(:TypeDomainInteger, TypeDomainInteger, x)) + TypeDomainIntegers.Conversion.tdi_from_x(x) +end function BigInt(x::BigFloat) isinteger(x) || throw(InexactError(:BigInt, BigInt, x)) trunc(BigInt, x) @@ -951,6 +967,17 @@ cmp(x::CdoubleMax, y::BigFloat) = -cmp(y,x) <=(x::BigFloat, y::CdoubleMax) = !isnan(x) && !isnan(y) && cmp(x,y) <= 0 <=(x::CdoubleMax, y::BigFloat) = !isnan(x) && !isnan(y) && cmp(y,x) >= 0 +for func ∈ (:+, :-, :*, :(==)) + @eval begin + function Base.$func((@nospecialize l::BigFloat), (@nospecialize r::TypeDomainInteger)) + TypeDomainIntegers.BaseHelpers.apply_n_t($func, l, r) + end + function Base.$func((@nospecialize l::TypeDomainInteger), (@nospecialize r::BigFloat)) + TypeDomainIntegers.BaseHelpers.apply_t_n($func, l, r) + end + end +end + # Note: this inlines the implementation of `mpfr_signbit` to avoid a # `ccall`. signbit(x::BigFloat) = signbit(x.sign) diff --git a/base/rational.jl b/base/rational.jl index fb1824acb6b31..d6525094b37ee 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -310,7 +310,7 @@ julia> denominator(4) 1 ``` """ -denominator(x::Integer) = one(x) +denominator(@nospecialize x::Integer) = one(TypeDomainInteger) denominator(x::Rational) = x.den sign(x::Rational) = oftype(x, sign(x.num)) diff --git a/base/special/trig.jl b/base/special/trig.jl index 66e4b46d7d489..949da430d4b11 100644 --- a/base/special/trig.jl +++ b/base/special/trig.jl @@ -931,10 +931,18 @@ function tanpi(_x::T) where T<:IEEEFloat return float(T)(si / co) end -sinpi(x::Integer) = x >= 0 ? zero(float(x)) : -zero(float(x)) +function cospi(n::TypeDomainInteger) + o = one(NonnegativeInteger) + if iseven(n) + o + else + -o + end +end + +sinpi(x::Integer) = zero(NonnegativeInteger) cospi(x::Integer) = isodd(x) ? -one(float(x)) : one(float(x)) -tanpi(x::Integer) = x >= 0 ? (isodd(x) ? -zero(float(x)) : zero(float(x))) : - (isodd(x) ? zero(float(x)) : -zero(float(x))) +tanpi(x::Integer) = zero(NonnegativeInteger) sincospi(x::Integer) = (sinpi(x), cospi(x)) sinpi(x::AbstractFloat) = sin(pi*x) cospi(x::AbstractFloat) = cos(pi*x) @@ -1168,6 +1176,22 @@ for (finv, f, finvh, fh, finvd, fd, fn) in ((:sec, :cos, :sech, :cosh, :secd, :c end end +function sinc(@nospecialize n::TypeDomainInteger) + z = zero(TypeDomainInteger) + if iszero(n) + natural_successor(z) + else + z + end +end +function cosc(@nospecialize n::Union{ + typeof(TypeDomainInteger(-1)), + typeof(TypeDomainInteger( 0)), + typeof(TypeDomainInteger( 1)), +}) + -n +end + for (tfa, tfainv, hfa, hfainv, fn) in ((:asec, :acos, :asech, :acosh, "secant"), (:acsc, :asin, :acsch, :asinh, "cosecant"), (:acot, :atan, :acoth, :atanh, "cotangent")) diff --git a/base/typedomainintegers.jl b/base/typedomainintegers.jl new file mode 100644 index 0000000000000..17698ec4ead78 --- /dev/null +++ b/base/typedomainintegers.jl @@ -0,0 +1,744 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +baremodule TypeDomainIntegers + baremodule Basic + using Base: @nospecialize + export + natural_successor, natural_predecessor, + NonnegativeInteger, PositiveInteger, PositiveIntegerUpperBound, + zero + baremodule RecursiveStep + using Base: @nospecialize + export recursive_step + function recursive_step(@nospecialize t::Type) + Union{Nothing,t} + end + end + baremodule UpperBounds + using ..RecursiveStep + const s = Integer + abstract type A{P <: recursive_step(s)} <: s end + abstract type B{P <: recursive_step(A)} <: A{P} end + end + const NonnegativeIntegerUpperBound = UpperBounds.B + using .RecursiveStep + struct NonnegativeIntegerImpl{ + Predecessor<:recursive_step(NonnegativeIntegerUpperBound), + } <: NonnegativeIntegerUpperBound{Predecessor} + predecessor::Predecessor + function stricter(t::UnionAll) + t{P} where {P <: recursive_step(t)} + end + global const NonnegativeIntegerImplTighter = stricter(NonnegativeIntegerImpl) + global const NonnegativeInteger = stricter(NonnegativeIntegerImplTighter) + global const PositiveInteger = let t = NonnegativeIntegerImplTighter + t{P} where {P <: t} + end + global const PositiveIntegerUpperBound = let t = UpperBounds.A + t{P} where {P <: t} + end + global function new_nonnegative_integer(p::P) where {P<:recursive_step(NonnegativeInteger)} + t_p = P::DataType + r = new{t_p}(p) + r::NonnegativeInteger + end + end + function natural_successor(o::NonnegativeInteger) + new_nonnegative_integer(o)::PositiveInteger + end + function natural_predecessor(o::PositiveInteger) + r = o.predecessor + r::NonnegativeInteger + end + function zero() + new_nonnegative_integer(nothing) + end + end + + baremodule LazyMinus + using ..Basic + using Base: @nospecialize + export NegativeInteger, TypeDomainInteger, negated, absolute_value_of + struct NegativeInteger{X<:PositiveInteger} <: Integer + x::X + global function new_negative_integer(x::X) where {X<:PositiveInteger} + new{X}(x) + end + end + const TypeDomainInteger = Union{NonnegativeInteger,NegativeInteger} + function negated(@nospecialize n::TypeDomainInteger) + if n isa NegativeInteger + n.x + else + n = n::NonnegativeInteger + if n isa PositiveIntegerUpperBound + new_negative_integer(n) + else + n + end + end + end + function absolute_value_of(@nospecialize n::TypeDomainInteger) + if n isa NegativeInteger + n.x + else + n::NonnegativeInteger + end + end + end + + baremodule PrimitiveTypes + using Base: unsigned, map + const types_signed = (Int8, Int16, Int32, Int64, Int128) + const types_unsigned = (UInt8, UInt16, UInt32, UInt64, UInt128) + const types_int_without_bool = (types_signed..., types_unsigned...) + const types_int_with_bool = (Bool, types_int_without_bool...) + const types_float = (Float16, Float32, Float64) + const types_all = (types_int_with_bool..., types_float...) + function union_of_types(t::Tuple{Vararg{DataType}}) + Union{t...,}::Type + end + const type_type = Type{<:Type} + function type(t::Type) + Type{t}::type_type + end + function type_union_of_types(t::Tuple{Vararg{Type}}) + s = map(type, t) + union_of_types(s)::type_type + end + const TypesSigned = union_of_types(types_signed) + const TypesSignedType = type_union_of_types(types_signed) + const TypesAll = union_of_types(types_all) + const TypesAllType = type_union_of_types(types_all) + end + + baremodule RecursiveAlgorithms + using ..Basic, ..LazyMinus, ..PrimitiveTypes + using Base: Base, signbit, typemax, !, +, -, <, @assume_effects, @inline, @nospecialize + export subtracted, added, to_narrowest_signed_int, from_abs_int, is_even, multiplied, is_less + @assume_effects :foldable function is_less((@nospecialize l::NonnegativeInteger), @nospecialize r::NonnegativeInteger) + if r isa PositiveIntegerUpperBound + if l isa PositiveIntegerUpperBound + let pl = natural_predecessor(l), pr = natural_predecessor(r), res = @inline is_less(pl, pr) + res::Bool + end + else + true + end + else + false + end + end + @assume_effects :foldable function subtracted((@nospecialize l::NonnegativeInteger), @nospecialize r::NonnegativeInteger) + l_pos = l isa PositiveIntegerUpperBound + if r isa PositiveIntegerUpperBound + if l_pos + let a = natural_predecessor(l), b = natural_predecessor(r), ret = @inline subtracted(a, b) + ret::TypeDomainInteger + end + else + negated(r) + end + else + if l_pos + l::PositiveInteger + else + zero() + end + end + end + @assume_effects :foldable function added((@nospecialize l::NonnegativeInteger), @nospecialize r::NonnegativeInteger) + ret = if r isa PositiveIntegerUpperBound + let a = natural_successor(l), b = natural_predecessor(r) + @inline added(a, b) + end + else + l + end + ret::NonnegativeInteger + end + const PSigned = PrimitiveTypes.TypesSigned + @assume_effects :foldable function widening_increment(n::PSigned) + if n < typemax(n) + n + true + else + Base.widen(n) + true + end::PSigned + end + @assume_effects :foldable function abs_decrement(n::PSigned) + if signbit(n) + n + true + else + n - true + end::PSigned + end + @assume_effects :foldable function to_narrowest_signed_int(@nospecialize o::NonnegativeInteger) + if o isa PositiveIntegerUpperBound + let p = natural_predecessor(o), t = @inline to_narrowest_signed_int(p) + widening_increment(t) + end + else + Int8(0) + end + end + struct ConvertNaturalToNegativeException <: Exception end + @assume_effects :foldable function from_abs_int(n::PSigned) + ret = if Base.iszero(n) + zero() + else + let v = abs_decrement(n), p = @inline from_abs_int(v) + p = p::NonnegativeInteger + natural_successor(p) + end + end + ret::NonnegativeInteger + end + @assume_effects :foldable function is_even(@nospecialize o::NonnegativeInteger) + if o isa PositiveIntegerUpperBound + let p = natural_predecessor(o) + if p isa PositiveIntegerUpperBound + let s = natural_predecessor(p), r = @inline is_even(s) + r::Bool + end + else + false + end + end + else + true + end + end + @assume_effects :foldable function multiplied((@nospecialize l::NonnegativeInteger), @nospecialize r::NonnegativeInteger) + if r isa PositiveIntegerUpperBound + let p = natural_predecessor(r), x = @inline multiplied(l, p) + added(x, l) + end + else + zero() + end + end + end + + baremodule Conversion + using Base: map, signbit, -, @nospecialize + using ..Basic, ..LazyMinus, ..RecursiveAlgorithms, ..PrimitiveTypes + using ..RecursiveAlgorithms: ConvertNaturalToNegativeException + export + tdnn_to_int, tdi_to_int, tdnn_from_int, tdi_from_int, + tdi_to_x, tdnn_from_x, tdi_from_x, + tdi_to_x_with_extra + function tdnn_to_int(@nospecialize n::NonnegativeInteger) + if n isa PositiveIntegerUpperBound + let p = natural_predecessor(n) + if p isa PositiveIntegerUpperBound + to_narrowest_signed_int(n) + else + true + end + end + else + false + end + end + function tdi_to_int(@nospecialize n::TypeDomainInteger) + if n isa NegativeInteger + let m = negated(n)::PositiveInteger + -to_narrowest_signed_int(m) + end + else + tdnn_to_int(n) + end + end + const PSigned = PrimitiveTypes.TypesSigned + const TNumber = Type{<:Number} + function tdnn_from_int(i::PSigned) + if signbit(i) + throw(ConvertNaturalToNegativeException()) + end + from_abs_int(i) + end + function tdi_from_int(i::PSigned) + n = from_abs_int(i) + if signbit(i) + negated(n)::NegativeInteger + else + n + end + end + function tdnn_from_int(i::Bool) + z = zero() + if i + natural_successor(z) + else + z + end + end + function tdi_from_int(i::Bool) + tdnn_from_int(i) + end + function tdi_to_x(x::TNumber, @nospecialize n::TypeDomainInteger) + i = tdi_to_int(n) + x(i) + end + function tdi_to_x_with_extra(x::TNumber, (@nospecialize n::TypeDomainInteger), e) + i = tdi_to_int(n) + x(i, e) + end + function x_to_int(x::Number) + t = Int16 # presumably wide enough for any type domain integer + t(x)::t + end + function tdnn_from_x(x::Number) + i = x_to_int(x) + tdnn_from_int(i) + end + function tdi_from_x(x::Number) + i = x_to_int(x) + tdi_from_int(i) + end + end + + baremodule BaseOverloadsPromotion + using ..Basic, ..RecursiveAlgorithms, ..LazyMinus, ..PrimitiveTypes + using ..Basic: UpperBounds + using Base: Base, ==, @nospecialize, @eval + struct UnexpectedException <: Exception end + const ZeroOrOne = Union{typeof(zero()),typeof(natural_successor(zero()))} + for type ∈ PrimitiveTypes.types_all + @eval function Base.promote_rule( + (@nospecialize tdt::Type{<:TypeDomainInteger}), + ::Type{$type}, + ) + if tdt <: Union{} + throw(UnexpectedException()) + end + t = if tdt <: ZeroOrOne + Bool + else + Int16 # presumably wide enough for any type domain integer + end + Base.promote_type(t, $type) + end + end + function Base.promote_rule( + (@nospecialize l::Type{<:TypeDomainInteger}), + (@nospecialize r::Type{<:TypeDomainInteger}), + ) + if (l <: Union{}) || (r <: Union{}) + throw(UnexpectedException()) + end + if l == r + l + elseif (l <: ZeroOrOne) && (r <: ZeroOrOne) + Bool + else + Int16 # presumably wide enough for any type domain integer + end + end + end + + baremodule BaseOverloads + using ..Basic, ..RecursiveAlgorithms, ..LazyMinus, ..PrimitiveTypes, ..Conversion + using Base: Base, convert, RoundingMode, <, +, -, *, isless, !, @nospecialize, @eval + function Base.zero(@nospecialize unused::Type{<:TypeDomainInteger}) + zero() + end + function Base.zero(@nospecialize unused::TypeDomainInteger) + zero() + end + function Base.iszero(@nospecialize n::TypeDomainInteger) + if n isa NegativeInteger + false + else + n = n::NonnegativeInteger + if n isa PositiveIntegerUpperBound + false + else + true + end + end + end + const PAll = PrimitiveTypes.TypesAll + for type ∈ (AbstractFloat, PrimitiveTypes.types_all...) + @eval begin + function Base.convert(::Type{$type}, @nospecialize n::TypeDomainInteger) + tdi_to_x($type, n) + end + function (::Type{$type})(@nospecialize n::TypeDomainInteger) + tdi_to_x($type, n) + end + end + end + for type ∈ (AbstractFloat, PrimitiveTypes.types_float...) + @eval begin + function (::Type{$type})((@nospecialize n::TypeDomainInteger), rm::RoundingMode) + tdi_to_x_with_extra($type, n, rm) + end + end + end + function Base.convert(::Type{NonnegativeInteger}, x::PAll) + tdnn_from_x(x) + end + function Base.convert(::Type{TypeDomainInteger}, x::PAll) + tdi_from_x(x) + end + function NonnegativeInteger(x::PAll) + tdnn_from_x(x) + end + function TypeDomainInteger(x::PAll) + tdi_from_x(x) + end + function Base.:(-)((@nospecialize l::TypeDomainInteger), @nospecialize r::TypeDomainInteger) + n = negated(r) + l + n + end + function Base.:(+)((@nospecialize l::TypeDomainInteger), @nospecialize r::TypeDomainInteger) + l_neg = l isa NegativeInteger + if r isa NegativeInteger + if l_neg + l = l::NegativeInteger + let a = negated(l)::PositiveInteger, b = negated(r)::PositiveInteger, s = added(a, b) + negated(s) + end + else + l = l::NonnegativeInteger + let b = negated(r)::PositiveInteger + subtracted(l, b) + end + end + else + r = r::NonnegativeInteger + if l_neg + l = l::NegativeInteger + let a = negated(l)::PositiveInteger + subtracted(r, a) + end + else + l = l::NonnegativeInteger + added(l, r) + end + end + end + function Base.propertynames( + (@nospecialize unused::Union{Basic.NonnegativeIntegerImpl,NegativeInteger}), + ::Bool = false, + ) + () + end + function Base.iseven(@nospecialize o::TypeDomainInteger) + if o isa NegativeInteger + is_even(negated(o)) + else + o = o::NonnegativeInteger + is_even(o) + end + end + function Base.isodd(@nospecialize o::TypeDomainInteger) + !Base.iseven(o) + end + function Base.:(<)((@nospecialize l::TypeDomainInteger), @nospecialize r::TypeDomainInteger) + l_neg = l isa NegativeInteger + if r isa NegativeInteger + if l_neg + l = l::NegativeInteger + let a = negated(l)::PositiveInteger, b = negated(r)::PositiveInteger + is_less(b, a) + end + else + false + end + else + r = r::NonnegativeInteger + if l_neg + true + else + l = l::NonnegativeInteger + is_less(l, r) + end + end + end + function Base.isless((@nospecialize l::TypeDomainInteger), @nospecialize r::TypeDomainInteger) + l < r + end + function Base.one(@nospecialize unused::Type{<:TypeDomainInteger}) + natural_successor(zero()) + end + function Base.one(@nospecialize unused::TypeDomainInteger) + natural_successor(zero()) + end + function Base.isone(@nospecialize n::TypeDomainInteger) + if n isa NegativeInteger + false + else + n = n::NonnegativeInteger + if n isa PositiveIntegerUpperBound + let p = natural_predecessor(n) + if p isa PositiveIntegerUpperBound + false + else + true + end + end + else + false + end + end + end + function Base.:(*)((@nospecialize l::TypeDomainInteger), @nospecialize r::TypeDomainInteger) + l_neg = l isa NegativeInteger + if r isa NegativeInteger + if l_neg + l = l::NegativeInteger + let a = negated(l)::PositiveInteger, b = negated(r)::PositiveInteger + multiplied(a, b)::PositiveInteger + end + else + l = l::NonnegativeInteger + let b = negated(r)::PositiveInteger, m = multiplied(l, b) + negated(m) + end + end + else + r = r::NonnegativeInteger + if l_neg + l = l::NegativeInteger + let a = negated(l)::PositiveInteger, m = multiplied(a, r) + negated(m) + end + else + l = l::NonnegativeInteger + multiplied(l, r) + end + end + end + function Base.:(-)(@nospecialize n::TypeDomainInteger) + negated(n) + end + function Base.abs(@nospecialize n::TypeDomainInteger) + absolute_value_of(n) + end + function Base.signbit(@nospecialize n::TypeDomainInteger) + n isa NegativeInteger + end + function Base.sign(@nospecialize n::TypeDomainInteger) + zer = zero() + plus = natural_successor(zer) + minus = negated(plus) + if n isa NegativeInteger + minus + else + n = n::NonnegativeInteger + if n isa PositiveIntegerUpperBound + plus + else + zer + end + end + end + function Base.inv(@nospecialize n::Union{ + typeof(TypeDomainInteger(-1)), + typeof(TypeDomainInteger( 1)), + }) + n + end + function Base.widen(@nospecialize n::TypeDomainInteger) + n + end + function Base.widen(@nospecialize t::Type{<:TypeDomainInteger}) + t + end + function Base.show(io::Base.IO, n::TypeDomainInteger) + i = tdi_to_int(n) + Base.show(io, Int(i)) + end + end + + baremodule BaseHelpers + using ..Basic, ..LazyMinus, ..Conversion + using Base: convert, isequal, iszero, <, +, -, *, ==, !, @nospecialize + export apply_n_t, apply_t_n + function apply_n_t(::typeof(+), (@nospecialize l::Number), @nospecialize r::TypeDomainInteger) + if r isa NegativeInteger + let pos = negated(r)::PositiveInteger + l + -tdi_to_int(pos) + end + else + r = r::NonnegativeInteger + if r isa PositiveIntegerUpperBound + l + tdi_to_int(r) + else + l + end + end + end + function apply_t_n(::typeof(+), (@nospecialize l::TypeDomainInteger), @nospecialize r::Number) + # addition is commutative + apply_n_t(+, r, l) + end + function apply_n_t(::typeof(-), (@nospecialize l::Number), @nospecialize r::TypeDomainInteger) + apply_n_t(+, l, negated(r)) + end + function apply_t_n(::typeof(-), (@nospecialize l::TypeDomainInteger), @nospecialize r::Number) + if l isa NegativeInteger + tdi_to_int(l) - r + else + l = l::NonnegativeInteger + if l isa PositiveIntegerUpperBound + tdi_to_int(l) - r + else + -r + end + end + end + function apply_n_t(::typeof(*), (@nospecialize l::Number), @nospecialize r::TypeDomainInteger) + if r isa NegativeInteger + let pos = negated(r), posm1 = natural_predecessor(pos) + if posm1 isa PositiveIntegerUpperBound + l * tdi_to_int(r) + else + -l + end + end + else + r = r::NonnegativeInteger + if r isa PositiveIntegerUpperBound + let p = natural_predecessor(r) + if p isa PositiveIntegerUpperBound + l * tdi_to_int(r) + else + l + end + end + else + zero() + end + end + end + function apply_t_n(::typeof(*), (@nospecialize l::TypeDomainInteger), @nospecialize r::Number) + if l isa NegativeInteger + let pos = negated(l), posm1 = natural_predecessor(pos) + if posm1 isa PositiveIntegerUpperBound + tdi_to_int(l) * r + else + -r + end + end + else + l = l::NonnegativeInteger + if l isa PositiveIntegerUpperBound + let p = natural_predecessor(l) + if p isa PositiveIntegerUpperBound + tdi_to_int(l) * r + else + r + end + end + else + zero() + end + end + end + function apply_n_t( + func::Union{typeof(isequal),typeof(==)}, + (@nospecialize l::Number), + (@nospecialize r::TypeDomainInteger), + ) + if r isa NegativeInteger + func(l, tdi_to_int(r)) + else + r = r::NonnegativeInteger + if r isa PositiveIntegerUpperBound + func(l, tdi_to_int(r)) + else + iszero(l) + end + end + end + function apply_t_n( + func::Union{typeof(isequal),typeof(==)}, + (@nospecialize l::TypeDomainInteger), + (@nospecialize r::Number), + ) + # equality is commutative + apply_n_t(func, r, l) + end + end + + baremodule BaseOverloadsBinaryOperations + using Base: Base, +, -, *, ==, @nospecialize, @eval + using ..Basic, ..LazyMinus, ..PrimitiveTypes, ..BaseHelpers + for type ∈ PrimitiveTypes.types_all, func ∈ (:+, :-, :*, :(==)) + @eval begin + function Base.$func((@nospecialize l::$type), (@nospecialize r::TypeDomainInteger)) + apply_n_t($func, l, r) + end + function Base.$func((@nospecialize l::TypeDomainInteger), (@nospecialize r::$type)) + apply_t_n($func, l, r) + end + end + end + end + + export + natural_successor, natural_predecessor, NonnegativeInteger, PositiveInteger, + PositiveIntegerUpperBound, NegativeInteger, TypeDomainInteger + + """ + natural_successor(::NonnegativeInteger) + + Return the successor of a natural number. + """ + const natural_successor = Basic.natural_successor + + """ + natural_predecessor(::PositiveInteger) + + Return the predecessor of a nonzero natural number. + """ + const natural_predecessor = Basic.natural_predecessor + + """ + NonnegativeInteger + + Nonnegative integers in the type domain. + + The implementation is basically the unary numeral system. Especially inspired by + the Peano axioms/Zermelo construction of the natural numbers. + """ + const NonnegativeInteger = Basic.NonnegativeInteger + + """ + PositiveInteger + + Positive integers in the type domain. Subtypes [`NonnegativeInteger`](@ref). + """ + const PositiveInteger = Basic.PositiveInteger + + """ + PositiveIntegerUpperBound + + Positive integers in the type domain. Supertypes [`PositiveInteger`](@ref). + """ + const PositiveIntegerUpperBound = Basic.PositiveIntegerUpperBound + + """ + ConvertNaturalToNegativeException + + Thrown when a conversion of a negative integer to a natural number is attempted. + """ + const ConvertNaturalToNegativeException = RecursiveAlgorithms.ConvertNaturalToNegativeException + + """ + NegativeInteger + + Negative integers in the type domain. + """ + const NegativeInteger = LazyMinus.NegativeInteger + + """ + TypeDomainInteger + + Integers in the type domain. + """ + const TypeDomainInteger = LazyMinus.TypeDomainInteger +end diff --git a/base/typedomainintegers_irrationals.jl b/base/typedomainintegers_irrationals.jl new file mode 100644 index 0000000000000..8ea88e84b71d5 --- /dev/null +++ b/base/typedomainintegers_irrationals.jl @@ -0,0 +1,75 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +function angle(@nospecialize n::TypeDomainInteger) + if n isa NegativeInteger + π + else + zero(TypeDomainInteger) + end +end + +function exp(@nospecialize n::Union{ + typeof(NonnegativeInteger(0)), + typeof(NonnegativeInteger(1)), +}) + if n isa PositiveIntegerUpperBound + ℯ + else + NonnegativeInteger(1) + end +end + +const ZeroOrPi = Union{ + typeof(NonnegativeInteger(0)), + typeof(π), +} + +function sin(@nospecialize x::ZeroOrPi) + NonnegativeInteger(0) +end +function tan(@nospecialize x::ZeroOrPi) + NonnegativeInteger(0) +end +function cos(@nospecialize x::ZeroOrPi) + o = one(NonnegativeInteger) + if iszero(x) + o + else + -o + end +end +function Math.sec(@nospecialize x::ZeroOrPi) + o = one(NonnegativeInteger) + if iszero(x) + o + else + -o + end +end + +function asin(x::typeof(NonnegativeInteger(0))) + x +end +function atan(x::typeof(NonnegativeInteger(0))) + x +end +function acos(@nospecialize x::Union{ + typeof(TypeDomainInteger(-1)), + typeof(TypeDomainInteger( 1)), +}) + if x isa NegativeInteger + π + else + zero(NonnegativeInteger) + end +end +function Math.asec(@nospecialize x::Union{ + typeof(TypeDomainInteger(-1)), + typeof(TypeDomainInteger( 1)), +}) + if x isa NegativeInteger + π + else + zero(NonnegativeInteger) + end +end diff --git a/stdlib/InteractiveUtils/src/InteractiveUtils.jl b/stdlib/InteractiveUtils/src/InteractiveUtils.jl index 835988ddf149f..7c11fbd91390f 100644 --- a/stdlib/InteractiveUtils/src/InteractiveUtils.jl +++ b/stdlib/InteractiveUtils/src/InteractiveUtils.jl @@ -290,12 +290,13 @@ are included, including those not visible in the current module. See also [`supertype`](@ref), [`supertypes`](@ref), [`methodswith`](@ref). # Examples -```jldoctest -julia> subtypes(Integer) -3-element Vector{Any}: - Bool - Signed - Unsigned +```julia +julia> subtypes(Real) +4-element Vector{Any}: + AbstractFloat + AbstractIrrational + Integer + Rational ``` """ subtypes(x::Type) = _subtypes_in!(Base.loaded_modules_array(), x) diff --git a/test/choosetests.jl b/test/choosetests.jl index 96d230d185c71..7c9b6d3362119 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -16,7 +16,7 @@ const TESTNAMES = [ "operators", "ordering", "path", "ccall", "parse", "loading", "gmp", "sorting", "spawn", "backtrace", "exceptions", "file", "read", "version", "namedtuple", - "mpfr", "broadcast", "complex", + "mpfr", "broadcast", "complex", "typedomainintegers", "floatapprox", "stdlib", "reflection", "regex", "float16", "combinatorics", "sysinfo", "env", "rounding", "ranges", "mod2pi", "euler", "show", "client", "terminfo", diff --git a/test/math.jl b/test/math.jl index c0a2d8bf8c9f8..5e26a83e9e48e 100644 --- a/test/math.jl +++ b/test/math.jl @@ -83,11 +83,11 @@ end @test repr(Any[pi ℯ; ℯ pi]) == "Any[π ℯ; ℯ π]" @test string(pi) == "π" - @test sin(π) == sind(180) === sinpi(1) === sinpi(1//1) == tan(π) == 0 - @test tan(π) == tand(180) === tanpi(1) === tanpi(1//1) === -0.0 - @test cos(π) == cosd(180) === cospi(1) === cospi(1//1) == sec(π) == -1 + @test sin(π) == sind(180) == sinpi(1) == sinpi(1//1) == tan(π) == 0 + @test tan(π) == tand(180) == tanpi(1) == tanpi(1//1) == -0.0 + @test cos(π) == cosd(180) == cospi(1) == cospi(1//1) == sec(π) == -1 @test csc(π) == 1/0 && cot(π) == -1/0 - @test sincos(π) === sincospi(1) == (0, -1) + @test sincos(π) == sincospi(1) == (0, -1) end @testset "frexp,ldexp,significand,exponent" begin @@ -572,16 +572,16 @@ end @testset "Integer and Inf args for sinpi/cospi/tanpi/sinc/cosc" begin for (sinpi, cospi) in ((sinpi, cospi), (x->sincospi(x)[1], x->sincospi(x)[2])) - @test sinpi(1) === 0.0 - @test sinpi(-1) === -0.0 + @test sinpi(1) == 0.0 + @test sinpi(-1) == -0.0 @test cospi(1) == -1 @test cospi(2) == 1 end - @test tanpi(1) === -0.0 - @test tanpi(-1) === 0.0 - @test tanpi(2) === 0.0 - @test tanpi(-2) === -0.0 + @test tanpi(1) == -0.0 + @test tanpi(-1) == 0.0 + @test tanpi(2) == 0.0 + @test tanpi(-2) == -0.0 @test sinc(1) == 0 @test sinc(complex(1,0)) == 0 @test sinc(0) == 1 @@ -878,7 +878,7 @@ end @test exp10(5) ≈ exp10(5.0) @test exp10(50//10) ≈ exp10(5.0) @test log10(exp10(ℯ)) ≈ ℯ - @test log(ℯ) === 1 + @test log(ℯ) == 1 @test exp2(Float16(2.0)) ≈ exp2(2.0) @test exp2(Float16(1.0)) === Float16(exp2(1.0)) @test exp10(Float16(1.0)) === Float16(exp10(1.0)) diff --git a/test/numbers.jl b/test/numbers.jl index 34e775f9b2eea..d5ded303176b9 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -524,8 +524,8 @@ end @test isa(sign(2//3), Rational{Int}) @test isa(2//3 + 2//3im, Complex{Rational{Int}}) @test isa(sign(2//3 + 2//3im), ComplexF64) - @test sign(pi) === 1.0 - @test sign(pi) === -sign(-pi) + @test sign(pi) == 1.0 + @test sign(pi) == -sign(-pi) @test sign(one(UInt)) == 1 @test sign(zero(UInt)) == 0 @@ -1115,10 +1115,13 @@ end @testset "Irrational zero and one" begin for i in (π, ℯ, γ, catalan) - @test one(i) === true - @test zero(i) === false - @test one(typeof(i)) === true - @test zero(typeof(i)) === false + @test one(i) == true + @test zero(i) == false + @test one(typeof(i)) == true + @test zero(typeof(i)) == false + @testset "JuliaLang/julia/issues/37977" begin + @test i === one(i)*i + end end end diff --git a/test/typedomainintegers.jl b/test/typedomainintegers.jl new file mode 100644 index 0000000000000..2201e86eb3f61 --- /dev/null +++ b/test/typedomainintegers.jl @@ -0,0 +1,259 @@ +using Base.TypeDomainIntegers +using Test + +const max_tested_number = 7 + +@testset "TypeDomainNaturalNumbers.jl" begin + @testset "subtyping" begin + @test PositiveInteger <: NonnegativeInteger <: TypeDomainInteger <: Integer + @test NegativeInteger <: TypeDomainInteger + @test !(NonnegativeInteger <: PositiveInteger) + @test PositiveInteger <: PositiveIntegerUpperBound + @test !(NonnegativeInteger <: PositiveIntegerUpperBound) + @test PositiveInteger == typeintersect(NonnegativeInteger,PositiveIntegerUpperBound) + end + @testset "zero" begin + n = @inferred zero(NonnegativeInteger) + @test @inferred iszero(n) + @test n isa NonnegativeInteger + @test !(n isa PositiveInteger) + @test Base.issingletontype(typeof(n)) + end + @testset "positive integers" begin + n = @inferred zero(NonnegativeInteger) + for _ ∈ 1:max_tested_number + n = @inferred natural_successor(n) + @test !(@inferred iszero(n)) + @test n isa NonnegativeInteger + @test n isa PositiveInteger + @test n isa PositiveIntegerUpperBound + end + end + @testset "successor, predecessor" begin + m = @inferred zero(NonnegativeInteger) + @test_throws MethodError natural_predecessor(m) + n = @inferred natural_successor(m) + for _ ∈ 1:max_tested_number + @test m === @inferred natural_predecessor(n) + @test n === @inferred natural_successor(m) + end + end + @testset "conversion to/from `Bool`" begin + z = @inferred zero(NonnegativeInteger) + o = @inferred natural_successor(z) + t = @inferred natural_successor(o) + @test z === convert(NonnegativeInteger, false) + @test o === convert(NonnegativeInteger, true) + @test false === @inferred convert(Bool, z) + @test true === @inferred convert(Bool, o) + @test_throws InexactError convert(Bool, t) + end + @testset "conversion to/from `Int`" begin + for i ∈ 0:max_tested_number + @test i === convert(Int, convert(NonnegativeInteger, i)) + @test convert(NonnegativeInteger, i) isa NonnegativeInteger + end + @testset "negative" begin + for i ∈ -5:-1 + @test_throws Exception convert(NonnegativeInteger, i) + end + end + end + @testset "identity conversion" begin + for i ∈ -max_tested_number:max_tested_number + n = convert(TypeDomainInteger, i) + @test n === @inferred convert(TypeDomainInteger, n) + end + for i ∈ 0:max_tested_number + n = convert(NonnegativeInteger, i) + @test n === @inferred convert(NonnegativeInteger, n) + end + end + @testset "constructors" begin + @testset "`Bool`" begin + for i ∈ 0:1 + b = Bool(i) + n = convert(NonnegativeInteger, i) + @test b === @inferred Bool(n) + @test n === NonnegativeInteger(b) + end + @testset "failure" begin + t = convert(NonnegativeInteger, 2) + @test_throws InexactError Bool(t) + end + end + @testset "`Int`" begin + for i ∈ 0:max_tested_number + n = convert(NonnegativeInteger, i) + @test i === @inferred Int(n) + @test n === NonnegativeInteger(i) + end + for i ∈ -max_tested_number:max_tested_number + n = convert(TypeDomainInteger, i) + @test i === @inferred Int(n) + @test n === TypeDomainInteger(i) + end + end + end + @testset "instance zero" begin + for i ∈ 0:max_tested_number + @test zero(NonnegativeInteger) === zero(convert(NonnegativeInteger, i)) + end + for i ∈ -max_tested_number:max_tested_number + @test zero(TypeDomainInteger) === zero(convert(TypeDomainInteger, i)) + end + end + @testset "properties" begin + for i ∈ -max_tested_number:max_tested_number + n = convert(TypeDomainInteger, i) + @test () === @inferred propertynames(n) + @test () === @inferred propertynames(n, false) + end + end + @testset "`iseven`, `isodd`" begin + for i ∈ -max_tested_number:max_tested_number + n = convert(TypeDomainInteger, i) + @test iseven(n) === iseven(i) + @test isodd(n) === isodd(i) + end + end + @testset "comparisons" begin + @testset "between type domain integers" begin + for a ∈ -max_tested_number:max_tested_number + l = convert(TypeDomainInteger, a) + for b ∈ -max_tested_number:max_tested_number + r = convert(TypeDomainInteger, b) + for op ∈ (==, <, isequal, isless) + @test op(l, r) isa Bool + @test op(l, r) == op(a, b) + end + end + end + end + @testset "between a type domain integer and a `Number`" begin + z = @inferred zero(NonnegativeInteger) + o = @inferred natural_successor(z) + t = @inferred natural_successor(o) + for i ∈ -max_tested_number:max_tested_number + lesser_numbers = ( + prevfloat(Float64(i)), prevfloat(Float32(i)), prevfloat(Float16(i)), + i - true, Float64(i) - true, Float32(i) - true, Float16(i) - true, + -Inf16, -Inf32, -Inf64, + ) + greater_numbers = ( + nextfloat(Float64(i)), nextfloat(Float32(i)), nextfloat(Float16(i)), + i + true, Float64(i) + true, Float32(i) + true, Float16(i) + true, + Inf16, Inf32, Inf64, + ) + unequal_numbers = (lesser_numbers..., greater_numbers...) + n = convert(TypeDomainInteger, i) + @testset "`==`, `isequal`" begin + for op ∈ (==, isequal) + for x ∈ (i, Float64(i), Float32(i), Float16(i)) + @test op(n, x) + @test op(x, n) + end + for x ∈ unequal_numbers + @test !op(n, x) + @test !op(x, n) + end + end + @testset "`missing`" begin + @test ismissing(n == missing) + @test ismissing(missing == n) + @test !isequal(n, missing) + @test !isequal(missing, n) + end + end + @testset "`<`, `isless`" begin + for op ∈ (<, isless) + for x ∈ (i, Float64(i), Float32(i), Float16(i)) + @test !op(n, x) + @test !op(x, n) + end + for x ∈ greater_numbers + @test op(n, x) + @test !op(x, n) + end + for x ∈ lesser_numbers + @test !op(n, x) + @test op(x, n) + end + end + @testset "`missing`" begin + @test ismissing(n < missing) + @test ismissing(missing < n) + @test isless(n, missing) + @test !isless(missing, n) + end + end + end + end + end + @testset "addition and subtraction" begin + @testset "identity" begin + id = zero(NonnegativeInteger) + for i ∈ -max_tested_number:max_tested_number + n = convert(TypeDomainInteger, i) + @test (@inferred (n + id)) === (id + n) === (n - id) === n + @test (@inferred (n - n)) === id + end + end + @testset "special cases" begin + z = zero(NonnegativeInteger) + o = @inferred natural_successor(z) + t = @inferred natural_successor(o) + @test -o === (z - o) + @test -t === (z - t) + @test -o === (o - t) + @test t === @inferred (o + o) + @test o === @inferred (t - o) + end + @testset "systematic" begin + for a ∈ -max_tested_number:max_tested_number + l = convert(TypeDomainInteger, a) + for b ∈ -max_tested_number:max_tested_number + r = convert(TypeDomainInteger, b) + @test convert(Int, l + r) === (a + b) + end + end + end + end + @testset "multiplication" begin + z = zero(NonnegativeInteger) + o = @inferred natural_successor(z) + t = @inferred natural_successor(o) + @testset "`one`, `isone`" begin + @test o === one(TypeDomainInteger) === one(NonnegativeInteger) === one(z) === one(o) === one(t) + @test !isone(z) + @test isone(o) + @test !isone(t) + end + for a ∈ -max_tested_number:max_tested_number + l = convert(TypeDomainInteger, a) + @test l === (l * o) === (o * l) + for b ∈ -max_tested_number:max_tested_number + r = convert(TypeDomainInteger, b) + @test convert(Int, l * r) === (a * b) + end + end + end + @testset "some identities" begin + z = @inferred zero(NonnegativeInteger) + o = @inferred natural_successor(z) + for c ∈ (π, ℯ, 7, 0x7, 7.0, true) + @test (z + c) === c === (c + z) === (c - z) === (o * c) === (c * o) + end + end + @testset "heterogeneous `+` `-` `*`" begin + for i ∈ -max_tested_number:max_tested_number + n = convert(TypeDomainInteger, i) + for x ∈ (((-1):5)..., ((-3):0.1:3)...) + for op ∈ (+, -, *) + @test op(n, x) == op(i, x) + @test op(x, n) == op(x, i) + end + end + end + end +end