diff --git a/src/comparison.jl b/src/comparison.jl index 8d1eba87..504d6da7 100644 --- a/src/comparison.jl +++ b/src/comparison.jl @@ -459,11 +459,6 @@ struct ExponentsIterator{M,D<:Union{Nothing,Int},O} ), ) end - if length(object) == 0 && isnothing(maxdegree) - # Otherwise, it will incorrectly think that the iterator is infinite - # while it actually has zero elements - maxdegree = mindegree - end return new{M,typeof(maxdegree),typeof(object)}( object, mindegree, @@ -474,24 +469,41 @@ struct ExponentsIterator{M,D<:Union{Nothing,Int},O} end Base.eltype(::Type{ExponentsIterator{M,D,O}}) where {M,D,O} = O +# `IteratorSize` may actually be finite if the list of variables is empty. +# The same issue happens for `Iterators.Cycle`: +# https://github.com/JuliaLang/julia/pull/54187 +# With `Base.IsInfinite`, some tests fail but with `Base.SizeUnknown`, all +# tests pass. function Base.IteratorSize(::Type{<:ExponentsIterator{M,Nothing}}) where {M} - return Base.IsInfinite() + return Base.SizeUnknown() end function Base.IteratorSize(::Type{<:ExponentsIterator{M,Int}}) where {M} return Base.HasLength() end -function Base.length(it::ExponentsIterator{M,Int}) where {M} - if it.maxdegree < it.mindegree +function _length(it::ExponentsIterator, maxdegree) + if maxdegree < it.mindegree return 0 end - len = binomial(nvariables(it) + it.maxdegree, nvariables(it)) + len = binomial(nvariables(it) + maxdegree, nvariables(it)) if it.mindegree > 0 len -= binomial(nvariables(it) + it.mindegree - 1, nvariables(it)) end return len end +function Base.length(it::ExponentsIterator{M,Int}) where {M} + return _length(it, it.maxdegree) +end + +function Base.length(it::ExponentsIterator{M,Nothing}) where {M} + if isempty(it.object) + return _length(it, it.mindegree) + else + error("The iterator is infinity because `maxdegree` is `nothing`.") + end +end + nvariables(it::ExponentsIterator) = length(it.object) _last_lex_index(n, ::Type{LexOrder}) = n diff --git a/test/comparison.jl b/test/comparison.jl index bab42246..1df0520e 100644 --- a/test/comparison.jl +++ b/test/comparison.jl @@ -7,6 +7,7 @@ function _test(object, M; kws...) it = ExponentsIterator{M}(object; kws...) v = collect(Iterators.take(it, 20)) @test issorted(v, lt = (a, b) -> cmp(M(), a, b) < 0) + @test issorted(v, lt = M()) end function _test(nvars::Int, M; kws...) @@ -26,6 +27,11 @@ function test_errors() "Ordering `$M` is not a valid ordering, use `Graded{$M}` instead.", ) @test_throws err ExponentsIterator{M}([0], maxdegree = 2) + exps = ExponentsIterator{LexOrder}([0]) + err = ErrorException( + "The iterator is infinity because `maxdegree` is `nothing`.", + ) + @test_throws err length(exps) end function test_exponents_iterator()