Skip to content

Commit 1b76b87

Browse files
Remove recursion from Base.:^ (#618)
Co-authored-by: Martin Holters <martin.holters@hsu-hh.de>
1 parent 1f70965 commit 1b76b87

File tree

2 files changed

+71
-52
lines changed

2 files changed

+71
-52
lines changed

src/Filters/coefficients.jl

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Filter types and conversions
22

3+
using Base: uabs
4+
35
abstract type FilterCoefficients{Domain} end
46

57
Base.convert(::Type{T}, f::FilterCoefficients) where {T<:FilterCoefficients} = T(f)
@@ -45,11 +47,9 @@ Base.promote_rule(::Type{ZeroPoleGain{D,Z1,P1,K1}}, ::Type{ZeroPoleGain{D,Z2,P2,
4547
Base.inv(f::ZeroPoleGain{D}) where {D} = ZeroPoleGain{D}(f.p, f.z, inv(f.k))
4648

4749
function Base.:^(f::ZeroPoleGain{D}, e::Integer) where {D}
48-
if e < 0
49-
return inv(f^-e)
50-
else
51-
return ZeroPoleGain{D}(repeat(f.z, e), repeat(f.p, e), f.k^e)
52-
end
50+
ae = uabs(e)
51+
z, p = repeat(f.z, ae), repeat(f.p, ae)
52+
return e < 0 ? ZeroPoleGain{D}(p, z, inv(f.k)^ae) : ZeroPoleGain{D}(z, p, f.k^ae)
5353
end
5454

5555
#
@@ -184,11 +184,12 @@ end
184184
Base.inv(f::PolynomialRatio{D}) where {D} = PolynomialRatio{D}(f.a, f.b)
185185

186186
function Base.:^(f::PolynomialRatio{D,T}, e::Integer) where {D,T}
187+
ae = uabs(e)
188+
b, a = f.b^ae, f.a^ae
187189
if e < 0
188-
return PolynomialRatio{D}(f.a^-e, f.b^-e)
189-
else
190-
return PolynomialRatio{D}(f.b^e, f.a^e)
190+
b, a = a, b
191191
end
192+
return PolynomialRatio{D}(b, a)
192193
end
193194

194195
coef_s(p::LaurentPolynomial) = p[end:-1:0]
@@ -304,7 +305,49 @@ Base.promote_rule(::Type{SecondOrderSections{D,T1,G1}}, ::Type{SecondOrderSectio
304305

305306
SecondOrderSections{D,T,G}(f::SecondOrderSections) where {D,T,G} =
306307
SecondOrderSections{D,T,G}(f.biquads, f.g)
308+
SecondOrderSections{D,T,G}(f::Biquad{D}) where {D,T,G} = SecondOrderSections{D,T,G}([f], one(G))
309+
307310
SecondOrderSections{D}(f::SecondOrderSections{D,T,G}) where {D,T,G} = SecondOrderSections{D,T,G}(f)
311+
SecondOrderSections{D}(f::Biquad{D,T}) where {D,T} = SecondOrderSections{D,T,Int}(f)
312+
313+
*(f::SecondOrderSections{D}, g::Number) where {D} = SecondOrderSections{D}(f.biquads, f.g * g)
314+
*(g::Number, f::SecondOrderSections{D}) where {D} = SecondOrderSections{D}(f.biquads, f.g * g)
315+
*(f1::SecondOrderSections{D}, f2::SecondOrderSections{D}) where {D} =
316+
SecondOrderSections{D}([f1.biquads; f2.biquads], f1.g * f2.g)
317+
*(f1::SecondOrderSections{D}, fs::SecondOrderSections{D}...) where {D} =
318+
SecondOrderSections{D}(vcat(f1.biquads, map(f -> f.biquads, fs)...), f1.g * prod(f.g for f in fs))
319+
320+
*(f1::Biquad{D}, f2::Biquad{D}) where {D} = SecondOrderSections{D}([f1, f2], 1)
321+
*(f1::Biquad{D}, fs::Biquad{D}...) where {D} = SecondOrderSections{D}([f1, fs...], 1)
322+
*(f1::SecondOrderSections{D}, f2::Biquad{D}) where {D} =
323+
SecondOrderSections{D}([f1.biquads; f2], f1.g)
324+
*(f1::Biquad{D}, f2::SecondOrderSections{D}) where {D} =
325+
SecondOrderSections{D}([f1; f2.biquads], f2.g)
326+
327+
Base.inv(f::SecondOrderSections{D}) where {D} = SecondOrderSections{D}(inv.(f.biquads), inv(f.g))
328+
329+
function Base.:^(f::SecondOrderSections{D}, e::Integer) where {D}
330+
ae = uabs(e)
331+
if e < 0
332+
inv_f = inv(f)
333+
return SecondOrderSections{D}(repeat(inv_f.biquads, ae), inv_f.g^ae)
334+
else
335+
return SecondOrderSections{D}(repeat(f.biquads, ae), f.g^ae)
336+
end
337+
end
338+
339+
function Base.:^(f::Biquad{D}, e::Integer) where {D}
340+
ae = uabs(e)
341+
return SecondOrderSections{D}(fill(e < 0 ? inv(f) : f, ae), 1)
342+
end
343+
344+
function Biquad{D,T}(f::SecondOrderSections{D}) where {D,T}
345+
if length(f.biquads) != 1
346+
throw(ArgumentError("only a single second order section may be converted to a biquad"))
347+
end
348+
Biquad{D,T}(f.biquads[1] * f.g)
349+
end
350+
Biquad{D}(f::SecondOrderSections{D,T,G}) where {D,T,G} = Biquad{D,promote_type(T, G)}(f)
308351

309352
function ZeroPoleGain{D,Z,P,K}(f::SecondOrderSections{D}) where {D,Z,P,K}
310353
z = Z[]
@@ -321,14 +364,6 @@ end
321364
ZeroPoleGain{D}(f::SecondOrderSections{D,T,G}) where {D,T,G} =
322365
ZeroPoleGain{D,complex(T),complex(T),G}(f)
323366

324-
function Biquad{D,T}(f::SecondOrderSections{D}) where {D,T}
325-
if length(f.biquads) != 1
326-
throw(ArgumentError("only a single second order section may be converted to a biquad"))
327-
end
328-
Biquad{D,T}(f.biquads[1] * f.g)
329-
end
330-
Biquad{D}(f::SecondOrderSections{D,T,G}) where {D,T,G} = Biquad{D,promote_type(T,G)}(f)
331-
332367
PolynomialRatio{D,T}(f::SecondOrderSections{D}) where {D,T} = PolynomialRatio{D,T}(ZeroPoleGain(f))
333368
PolynomialRatio{D}(f::SecondOrderSections{D}) where {D} = PolynomialRatio{D}(ZeroPoleGain(f))
334369

@@ -447,39 +482,4 @@ end
447482
SecondOrderSections{D}(f::ZeroPoleGain{D,Z,P,K}) where {D,Z,P,K} =
448483
SecondOrderSections{D,promote_type(real(Z), real(P)), K}(f)
449484

450-
451-
SecondOrderSections{D,T,G}(f::Biquad{D}) where {D,T,G} = SecondOrderSections{D,T,G}([f], one(G))
452-
SecondOrderSections{D}(f::Biquad{D,T}) where {D,T} = SecondOrderSections{D,T,Int}(f)
453485
SecondOrderSections{D}(f::FilterCoefficients{D}) where {D} = SecondOrderSections{D}(ZeroPoleGain(f))
454-
455-
*(f::SecondOrderSections{D}, g::Number) where {D} = SecondOrderSections{D}(f.biquads, f.g * g)
456-
*(g::Number, f::SecondOrderSections{D}) where {D} = SecondOrderSections{D}(f.biquads, f.g * g)
457-
*(f1::SecondOrderSections{D}, f2::SecondOrderSections{D}) where {D} =
458-
SecondOrderSections{D}([f1.biquads; f2.biquads], f1.g * f2.g)
459-
*(f1::SecondOrderSections{D}, fs::SecondOrderSections{D}...) where {D} =
460-
SecondOrderSections{D}(vcat(f1.biquads, map(f -> f.biquads, fs)...), f1.g * prod(f.g for f in fs))
461-
462-
*(f1::Biquad{D}, f2::Biquad{D}) where {D} = SecondOrderSections{D}([f1, f2], 1)
463-
*(f1::Biquad{D}, fs::Biquad{D}...) where {D} = SecondOrderSections{D}([f1, fs...], 1)
464-
*(f1::SecondOrderSections{D}, f2::Biquad{D}) where {D} =
465-
SecondOrderSections{D}([f1.biquads; f2], f1.g)
466-
*(f1::Biquad{D}, f2::SecondOrderSections{D}) where {D} =
467-
SecondOrderSections{D}([f1; f2.biquads], f2.g)
468-
469-
Base.inv(f::SecondOrderSections{D}) where {D} = SecondOrderSections{D}(inv.(f.biquads), inv(f.g))
470-
471-
function Base.:^(f::SecondOrderSections{D}, e::Integer) where {D}
472-
if e < 0
473-
return inv(f)^-e
474-
else
475-
return SecondOrderSections{D}(repeat(f.biquads, e), f.g^e)
476-
end
477-
end
478-
479-
function Base.:^(f::Biquad{D}, e::Integer) where {D}
480-
if e < 0
481-
return inv(f)^-e
482-
else
483-
return SecondOrderSections{D}(fill(f, e), 1)
484-
end
485-
end

test/filter_conversion.jl

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ end
219219
p = rand(ComplexF64, Npc) .- (0.5 + 0.5im);
220220
p = [p; conj(p); rand(Npr) .- 0.5; zeros(max(2Nzc+Nzr-2Npc-Npr, 0))]
221221
H′ = ZeroPoleGain(z, p,
222-
(rand() + 0.5) * rand([-1, 1]), # non-zero gain with random sign
222+
(rand() + 0.5) * rand((-1, 1)), # non-zero gain with random sign
223223
)
224224
maybe_biquad = length(z) 2 && length(p) 2 ? (Biquad,) : ()
225225
for T (PolynomialRatio, ZeroPoleGain, SecondOrderSections, maybe_biquad...)
@@ -236,6 +236,25 @@ end
236236
@test filt(H^0, x) x
237237
end
238238
end
239+
# test that ^ doesn't cause stack overflow
240+
let H = PolynomialRatio([1.0], [2.0])^typemin(Int8)
241+
@test coefb(H) == [2.0^128]
242+
@test coefa(H) == [1.0]
243+
end
244+
let zpg = ZeroPoleGain([1], [2], 3)^typemin(Int8)
245+
@test length(zpg.z) == length(zpg.p) == 128
246+
@test all(==(2), zpg.z)
247+
@test all(==(1), zpg.p)
248+
@test zpg.k inv(3)^128
249+
end
250+
let bq = Biquad(1:5...)
251+
sos1 = bq^typemin(Int8)
252+
sos2 = SecondOrderSections(bq)^typemin(Int8)
253+
@test length(sos1.biquads) == length(sos2.biquads) == 128
254+
@test all(==(inv(bq)), sos1.biquads)
255+
@test all(==(inv(bq)), sos2.biquads)
256+
@test sos1.g == sos2.g == 1
257+
end
239258
end
240259

241260
@testset "types" begin

0 commit comments

Comments
 (0)