| Line | Exclusive | Inclusive | Code |
|---|---|---|---|
| 1 | export polynomial, polynomial!, polynomialtype, terms, nterms, coefficients, monomials | ||
| 2 | export coefficienttype, monomialtype | ||
| 3 | export mindegree, maxdegree, extdegree, effective_variables | ||
| 4 | export leadingterm, leadingcoefficient, leadingmonomial | ||
| 5 | export removeleadingterm, removemonomials, monic | ||
| 6 | |||
| 7 | LinearAlgebra.norm(p::AbstractPolynomialLike, r::Int=2) = LinearAlgebra.norm(coefficients(p), r) | ||
| 8 | |||
| 9 | changecoefficienttype(::Type{TT}, ::Type{T}) where {TT<:AbstractTermLike, T} = termtype(TT, T) | ||
| 10 | changecoefficienttype(::Type{PT}, ::Type{T}) where {PT<:AbstractPolynomial, T} = polynomialtype(PT, T) | ||
| 11 | |||
| 12 | changecoefficienttype(p::PT, ::Type{T}) where {PT<:APL, T} = convert(changecoefficienttype(PT, T), p) | ||
| 13 | |||
| 14 | abstract type ListState end | ||
| 15 | abstract type UnsortedState <: ListState end | ||
| 16 | struct MessyState <: UnsortedState end | ||
| 17 | # No duplicates or zeros | ||
| 18 | struct UniqState <: UnsortedState end | ||
| 19 | sortstate(::MessyState) = SortedState() | ||
| 20 | sortstate(::UniqState) = SortedUniqState() | ||
| 21 | struct SortedState <: ListState end | ||
| 22 | struct SortedUniqState <: ListState end | ||
| 23 | |||
| 24 | """ | ||
| 25 | polynomial(p::AbstractPolynomialLike) | ||
| 26 | |||
| 27 | Converts `p` to a value with polynomial type. | ||
| 28 | |||
| 29 | polynomial(p::AbstractPolynomialLike, ::Type{T}) where T | ||
| 30 | |||
| 31 | Converts `p` to a value with polynomial type with coefficient type `T`. | ||
| 32 | |||
| 33 | polynomial(a::AbstractVector, mv::AbstractVector{<:AbstractMonomialLike}) | ||
| 34 | |||
| 35 | Creates a polynomial equal to `dot(a, mv)`. | ||
| 36 | |||
| 37 | polynomial(terms::AbstractVector{<:AbstractTerm}, s::ListState=MessyState()) | ||
| 38 | |||
| 39 | Creates a polynomial equal to `sum(terms)` where `terms` are guaranteed to be in state `s`. | ||
| 40 | |||
| 41 | polynomial(f::Function, mv::AbstractVector{<:AbstractMonomialLike}) | ||
| 42 | |||
| 43 | Creates a polynomial equal to `sum(f(i) * mv[i] for i in 1:length(mv))`. | ||
| 44 | |||
| 45 | ### Examples | ||
| 46 | |||
| 47 | Calling `polynomial([2, 4, 1], [x, x^2*y, x*y])` should return ``4x^2y + xy + 2x``. | ||
| 48 | """ | ||
| 49 | polynomial(p::AbstractPolynomial) = p | ||
| 50 | polynomial(p::APL{T}, ::Type{T}) where T = polynomial(terms(p)) | ||
| 51 | polynomial(p::APL{T}) where T = polynomial(p, T) | ||
| 52 | function polynomial(Q::AbstractMatrix, mv::AbstractVector) | ||
| 53 | LinearAlgebra.dot(mv, Q * mv) | ||
| 54 | end | ||
| 55 | function polynomial(Q::AbstractMatrix, mv::AbstractVector, ::Type{T}) where T | ||
| 56 | polynomial(polynomial(Q, mv), T) | ||
| 57 | end | ||
| 58 | |||
| 59 | polynomial(f::Function, mv::AbstractVector{<:AbstractMonomialLike}) = polynomial!([f(i) * mv[i] for i in 1:length(mv)]) | ||
| 60 | |||
| 61 | polynomial(a::AbstractVector, x::AbstractVector, s::ListState=MessyState()) = polynomial([α * m for (α, m) in zip(a, x)], s) | ||
| 62 | |||
| 63 | polynomial(ts::AbstractVector, s::ListState=MessyState()) = sum(ts) | ||
| 64 | polynomial!(ts::AbstractVector, s::ListState=MessyState()) = sum(ts) | ||
| 65 | |||
| 66 | polynomial!(ts::AbstractVector{<:AbstractTerm}, s::SortedUniqState) = polynomial(coefficient.(ts), monomial.(ts), s) | ||
| 67 | |||
| 68 | function polynomial!(ts::AbstractVector{<:AbstractTerm}, s::SortedState) | ||
| 69 | 92 (11 %) | polynomial!(uniqterms!(ts), SortedUniqState()) | |
| 70 | end | ||
| 71 | function polynomial!(ts::AbstractVector{<:AbstractTerm}, s::UnsortedState=MessyState()) | ||
| 72 | 1388 (166 %) |
1388 (166 %)
samples spent in polynomial!
694 (50 %) (incl.) when called from polynomial! line 72 694 (50 %) (incl.) when called from * line 188
602 (43 %)
samples spent calling
sort!##kw
polynomial!(sort!(ts, lt=(>)), sortstate(s))
92 (7 %) samples spent calling polynomial! 694 (50 %) samples spent calling polynomial! |
|
| 73 | end | ||
| 74 | |||
| 75 | _collect(v::Vector) = v | ||
| 76 | _collect(v::AbstractVector) = collect(v) | ||
| 77 | polynomial(ts::AbstractVector{<:AbstractTerm}, args::Vararg{ListState, N}) where {N} = polynomial!(MA.mutable_copy(_collect(ts)), args...) | ||
| 78 | |||
| 79 | """ | ||
| 80 | polynomialtype(p::AbstractPolynomialLike) | ||
| 81 | |||
| 82 | Returns the type that `p` would have if it was converted into a polynomial. | ||
| 83 | |||
| 84 | polynomialtype(::Type{PT}) where PT<:AbstractPolynomialLike | ||
| 85 | |||
| 86 | Returns the same as `polynomialtype(::PT)`. | ||
| 87 | |||
| 88 | polynomialtype(p::AbstractPolynomialLike, ::Type{T}) where T | ||
| 89 | |||
| 90 | Returns the type that `p` would have if it was converted into a polynomial of coefficient type `T`. | ||
| 91 | |||
| 92 | polynomialtype(::Type{PT}, ::Type{T}) where {PT<:AbstractPolynomialLike, T} | ||
| 93 | |||
| 94 | Returns the same as `polynomialtype(::PT, ::Type{T})`. | ||
| 95 | """ | ||
| 96 | polynomialtype(::Union{P, Type{P}}) where P <: APL = Base.promote_op(polynomial, P) | ||
| 97 | polynomialtype(::Union{P, Type{P}}) where P <: AbstractPolynomial = P | ||
| 98 | polynomialtype(::Union{M, Type{M}}) where M<:AbstractMonomialLike = polynomialtype(termtype(M)) | ||
| 99 | polynomialtype(::Union{M, Type{M}}, ::Type{T}) where {M<:AbstractMonomialLike, T} = polynomialtype(termtype(M, T)) | ||
| 100 | polynomialtype(::Union{P, Type{P}}, ::Type{T}) where {P <: APL, T} = polynomialtype(polynomialtype(P), T) | ||
| 101 | polynomialtype(::Union{AbstractVector{PT}, Type{<:AbstractVector{PT}}}) where PT <: APL = polynomialtype(PT) | ||
| 102 | polynomialtype(::Union{AbstractVector{PT}, Type{<:AbstractVector{PT}}}, ::Type{T}) where {PT <: APL, T} = polynomialtype(PT, T) | ||
| 103 | |||
| 104 | function uniqterms!(ts::AbstractVector{<: AbstractTerm}) | ||
| 105 | i = firstindex(ts) | ||
| 106 | for j in Iterators.drop(eachindex(ts), 1) | ||
| 107 | 4 (0 %) |
4 (100 %)
samples spent calling
iszero
if !iszero(ts[j])
|
|
| 108 | 7 (1 %) | if monomial(ts[i]) == monomial(ts[j]) | |
| 109 | 1 (0 %) |
1 (100 %)
samples spent calling
setindex!
ts[i] = MA.add!(coefficient(ts[i]), coefficient(ts[j])) * monomial(ts[i])
|
|
| 110 | else | ||
| 111 | if !iszero(ts[i]) | ||
| 112 | i += 1 | ||
| 113 | end | ||
| 114 | 1 (0 %) |
1 (100 %)
samples spent calling
setindex!
ts[i] = MA.copy_if_mutable(ts[j])
|
|
| 115 | end | ||
| 116 | end | ||
| 117 | end | ||
| 118 | if i < length(ts) | ||
| 119 | if iszero(ts[i]) | ||
| 120 | i -= 1 | ||
| 121 | end | ||
| 122 | resize!(ts, i) | ||
| 123 | end | ||
| 124 | ts | ||
| 125 | end | ||
| 126 | |||
| 127 | """ | ||
| 128 | terms(p::AbstractPolynomialLike) | ||
| 129 | |||
| 130 | Returns an iterator over the nonzero terms of the polynomial `p` sorted in the decreasing monomial order. | ||
| 131 | |||
| 132 | ### Examples | ||
| 133 | |||
| 134 | Calling `terms` on ``4x^2y + xy + 2x`` should return an iterator of ``[4x^2y, xy, 2x]``. | ||
| 135 | """ | ||
| 136 | terms(t::AbstractTermLike) = iszero(t) ? termtype(t)[] : [term(t)] | ||
| 137 | terms(p::AbstractPolynomialLike) = terms(polynomial(p)) | ||
| 138 | |||
| 139 | """ | ||
| 140 | nterms(p::AbstractPolynomialLike) | ||
| 141 | |||
| 142 | Returns the number of nonzero terms in `p`, i.e. `length(terms(p))`. | ||
| 143 | |||
| 144 | ### Examples | ||
| 145 | |||
| 146 | Calling `nterms` on ``4x^2y + xy + 2x`` should return 3. | ||
| 147 | """ | ||
| 148 | function nterms end | ||
| 149 | |||
| 150 | nterms(t::AbstractTermLike) = iszero(t) ? 0 : 1 | ||
| 151 | nterms(p::AbstractPolynomialLike) = length(terms(p)) | ||
| 152 | |||
| 153 | """ | ||
| 154 | coefficients(p::AbstractPolynomialLike) | ||
| 155 | |||
| 156 | Returns an iterator over the coefficients of `p` of the nonzero terms of the polynomial sorted in the decreasing monomial order. | ||
| 157 | |||
| 158 | coefficients(p::AbstractPolynomialLike, X::AbstractVector) | ||
| 159 | |||
| 160 | Returns an iterator over the coefficients of the monomials of `X` in `p` where `X` is a monomial vector not necessarily sorted but with no duplicate entry. | ||
| 161 | |||
| 162 | ### Examples | ||
| 163 | |||
| 164 | Calling `coefficients` on ``4x^2y + xy + 2x`` should return an iterator of ``[4, 1, 2]``. | ||
| 165 | Calling `coefficients(4x^2*y + x*y + 2x + 3, [x, 1, x*y, y])` should return an iterator of ``[2, 3, 1, 0]``. | ||
| 166 | """ | ||
| 167 | coefficients(p::APL) = coefficient.(terms(p)) | ||
| 168 | function coefficients(p::APL{T}, X::AbstractVector) where T | ||
| 169 | σ, mv = sortmonovec(X) | ||
| 170 | @assert length(mv) == length(X) # no duplicate in X | ||
| 171 | c = zeros(T, length(mv)) | ||
| 172 | i = 1 | ||
| 173 | for t in terms(p) | ||
| 174 | m = monomial(t) | ||
| 175 | while i <= length(mv) && mv[i] > m | ||
| 176 | c[σ[i]] = zero(T) | ||
| 177 | i += 1 | ||
| 178 | end | ||
| 179 | if i <= length(mv) && mv[i] == m | ||
| 180 | c[σ[i]] = coefficient(t) | ||
| 181 | i += 1 | ||
| 182 | end | ||
| 183 | end | ||
| 184 | c | ||
| 185 | end | ||
| 186 | |||
| 187 | """ | ||
| 188 | monomials(p::AbstractPolynomialLike) | ||
| 189 | |||
| 190 | Returns an iterator over the monomials of `p` of the nonzero terms of the polynomial sorted in the decreasing order. | ||
| 191 | |||
| 192 | monomials(vars::Tuple, degs::AbstractVector{Int}, filter::Function = m -> true) | ||
| 193 | |||
| 194 | Builds the vector of all the monovec `m` with variables `vars` such that the degree `degree(m)` is in `degs` and `filter(m)` is `true`. | ||
| 195 | |||
| 196 | ### Examples | ||
| 197 | |||
| 198 | Calling `monomials` on ``4x^2y + xy + 2x`` should return an iterator of ``[x^2y, xy, x]``. | ||
| 199 | |||
| 200 | Calling `monomials((x, y), [1, 3], m -> degree(m, y) != 1)` should return `[x^3, x*y^2, y^3, x]` where `x^2*y` and `y` have been excluded by the filter. | ||
| 201 | """ | ||
| 202 | monomials(p::APL) = monovec(monomial.(terms(p))) | ||
| 203 | |||
| 204 | #$(SIGNATURES) | ||
| 205 | """ | ||
| 206 | mindegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}) | ||
| 207 | |||
| 208 | Returns the minimal total degree of the monomials of `p`, i.e. `minimum(degree, terms(p))`. | ||
| 209 | |||
| 210 | mindegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}, v::AbstractVariable) | ||
| 211 | |||
| 212 | Returns the minimal degree of the monomials of `p` in the variable `v`, i.e. `minimum(degree.(terms(p), v))`. | ||
| 213 | |||
| 214 | ### Examples | ||
| 215 | Calling `mindegree` on on ``4x^2y + xy + 2x`` should return 1, `mindegree(4x^2y + xy + 2x, x)` should return 1 and `mindegree(4x^2y + xy + 2x, y)` should return 0. | ||
| 216 | """ | ||
| 217 | function mindegree(X::AbstractVector{<:AbstractTermLike}, args...) | ||
| 218 | isempty(X) ? 0 : minimum(t -> degree(t, args...), X) | ||
| 219 | end | ||
| 220 | function mindegree(p::AbstractPolynomialLike, args...) | ||
| 221 | mindegree(terms(p), args...) | ||
| 222 | end | ||
| 223 | |||
| 224 | #$(SIGNATURES) | ||
| 225 | """ | ||
| 226 | maxdegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}) | ||
| 227 | |||
| 228 | Returns the maximal total degree of the monomials of `p`, i.e. `maximum(degree, terms(p))`. | ||
| 229 | |||
| 230 | maxdegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}, v::AbstractVariable) | ||
| 231 | |||
| 232 | Returns the maximal degree of the monomials of `p` in the variable `v`, i.e. `maximum(degree.(terms(p), v))`. | ||
| 233 | |||
| 234 | ### Examples | ||
| 235 | Calling `maxdegree` on on ``4x^2y + xy + 2x`` should return 3, `maxdegree(4x^2y + xy + 2x, x)` should return 2 and `maxdegree(4x^2y + xy + 2x, y)` should return 1. | ||
| 236 | """ | ||
| 237 | function maxdegree(X::AbstractVector{<:AbstractTermLike}, args...) | ||
| 238 | isempty(X) ? 0 : maximum(t -> degree(t, args...), X) | ||
| 239 | end | ||
| 240 | function maxdegree(p::AbstractPolynomialLike, args...) | ||
| 241 | maxdegree(terms(p), args...) | ||
| 242 | end | ||
| 243 | |||
| 244 | #$(SIGNATURES) | ||
| 245 | """ | ||
| 246 | extdegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}) | ||
| 247 | |||
| 248 | Returns the extremal total degrees of the monomials of `p`, i.e. `(mindegree(p), maxdegree(p))`. | ||
| 249 | |||
| 250 | extdegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}, v::AbstractVariable) | ||
| 251 | |||
| 252 | Returns the extremal degrees of the monomials of `p` in the variable `v`, i.e. `(mindegree(p, v), maxdegree(p, v))`. | ||
| 253 | |||
| 254 | ### Examples | ||
| 255 | Calling `extdegree` on on ``4x^2y + xy + 2x`` should return `(1, 3)`, `extdegree(4x^2y + xy + 2x, x)` should return `(1, 2)` and `maxdegree(4x^2y + xy + 2x, y)` should return `(0, 1)`. | ||
| 256 | """ | ||
| 257 | function extdegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}, args...) | ||
| 258 | (mindegree(p, args...), maxdegree(p, args...)) | ||
| 259 | end | ||
| 260 | |||
| 261 | """ | ||
| 262 | effective_variables(p::AbstractPolynomialLike) | ||
| 263 | |||
| 264 | Return a vector of `eltype` `variable_union_type(p)` (see [`variable_union_type`](@ref)), | ||
| 265 | containing all the variables that has nonzero degree in at least one term. | ||
| 266 | That is, return all the variables `v` such that `maxdegree(p, v)` is not zero. | ||
| 267 | """ | ||
| 268 | function effective_variables(p::AbstractPolynomialLike) | ||
| 269 | VT = variable_union_type(p) | ||
| 270 | return VT[v for v in variables(p) if !iszero(maxdegree(p, v))] | ||
| 271 | end | ||
| 272 | |||
| 273 | """ | ||
| 274 | leadingterm(p::AbstractPolynomialLike) | ||
| 275 | |||
| 276 | Returns the coefficient of the leading term, i.e. `first(terms(p))`. | ||
| 277 | |||
| 278 | ### Examples | ||
| 279 | |||
| 280 | Calling `leadingterm` on ``4x^2y + xy + 2x`` should return ``4x^2y``. | ||
| 281 | """ | ||
| 282 | function leadingterm(p::AbstractPolynomialLike) | ||
| 283 | if iszero(p) | ||
| 284 | zeroterm(p) | ||
| 285 | else | ||
| 286 | first(terms(p)) | ||
| 287 | end | ||
| 288 | end | ||
| 289 | leadingterm(t::AbstractTermLike) = term(t) | ||
| 290 | |||
| 291 | #$(SIGNATURES) | ||
| 292 | """ | ||
| 293 | leadingcoefficient(p::AbstractPolynomialLike) | ||
| 294 | |||
| 295 | Returns the coefficient of the leading term of `p`, i.e. `coefficient(leadingterm(p))`. | ||
| 296 | |||
| 297 | ### Examples | ||
| 298 | |||
| 299 | Calling `leadingcoefficient` on ``4x^2y + xy + 2x`` should return ``4`` and calling it on ``0`` should return ``0``. | ||
| 300 | """ | ||
| 301 | function leadingcoefficient(p::AbstractPolynomialLike) | ||
| 302 | coefficient(leadingterm(p)) | ||
| 303 | end | ||
| 304 | |||
| 305 | #$(SIGNATURES) | ||
| 306 | """ | ||
| 307 | leadingmonomial(p::AbstractPolynomialLike) | ||
| 308 | |||
| 309 | Returns the monomial of the leading term of `p`, i.e. `monomial(leadingterm(p))` or `first(monomials(p))`. | ||
| 310 | |||
| 311 | ### Examples | ||
| 312 | |||
| 313 | Calling `leadingmonomial` on ``4x^2y + xy + 2x`` should return ``x^2y``. | ||
| 314 | """ | ||
| 315 | function leadingmonomial(p::AbstractPolynomialLike) | ||
| 316 | # first(monomials(p)) would be more efficient for DynamicPolynomials but | ||
| 317 | # monomial(leadingterm(p)) is more efficient for TypedPolynomials and is better if p is a term | ||
| 318 | monomial(leadingterm(p)) | ||
| 319 | end | ||
| 320 | |||
| 321 | #$(SIGNATURES) | ||
| 322 | """ | ||
| 323 | removeleadingterm(p::AbstractPolynomialLike) | ||
| 324 | |||
| 325 | Returns a polynomial with the leading term removed in the polynomial `p`. | ||
| 326 | |||
| 327 | ### Examples | ||
| 328 | |||
| 329 | Calling `removeleadingterm` on ``4x^2y + xy + 2x`` should return ``xy + 2x``. | ||
| 330 | """ | ||
| 331 | function removeleadingterm(p::AbstractPolynomialLike) | ||
| 332 | # Iterators.drop returns an Interators.Drop which is not an AbstractVector | ||
| 333 | polynomial(terms(p)[2:end], SortedUniqState()) | ||
| 334 | end | ||
| 335 | |||
| 336 | #$(SIGNATURES) | ||
| 337 | """ | ||
| 338 | |||
| 339 | Returns a polynomial with the terms having their monomial in the monomial vector `mv` removed in the polynomial `p`. | ||
| 340 | |||
| 341 | ### Examples | ||
| 342 | |||
| 343 | Calling `removemonomials(4x^2*y + x*y + 2x, [x*y])` should return ``4x^2*y + 2x``. | ||
| 344 | """ | ||
| 345 | function removemonomials(p::AbstractPolynomialLike, mv::AbstractVector{MT}) where {MT <: AbstractMonomialLike} | ||
| 346 | smv = monovec(mv) # Make sure it is sorted | ||
| 347 | i = 1 | ||
| 348 | q = zero(p) | ||
| 349 | for t in terms(p) | ||
| 350 | m = monomial(t) | ||
| 351 | while i <= length(smv) && smv[i] > m | ||
| 352 | i += 1 | ||
| 353 | end | ||
| 354 | if i > length(smv) || smv[i] != m | ||
| 355 | q += t | ||
| 356 | end | ||
| 357 | end | ||
| 358 | q | ||
| 359 | end | ||
| 360 | |||
| 361 | """ | ||
| 362 | monic(p::AbstractPolynomialLike) | ||
| 363 | |||
| 364 | Returns `p / leadingcoefficient(p)` where the leading coefficient of the returned polynomials is made sure to be exactly one to avoid rounding error. | ||
| 365 | """ | ||
| 366 | function monic(p::APL) | ||
| 367 | α = leadingcoefficient(p) | ||
| 368 | polynomial!(_divtoone.(terms(p), α)) | ||
| 369 | end | ||
| 370 | monic(m::AbstractMonomialLike) = m | ||
| 371 | monic(t::AbstractTermLike{T}) where T = one(T) * monomial(t) | ||
| 372 | |||
| 373 | function _divtoone(t::AbstractTermLike{T}, α::S) where {T, S} | ||
| 374 | U = Base.promote_op(/, T, S) | ||
| 375 | β = coefficient(t) | ||
| 376 | if β == α | ||
| 377 | one(U) * monomial(t) | ||
| 378 | else | ||
| 379 | (β / α) * monomial(t) | ||
| 380 | end | ||
| 381 | end | ||
| 382 | |||
| 383 | """ | ||
| 384 | mapcoefficientsnz(f::Function, p::AbstractPolynomialLike) | ||
| 385 | |||
| 386 | Returns the polynomial obtained by applying `f` to each coefficients where `f` is a function such that `f(x)` is nonzero if `x` is nonzero. | ||
| 387 | |||
| 388 | ### Examples | ||
| 389 | |||
| 390 | Calling `mapcoefficientsnz(α -> α^2, 2x*y + 3x + 1)` should return `4x*y + 9x + 1`. | ||
| 391 | """ | ||
| 392 | function mapcoefficientsnz(f::Function, p::AbstractPolynomialLike) | ||
| 393 | # Invariant: p has only nonzero coefficient | ||
| 394 | # therefore f(α) will be nonzero for every coefficient α of p | ||
| 395 | # hence we can use Uniq | ||
| 396 | polynomial!(mapcoefficientsnz.(f, terms(p)), SortedUniqState()) | ||
| 397 | end | ||
| 398 | mapcoefficientsnz(f::Function, t::AbstractTermLike) = f(coefficient(t)) * monomial(t) | ||
| 399 | |||
| 400 | Base.round(t::AbstractTermLike; args...) = round(coefficient(t); args...) * monomial(t) | ||
| 401 | function Base.round(p::AbstractPolynomialLike; args...) | ||
| 402 | # round(0.1) is zero so we cannot use SortedUniqState | ||
| 403 | polynomial!(round.(terms(p); args...), SortedState()) | ||
| 404 | end | ||
| 405 | |||
| 406 | Base.ndims(::Type{<:AbstractPolynomialLike}) = 0 | ||
| 407 | Base.broadcastable(p::AbstractPolynomialLike) = Ref(p) |