diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..58e6b99 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "julia.environmentPath": "/user/.julia/environments/static-dev" +} \ No newline at end of file diff --git a/src/Static.jl b/src/Static.jl index 184eed7..b319d23 100644 --- a/src/Static.jl +++ b/src/Static.jl @@ -24,7 +24,7 @@ end Base.Symbol(@nospecialize(s::StaticSymbol)) = known(s) -abstract type StaticInteger{N} <: Number end +abstract type StaticInteger{N} <: Integer end """ StaticBool(x::Bool) -> True/False @@ -47,6 +47,8 @@ function StaticBool(x::Bool) return False() end end +#!!!StaticBool(x::Real) = StaticBool(convert(Bool, x)) + """ StaticInt(N::Int) -> StaticInt{N}() @@ -59,6 +61,8 @@ struct StaticInt{N} <: StaticInteger{N} StaticInt(N::Int) = new{N}() StaticInt(@nospecialize N::StaticInt) = N StaticInt(::Val{N}) where {N} = StaticInt(N) + StaticInt(N::UInt) = StaticInt(Int(N)) + #!!!StaticInt(x::Real) = StaticInt(convert(Int, x)) end """ @@ -75,6 +79,43 @@ include("float.jl") const StaticNumber{N} = Union{StaticInt{N}, StaticBool{N}, StaticFloat64{N}} +StaticInt(x::Real) = StaticInt{Int(x)}() +StaticInt(x::Rational) = StaticInt{Int(x)}() # Resolves ambiguities with Base +StaticInt(x::BigFloat) = StaticInt{Int(x)}() # Resolves ambiguities with Base + +StaticInt{X}(y::Real) where X = StaticInt{Int(y)}() +StaticInt{X}(y::Rational) where X = StaticInt{Int(y)}() # Resolves ambiguities with Base +StaticInt{X}(yt::BigFloat) where X = StaticInt{Int(y)}() # Resolves ambiguities with Base + +StaticBool(x::Real) = StaticBool{Bool(x)}() +StaticBool(x::Rational) = StaticBool{Bool(x)}() # Resolves ambiguities with Base +StaticBool(x::BigFloat) = StaticBool{Bool(x)}() # Resolves ambiguities with Base + +True(x::Real) = StaticBool{Bool(x)}() +True(x::Rational) = StaticBool{Bool(x)}() # Resolves ambiguities with Base +True(x::BigFloat) = StaticBool{Bool(x)}() # Resolves ambiguities with Base + +False(x::Real) = StaticBool{Bool(x)}() +False(x::Rational) = StaticBool{Bool(x)}() # Resolves ambiguities with Base +False(x::BigFloat) = StaticBool{Bool(x)}() # Resolves ambiguities with Base + +StaticBool{X}(y::Real) where X = StaticBool{Bool(y)}() +StaticBool{X}(y::Rational) where X = StaticBool{Bool(y)}() # Resolves ambiguities with Base +StaticBool{X}(y::BigFloat) where X = StaticBool{Bool(y)}() # Resolves ambiguities with Base + +StaticFloat64(x::Real) = StaticFloat64{Float64(x)}() +StaticFloat64{X}(y::Real) where X = StaticFloat64{Float64(y)}() + + +function Base.convert(::Type{T}, @nospecialize(N::StaticNumber)) where {T <: Number} + convert(T, known(N)) +end + +#Base.Bool(::StaticInt{N}) where {N} = Bool(N) + +(::Type{T})(@nospecialize x::StaticNumber) where {T <: Union{Base.BitInteger,Float32,Float64}} = T(known(x)) + + Base.getindex(x::Tuple, ::StaticInt{N}) where {N} = getfield(x, N) Base.zero(@nospecialize(::StaticInt)) = StaticInt{0}() @@ -94,8 +135,8 @@ const FloatZero = StaticFloat64{zero(Float64)} const StaticType{T} = Union{StaticNumber{T}, StaticSymbol{T}} -StaticInt(x::False) = Zero() -StaticInt(x::True) = One() +#!!!StaticInt(x::False) = Zero() +#!!!StaticInt(x::True) = One() Base.Bool(::True) = true Base.Bool(::False) = false @@ -222,7 +263,7 @@ static(:x) ``` """ static(@nospecialize(x::Union{StaticSymbol, StaticNumber})) = x -static(x::Integer) = StaticInt(x) +static(x::Integer) = StaticInt{convert(Int, x)}() function static(x::Union{AbstractFloat, Complex, Rational, AbstractIrrational}) StaticFloat64(Float64(x)) end @@ -367,24 +408,27 @@ function Base.promote_rule(@nospecialize(T1::Type{<:StaticNumber}), @nospecialize(T2::Type{<:StaticNumber})) promote_rule(eltype(T1), eltype(T2)) end -function Base.promote_rule(::Type{<:Base.TwicePrecision{R}}, - @nospecialize(T::Type{<:StaticNumber})) where {R <: Number} - promote_rule(Base.TwicePrecision{R}, eltype(T)) -end -function Base.promote_rule(@nospecialize(T1::Type{<:StaticNumber}), - T2::Type{<:Union{Rational, AbstractFloat, Signed}}) - promote_rule(T2, eltype(T1)) -end +Base.promote_rule(@nospecialize(T1::Type{<:StaticNumber}), T2::Type{<:Real}) = promote_type(eltype(T1), T2) + +# Bool needs special handling, Base defines `promote_rule(::Type{Bool}, ::Type{T}) where {T<:Number} = T` +Base.promote_rule(::Type{Bool}, @nospecialize(T::Type{<:StaticNumber})) = eltype(T) +Base.promote_rule(@nospecialize(T::Type{<:StaticNumber}), ::Type{Bool}) = eltype(T) + + +@inline Base.promote(@nospecialize(a::StaticInt), @nospecialize(b::StaticInt)) = (a, b) +@inline Base.promote(@nospecialize(a::StaticBool), @nospecialize(b::StaticBool)) = (a, b) +@inline Base.promote(@nospecialize(a::StaticFloat64), @nospecialize(b::StaticFloat64)) = (a, b) + Base.:(~)(::StaticInteger{N}) where {N} = static(~N) Base.inv(x::StaticInteger{N}) where {N} = one(x) / x -Base.zero(@nospecialize T::Type{<:StaticInt}) = StaticInt(0) +Base.zero(@nospecialize T::Type{<:StaticInt}) = static(0) Base.zero(@nospecialize T::Type{<:StaticBool}) = False() Base.one(@nospecialize T::Type{<:StaticBool}) = True() -Base.one(@nospecialize T::Type{<:StaticInt}) = StaticInt(1) +Base.one(@nospecialize T::Type{<:StaticInt}) = static(1) @inline Base.iszero(::Union{StaticInt{0}, StaticFloat64{0.0}, False}) = true @inline Base.iszero(@nospecialize x::Union{StaticInt, True, StaticFloat64}) = false @@ -403,23 +447,18 @@ Base.sign(::StaticNumber{N}) where {N} = static(sign(N)) Base.widen(@nospecialize(x::StaticNumber)) = widen(known(x)) -function Base.convert(::Type{T}, @nospecialize(N::StaticNumber)) where {T <: Number} - convert(T, known(N)) -end - -#Base.Bool(::StaticInt{N}) where {N} = Bool(N) -Base.Integer(@nospecialize(x::StaticInt)) = x -(::Type{T})(x::StaticInteger) where {T <: Real} = T(known(x)) -function (@nospecialize(T::Type{<:StaticNumber}))(x::Union{AbstractFloat, - AbstractIrrational, Integer, - Rational}) - static(convert(eltype(T), x)) -end @inline Base.:(-)(::StaticNumber{N}) where {N} = static(-N) -Base.:(*)(::Union{AbstractFloat, AbstractIrrational, Integer, Rational}, y::Zero) = y -Base.:(*)(x::Zero, ::Union{AbstractFloat, AbstractIrrational, Integer, Rational}) = x +Base.:(*)(x::Zero, ::Zero) = x +Base.:(*)(::Real, y::Zero) = y +Base.:(*)(::Integer, y::Zero) = y +Base.:(*)(::Rational, y::Zero) = y +Base.:(*)(x::Zero, ::Real) = x +Base.:(*)(x::Zero, ::Integer) = x +Base.:(*)(x::Zero, ::Rational) = x +Base.:(*)(x::Zero, ::StaticInteger{Y}) where {Y} = x +Base.:(*)(::StaticInteger{X}, y::Zero) where {X} = y Base.:(*)(::StaticInteger{X}, ::StaticInteger{Y}) where {X, Y} = static(X * Y) Base.:(/)(::StaticInteger{X}, ::StaticInteger{Y}) where {X, Y} = static(X / Y) Base.:(-)(::StaticInteger{X}, ::StaticInteger{Y}) where {X, Y} = static(X - Y) @@ -429,71 +468,77 @@ Base.:(-)(::StaticInt{N}, y::Ptr) where {N} = y - N Base.:(+)(x::Ptr, ::StaticInt{N}) where {N} = x + N Base.:(+)(::StaticInt{N}, y::Ptr) where {N} = y + N +Base.leading_zeros(x::StaticInt) = leading_zeros(known(x)) +Base.trailing_zeros(x::StaticInt) = leading_zeros(known(x)) + @generated Base.sqrt(::StaticNumber{N}) where {N} = :($(static(sqrt(N)))) -function Base.div(::StaticNumber{X}, ::StaticNumber{Y}, m::RoundingMode) where {X, Y} +#=function Base.div(::StaticNumber{X}, ::StaticNumber{Y}, m::RoundingMode) where {X, Y} static(div(X, Y, m)) +end=# +for mode in (:Up, :Down, :ToZero, :Nearest, :NearestTiesAway, :NearestTiesUp) + @eval function Base.div(::StaticBool{X}, ::StaticBool{Y}, m::RoundingMode{$(QuoteNode(mode))}) where {X, Y} + static(div(X, Y, m)) + end + @eval function Base.div(::StaticFloat64{X}, ::StaticFloat64{Y}, m::RoundingMode{$(QuoteNode(mode))}) where {X, Y} + static(div(X, Y, m)) + end + @eval function Base.div(::StaticInt{X}, ::StaticInt{Y}, m::RoundingMode{$(QuoteNode(mode))}) where {X, Y} + static(div(X, Y, m)) + end end -Base.div(x::Real, ::StaticInteger{Y}, m::RoundingMode) where {Y} = div(x, Y, m) -Base.div(::StaticNumber{X}, y::Real, m::RoundingMode) where {X} = div(X, y, m) +#=function Base.div(::StaticNumber{X}, ::StaticNumber{Y}, m::Union{RoundingMode{:RoundUp},RoundingMode{:RoundDown},RoundingMode{:Nearest}, RoundingMode{:NearestTiesAway}, RoundingMode{:NearestTiesUp}}) where {X, Y} + static(div(X, Y, m)) +end=# Base.div(x::StaticBool, y::False) = throw(DivideError()) Base.div(x::StaticBool, y::True) = x Base.rem(@nospecialize(x::StaticNumber), T::Type{<:Integer}) = rem(known(x), T) +Base.rem(@nospecialize(x::StaticNumber), T::Type{Bool}) = rem(known(x), T) +Base.rem(@nospecialize(x::StaticNumber), T::Type{BigInt}) = rem(known(x), T) Base.rem(::StaticNumber{X}, ::StaticNumber{Y}) where {X, Y} = static(rem(X, Y)) -Base.rem(x::Real, ::StaticInteger{Y}) where {Y} = rem(x, Y) -Base.rem(::StaticInteger{X}, y::Real) where {X} = rem(X, y) Base.mod(::StaticNumber{X}, ::StaticNumber{Y}) where {X, Y} = static(mod(X, Y)) Base.:(==)(::StaticNumber{X}, ::StaticNumber{Y}) where {X, Y} = ==(X, Y) Base.:(<)(::StaticNumber{X}, ::StaticNumber{Y}) where {X, Y} = <(X, Y) +Base.:(<=)(::StaticNumber{X}, ::StaticNumber{Y}) where {X, Y} = <=(X, Y) Base.isless(::StaticInteger{X}, ::StaticInteger{Y}) where {X, Y} = isless(X, Y) -Base.isless(::StaticInteger{X}, y::Real) where {X} = isless(X, y) -Base.isless(x::Real, ::StaticInteger{Y}) where {Y} = isless(x, Y) Base.min(::StaticInteger{X}, ::StaticInteger{Y}) where {X, Y} = static(min(X, Y)) -Base.min(::StaticInteger{X}, y::Number) where {X} = min(X, y) -Base.min(x::Number, ::StaticInteger{Y}) where {Y} = min(x, Y) Base.max(::StaticInteger{X}, ::StaticInteger{Y}) where {X, Y} = static(max(X, Y)) -Base.max(::StaticInteger{X}, y::Number) where {X} = max(X, y) -Base.max(x::Number, ::StaticInteger{Y}) where {Y} = max(x, Y) Base.minmax(::StaticNumber{X}, ::StaticNumber{Y}) where {X, Y} = static(minmax(X, Y)) Base.:(<<)(::StaticInteger{X}, ::StaticInteger{Y}) where {X, Y} = static(<<(X, Y)) -Base.:(<<)(::StaticInteger{X}, n::Integer) where {X} = <<(X, n) -Base.:(<<)(x::Integer, ::StaticInteger{N}) where {N} = <<(x, N) +Base.:(<<)(::StaticInteger{X}, n::UInt) where {X} = <<(X, n) Base.:(>>)(::StaticInteger{X}, ::StaticInteger{Y}) where {X, Y} = static(>>(X, Y)) -Base.:(>>)(::StaticInteger{X}, n::Integer) where {X} = >>(X, n) -Base.:(>>)(x::Integer, ::StaticInteger{N}) where {N} = >>(x, N) +Base.:(>>)(::StaticInteger{X}, n::UInt) where {X} = >>(X, n) Base.:(>>>)(::StaticInteger{X}, ::StaticInteger{Y}) where {X, Y} = static(>>>(X, Y)) -Base.:(>>>)(::StaticInteger{X}, n::Integer) where {X} = >>>(X, n) -Base.:(>>>)(x::Integer, ::StaticInteger{N}) where {N} = >>>(x, N) +Base.:(>>>)(::StaticInteger{X}, n::UInt) where {X} = >>>(X, n) + Base.:(&)(::StaticInteger{X}, ::StaticInteger{Y}) where {X, Y} = static(X & Y) -Base.:(&)(::StaticInteger{X}, y::Union{Integer, Missing}) where {X} = X & y -Base.:(&)(x::Union{Integer, Missing}, ::StaticInteger{Y}) where {Y} = x & Y -Base.:(&)(x::Bool, y::True) = x -Base.:(&)(x::Bool, y::False) = y -Base.:(&)(x::True, y::Bool) = y -Base.:(&)(x::False, y::Bool) = x +#!!Base.:(&)(x::Bool, y::True) = x +#!!Base.:(&)(x::Bool, y::False) = y +#!!Base.:(&)(x::True, y::Bool) = y +#!!Base.:(&)(x::False, y::Bool) = x Base.:(|)(::StaticInteger{X}, ::StaticInteger{Y}) where {X, Y} = static(|(X, Y)) -Base.:(|)(::StaticInteger{X}, y::Union{Integer, Missing}) where {X} = X | y -Base.:(|)(x::Union{Integer, Missing}, ::StaticInteger{Y}) where {Y} = x | Y -Base.:(|)(x::Bool, y::True) = y -Base.:(|)(x::Bool, y::False) = x -Base.:(|)(x::True, y::Bool) = x -Base.:(|)(x::False, y::Bool) = y +#!!Base.:(|)(::StaticInteger{X}, y::Union{Integer, Missing}) where {X} = X | y +#!!Base.:(|)(x::Union{Integer, Missing}, ::StaticInteger{Y}) where {Y} = x | Y +#!!Base.:(|)(x::Bool, y::True) = y +#!!Base.:(|)(x::Bool, y::False) = x +#!!Base.:(|)(x::True, y::Bool) = x +#!!Base.:(|)(x::False, y::Bool) = y Base.xor(::StaticInteger{X}, ::StaticInteger{Y}) where {X, Y} = static(xor(X, Y)) -Base.xor(::StaticInteger{X}, y::Union{Integer, Missing}) where {X} = xor(X, y) -Base.xor(x::Union{Integer, Missing}, ::StaticInteger{Y}) where {Y} = xor(x, Y) +#!!Base.xor(::StaticInteger{X}, y::Union{Integer, Missing}) where {X} = xor(X, y) +#!!Base.xor(x::Union{Integer, Missing}, ::StaticInteger{Y}) where {Y} = xor(x, Y) Base.:(!)(::True) = False() Base.:(!)(::False) = True() @@ -546,7 +591,7 @@ Base.:(^)(x::BigInt, y::True) = x end @inline function invariant_permutation(@nospecialize(x::Tuple), @nospecialize(y::Tuple)) - if y === x === ntuple(static, StaticInt(nfields(x))) + if y === x === ntuple(static, static(nfields(x))) return True() else return False() @@ -617,7 +662,7 @@ value is a `StaticInt`. end function Base.invperm(p::Tuple{StaticInt, Vararg{StaticInt, N}}) where {N} - map(Base.Fix2(find_first_eq, p), ntuple(static, StaticInt(N + 1))) + map(Base.Fix2(find_first_eq, p), ntuple(static, static(N + 1))) end """ @@ -949,6 +994,9 @@ end return (Base.to_index(A, I[1]), to_indices(A, indstail, Base.tail(I))...) end +Base.string(::True) = "static(true)" +Base.string(::False) = "static(false)" + function Base.show(io::IO, @nospecialize(x::Union{StaticNumber, StaticSymbol, NDIndex})) show(io, MIME"text/plain"(), x) end diff --git a/src/float.jl b/src/float.jl index 3bb17bb..399b8c8 100644 --- a/src/float.jl +++ b/src/float.jl @@ -7,13 +7,14 @@ Use `StaticInt(N)` instead of `Val(N)` when you want it to behave like a number. """ struct StaticFloat64{N} <: Real StaticFloat64{N}() where {N} = new{N::Float64}() - StaticFloat64(x::Float64) = new{x}() + #!!!StaticFloat64(x::Float64) = new{x}() StaticFloat64(x::Int) = new{Base.sitofp(Float64, x)::Float64}() - StaticFloat64(x::StaticInt{N}) where {N} = StaticFloat64(convert(Float64, N)) - StaticFloat64(x::Complex) = StaticFloat64(convert(Float64, x)) + #!!!StaticFloat64(x::Complex) = StaticFloat64(convert(Float64, x)) StaticFloat64(@nospecialize x::StaticFloat64) = x + #!!!StaticFloat64(x::Real) = StaticFloat64(convert(Float64, x)) end + Base.zero(@nospecialize T::Type{<:StaticFloat64}) = Float64(0.0) Base.one(@nospecialize T::Type{<:StaticFloat64}) = Float64(1.0) @@ -74,9 +75,9 @@ Base.asec(x::StaticFloat64{M}) where {M} = acos(inv(x)) Base.acsc(x::StaticFloat64{M}) where {M} = asin(inv(x)) Base.acot(x::StaticFloat64{M}) where {M} = atan(inv(x)) -Base.rem(x::Real, ::StaticFloat64{Y}) where {Y} = rem(x, Y) -Base.rem(::StaticFloat64{X}, y::Real) where {X} = rem(X, y) -Base.rem(::StaticFloat64{X}, ::StaticFloat64{Y}) where {X, Y} = StaticFloat64(rem(X, Y)) +#!!!Base.rem(x::Real, ::StaticFloat64{Y}) where {Y} = rem(x, Y) +#!!!Base.rem(::StaticFloat64{X}, y::Real) where {X} = rem(X, y) +#!!!Base.rem(::StaticFloat64{X}, ::StaticFloat64{Y}) where {X, Y} = StaticFloat64(rem(X, Y)) Base.min(x::StaticFloat64{X}, y::StaticFloat64{Y}) where {X, Y} = X > Y ? y : x Base.min(x::Real, ::StaticFloat64{Y}) where {Y} = min(x, Y) diff --git a/src/ranges.jl b/src/ranges.jl index 6a15be8..ccd65e8 100644 --- a/src/ranges.jl +++ b/src/ranges.jl @@ -203,6 +203,9 @@ function Base.:(:)(start::Integer, ::StaticInt{1}, stop::Integer) OptionallyStaticUnitRange(start, stop) end +Base.:(:)(a::StaticFloat64, b::StaticFloat64) = (:)(known(a), known(b)) + + Base.isempty(r::OptionallyStaticUnitRange) = first(r) > last(r) @inline function Base.isempty(x::OptionallyStaticStepRange) start = first(x) diff --git a/test/runtests.jl b/test/runtests.jl index 440a86a..bc9ed39 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -142,17 +142,17 @@ end @test @inferred(xor(t, t)) === f @test @inferred(|(true, f)) - @test @inferred(|(true, t)) === t + @test @inferred(|(true, t)) === true @test @inferred(|(f, true)) - @test @inferred(|(t, true)) === t + @test @inferred(|(t, true)) === true @test @inferred(|(f, f)) === f @test @inferred(|(f, t)) === t @test @inferred(|(t, f)) === t @test @inferred(|(t, t)) === t - @test @inferred(Base.:(&)(true, f)) === f + @test @inferred(Base.:(&)(true, f)) === false @test @inferred(Base.:(&)(true, t)) - @test @inferred(Base.:(&)(f, true)) === f + @test @inferred(Base.:(&)(f, true)) === false @test @inferred(Base.:(&)(t, true)) @test @inferred(Base.:(&)(f, f)) === f @test @inferred(Base.:(&)(f, t)) === f @@ -485,10 +485,10 @@ y = 1:10 @test typeof(fone)(1.0) isa Static.StaticFloat64 @test @inferred(eltype(Static.StaticFloat64(static(1)))) <: Float64 - @test @inferred(promote_rule(typeof(fone), Int)) <: promote_type(Float64, Int) - @test @inferred(promote_rule(typeof(fone), Float64)) <: Float64 - @test @inferred(promote_rule(typeof(fone), Float32)) <: Float32 - @test @inferred(promote_rule(typeof(fone), Float16)) <: Float16 + @test @inferred(promote_rule(typeof(fone), Int)) == Float64 + @test @inferred(promote_type(typeof(fone), Float64)) == Float64 + @test @inferred(promote_type(typeof(fone), Float32)) == Float64 + @test @inferred(promote_type(typeof(fone), Float16)) == Float64 @test @inferred(inv(static(2.0))) === static(inv(2.0)) === inv(static(2)) @@ -521,6 +521,7 @@ end @test repr(static(true)) == "static(true)" @test repr(static(CartesianIndex(1, 1))) == "NDIndex(static(1), static(1))" @test string(static(true)) == "static(true)" == "$(static(true))" + @test string(static(false)) == "static(false)" == "$(static(false))" @test repr(static(1):static(10)) == "static(1):static(10)" @test repr(static(1):static(2):static(9)) == "static(1):static(2):static(9)" end