Skip to content

Commit

Permalink
Merge 3bc78f4 into e6e0798
Browse files Browse the repository at this point in the history
  • Loading branch information
jishnub committed Jun 24, 2019
2 parents e6e0798 + 3bc78f4 commit 48ae8bd
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 63 deletions.
7 changes: 4 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ language: julia
os:
- linux
julia:
- release
- 1.1
- 1.0
- nightly
notifications:
email: false
script:
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- julia --check-bounds=yes -e 'Pkg.clone(pwd()); Pkg.build("PiecewiseIncreasingRanges"); Pkg.test("PiecewiseIncreasingRanges"; coverage=true)'
- julia --check-bounds=yes -e 'if VERSION >= v"0.7.0-" using Pkg; end; Pkg.clone(pwd()); Pkg.build("PiecewiseIncreasingRanges"); Pkg.test("PiecewiseIncreasingRanges"; coverage=true)'
after_success:
- if [ $TRAVIS_JULIA_VERSION = "nightly" ]; then julia -e 'cd(Pkg.dir("PiecewiseIncreasingRanges")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())'; fi
- if [ $TRAVIS_JULIA_VERSION = "nightly" ]; then julia -e 'if VERSION >= v"0.7.0-" using Pkg; end; import PiecewiseIncreasingRanges; cd(joinpath(dirname(pathof(PiecewiseIncreasingRanges)),"..")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())'; fi
2 changes: 1 addition & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
julia 0.3
julia 1.1.1
Compat
93 changes: 45 additions & 48 deletions src/PiecewiseIncreasingRanges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ export PiecewiseIncreasingRange, NoNearestSampleError, findnearest, resample

using Compat, Base.Order

constructrange{T<:UnitRange}(::Type{T}, start, step, stop) = T(start, stop)
constructrange{T<:StepRange}(::Type{T}, start, step, stop) = T(start, step, stop)
constructrange(::Type{T}, start, step, stop) where {T<:UnitRange} = T(start, stop)
constructrange(::Type{T}, start, step, stop) where {T<:StepRange} = T(start, step, stop)

function combine_ranges{R<:Range}(ranges::Vector{R}, firstrg::R, firstrgidx::Int)
function combine_ranges(ranges::Vector{R}, firstrg::R, firstrgidx::Int) where {R<:AbstractRange}
newranges = R[]
offsets = Int[1]

step(firstrg) < 0 && throw(ArgumentError("ranges must be strictly monotonically increasing"))
step(firstrg) < zero(eltype(R)) && throw(ArgumentError("ranges must be strictly monotonically increasing"))
curstart = first(firstrg)
curstop = last(firstrg)
curstep = step(firstrg)
Expand All @@ -23,7 +23,7 @@ function combine_ranges{R<:Range}(ranges::Vector{R}, firstrg::R, firstrgidx::Int
# Can extend current range
curstop = last(newrg)
else
if first(newrg) <= curstop || step(newrg) < 0
if first(newrg) <= curstop || step(newrg) < zero(eltype(R))
throw(ArgumentError("ranges must be strictly monotonically increasing"))
end

Expand All @@ -42,11 +42,11 @@ function combine_ranges{R<:Range}(ranges::Vector{R}, firstrg::R, firstrgidx::Int
(newranges, offsets)
end

function combine_ranges{R<:FloatRange}(ranges::Vector{R}, firstrg::R, firstrgidx::Int)
function combine_ranges(ranges::Vector{R}, firstrg::R, firstrgidx::Int) where {R<:StepRangeLen}
newranges = R[]
offsets = Int[1]

if signbit(firstrg.step) != signbit(firstrg.divisor)
if step(firstrg) <= zero(eltype(R))
throw(ArgumentError("ranges must be strictly monotonically increasing"))
end
currg = firstrg
Expand All @@ -55,10 +55,11 @@ function combine_ranges{R<:FloatRange}(ranges::Vector{R}, firstrg::R, firstrgidx
newrg = ranges[i]
isempty(newrg) && continue

if step(newrg) == step(currg) && newrg.start*currg.divisor == (currg.start+currg.step*currg.len)*newrg.divisor
currg = FloatRange(currg.start, currg.step, currg.len + newrg.len, currg.divisor)
if step(newrg) == step(currg) && first(newrg) == last(currg) + step(currg)
currg = R(currg.ref, step(currg),
length(currg) + length(newrg),currg.offset)
else
if first(newrg) <= last(currg) || signbit(newrg.step) != signbit(newrg.divisor)
if first(newrg) <= last(currg) || step(newrg) <= zero(eltype(R))
throw(ArgumentError("ranges must be strictly monotonically increasing"))
end

Expand All @@ -73,40 +74,36 @@ function combine_ranges{R<:FloatRange}(ranges::Vector{R}, firstrg::R, firstrgidx
(newranges, offsets)
end

immutable PiecewiseIncreasingRange{T,R<:Range,S} <: AbstractVector{T}
struct PiecewiseIncreasingRange{T,R<:AbstractRange,S} <: AbstractVector{T}
ranges::Vector{R}
offsets::Vector{Int}
divisor::S

function PiecewiseIncreasingRange(ranges::AbstractVector, divisor)
function PiecewiseIncreasingRange{T,R,S}(ranges::AbstractVector{R}, divisor::S) where {T,R<:AbstractRange,S}
ranges = convert(Vector{R}, ranges)
isempty(ranges) && return new(ranges, Int[])
isempty(ranges) && return new{T,R,S}(ranges, Int[])

# Find first non-empty range
firstrg = ranges[1]
j = 0
for j = 1:length(ranges)
firstrg = ranges[j]
!isempty(firstrg) && break
end
isempty(firstrg) && return new(R[], Int[], divisor)
j = findfirst(!isempty,ranges)
isnothing(j) && return new{T,R,S}(R[], Int[], divisor)

firstrg = ranges[j]
newranges, offsets = combine_ranges(ranges, firstrg, j+1)
new(newranges, offsets, divisor)
new{T,R,S}(newranges, offsets, divisor)
end
end
PiecewiseIncreasingRange{R<:Range}(ranges::Vector{R}, divisor) = PiecewiseIncreasingRange{typeof(inv(one(eltype(R)))),R,typeof(divisor)}(ranges, divisor)
PiecewiseIncreasingRange{R<:Range}(ranges::Vector{R}) = PiecewiseIncreasingRange{eltype(R),R,@compat(Void)}(ranges, nothing)
PiecewiseIncreasingRange(ranges::Vector{R}, divisor) where {R<:AbstractRange} = PiecewiseIncreasingRange{typeof(inv(one(eltype(R)))),R,typeof(divisor)}(ranges, divisor)
PiecewiseIncreasingRange(ranges::Vector{R}) where {R<:AbstractRange} = PiecewiseIncreasingRange{eltype(R),R,@compat(Nothing)}(ranges, nothing)

Base.convert{T,R<:Range,S}(::Type{PiecewiseIncreasingRange{T,R,S}}, x::PiecewiseIncreasingRange{T,R,S}) = x
Base.convert{T,R,S}(::Type{PiecewiseIncreasingRange{T,R,S}}, x::PiecewiseIncreasingRange) =
Base.convert(::Type{PiecewiseIncreasingRange{T,R,S}}, x::PiecewiseIncreasingRange{T,R,S}) where {T,R<:AbstractRange,S} = x
Base.convert(::Type{PiecewiseIncreasingRange{T,R,S}}, x::PiecewiseIncreasingRange) where {T,R,S} =
PiecewiseIncreasingRange{T,R,S}(x.ranges, x.divisor)

# Avoid applying the divisor if it is one, to get types right
divide_divisor{T,R}(r::PiecewiseIncreasingRange{T,R,@compat(Void)}, x) = x
multiply_divisor{T,R}(r::PiecewiseIncreasingRange{T,R,@compat(Void)}, x) = x
divide_divisor{T,R,S}(r::PiecewiseIncreasingRange{T,R,S}, x) = x/r.divisor
multiply_divisor{T,R,S}(r::PiecewiseIncreasingRange{T,R,S}, x) = x*r.divisor
divide_divisor(r::PiecewiseIncreasingRange{T,R,@compat(Nothing)}, x) where {T,R} = x
multiply_divisor(r::PiecewiseIncreasingRange{T,R,@compat(Nothing)}, x) where {T,R} = x
divide_divisor(r::PiecewiseIncreasingRange{T,R,S}, x) where {T,R,S} = x/r.divisor
multiply_divisor(r::PiecewiseIncreasingRange{T,R,S}, x) where {T,R,S} = x*r.divisor

function Base.size(r::PiecewiseIncreasingRange)
isempty(r.ranges) && return (0,)
Expand All @@ -118,7 +115,7 @@ function Base.getindex(r::PiecewiseIncreasingRange, i::Integer)
divide_divisor(r, r.ranges[rgidx][i-r.offsets[rgidx]+1])
end

function Base.getindex{T,R,S}(r::PiecewiseIncreasingRange{T,R,S}, x::Range{Int})
function Base.getindex(r::PiecewiseIncreasingRange{T,R,S}, x::AbstractRange{Int}) where {T,R,S}
isempty(x) && return PiecewiseIncreasingRange{T,R,S}(R[], Int[], r.divisor)
(first(x) >= 1 && last(x) <= length(r)) || throw(BoundsError())

Expand All @@ -127,48 +124,48 @@ function Base.getindex{T,R,S}(r::PiecewiseIncreasingRange{T,R,S}, x::Range{Int})

firstrgidx = searchsortedlast(r.offsets, first(x), Forward)
lastrgidx = searchsortedlast(r.offsets, last(x), Forward)
newrgs = Array(R, lastrgidx-firstrgidx+1)
newrgs = Array{R}(undef, lastrgidx-firstrgidx+1)
for irange = firstrgidx:lastrgidx
elmax = min(last(x)-r.offsets[irange]+1, length(r.ranges[irange]))
newrg = newrgs[irange-firstrgidx+1] = r.ranges[irange][first(x)-r.offsets[irange]+1:elmax]
x = x[length(newrg)+1:end]
end
PiecewiseIncreasingRange(newrgs, r.divisor)
PiecewiseIncreasingRange{T,R,S}(newrgs, r.divisor)
end

function resample{T,R,S}(r::PiecewiseIncreasingRange{T,R,S}, ratio::Rational{Int})
function resample(r::PiecewiseIncreasingRange{T,R,S}, ratio::Rational{Int}) where {T,R,S}
excess = zero(eltype(R))
curpt = 0
newrgs = R[]
for i = 1:length(r.ranges)
endpt = last(r.ranges[i])*num(ratio)
newrg = first(r.ranges[i])*num(ratio)+excess*step(r.ranges[i]):step(r.ranges[i])*den(ratio):endpt
endpt = last(r.ranges[i])*numerator(ratio)
newrg = first(r.ranges[i])*numerator(ratio)+excess*step(r.ranges[i]):step(r.ranges[i])*denominator(ratio):endpt
curpt += length(newrg)
push!(newrgs, newrg)
if i != length(r.ranges)
# Need to linearly interpolate between endpoint and next range
# nextind is the next index in r (= 1 + curpt/ratio) times num(ratio)
nextind = num(ratio) + curpt*den(ratio)
if nextind < r.offsets[i+1]*num(ratio)
# nextind is the next index in r (= 1 + curpt/ratio) times numerator(ratio)
nextind = numerator(ratio) + curpt*denominator(ratio)
if nextind < r.offsets[i+1]*numerator(ratio)
# Need to interpolate between this point and the next
weight = mod(nextind, num(ratio))
interpt = last(r.ranges[i])*(num(ratio)-weight) + first(r.ranges[i+1])*weight
interpstep = (first(r.ranges[i+1]) - last(r.ranges[i]))*den(ratio)
newrg = interpt:interpstep:first(r.ranges[i+1])*num(ratio)
weight = mod(nextind, numerator(ratio))
interpt = last(r.ranges[i])*(numerator(ratio)-weight) + first(r.ranges[i+1])*weight
interpstep = (first(r.ranges[i+1]) - last(r.ranges[i]))*denominator(ratio)
newrg = interpt:interpstep:first(r.ranges[i+1])*numerator(ratio)
push!(newrgs, newrg)
curpt += length(newrg)
nextind += length(newrg)*den(ratio)
nextind += length(newrg)*denominator(ratio)
end
excess = nextind - r.offsets[i+1]*num(ratio)
excess = nextind - r.offsets[i+1]*numerator(ratio)
end
end
PiecewiseIncreasingRange(newrgs, multiply_divisor(r, num(ratio)))
PiecewiseIncreasingRange(newrgs, multiply_divisor(r, numerator(ratio)))
end

# searchsortedfirst, searchsortedlast
immutable PiecewiseIncreasingRangeFirstOrdering <: Ordering end
struct PiecewiseIncreasingRangeFirstOrdering <: Ordering end
Base.Order.lt(o::PiecewiseIncreasingRangeFirstOrdering, a, b) = isless(first(a), first(b))
immutable PiecewiseIncreasingRangeLastOrdering <: Ordering end
struct PiecewiseIncreasingRangeLastOrdering <: Ordering end
Base.Order.lt(o::PiecewiseIncreasingRangeLastOrdering, a, b) = isless(last(a), last(b))

function Base.searchsortedfirst(r::PiecewiseIncreasingRange, x)
Expand All @@ -189,7 +186,7 @@ function Base.searchsortedlast(r::PiecewiseIncreasingRange, x)
searchsortedlast(r.ranges[rgidx], xd, Forward) + r.offsets[rgidx] - 1
end

immutable NoNearestSampleError <: Exception end
struct NoNearestSampleError <: Exception end

function findnearest(r::PiecewiseIncreasingRange, x, within_half_step::Bool=false)
isempty(r.ranges) && throw(NoNearestSampleError())
Expand Down
2 changes: 1 addition & 1 deletion test/REQUIRE
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Grid
Interpolations
20 changes: 10 additions & 10 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using PiecewiseIncreasingRanges, Base.Test, Grid
using PiecewiseIncreasingRanges, Test, Interpolations

function test(rgs, divisor...)
vcrgs = vcat(rgs...)
Expand All @@ -7,11 +7,11 @@ function test(rgs, divisor...)
@test length(rg.ranges) == 4
@test vcrgs == rg

yi = InterpGrid(convert(Vector{Float64}, vcrgs), BCnil, InterpLinear)
@test_approx_eq resample(rg, 3//7) yi[1:7//3:length(yi)]
@test_approx_eq resample(rg, 7//3) yi[1:3//7:length(yi)]
@test_approx_eq resample(rg, 5//9) yi[1:9//5:length(yi)]
@test_approx_eq resample(rg, 61//3) yi[1:3//61:length(yi)]
yi = LinearInterpolation(1:length(vcrgs),convert(Vector{Float64}, vcrgs))
@test resample(rg, 3//7) yi(1:7//3:length(yi))
@test resample(rg, 7//3) yi(1:3//7:length(yi))
@test resample(rg, 5//9) yi(1:9//5:length(yi))
@test resample(rg, 61//3) yi(1:3//61:length(yi))

for i = 1:length(rg)
@test searchsortedfirst(rg, rg[i]) == i
Expand Down Expand Up @@ -49,7 +49,7 @@ end

test(StepRange{Int,Int}[0:4:40, 41:1:80, 82:2:112, 114:2:120, 136:4:144], 8)
test(StepRange{Rational{Int},Rational{Int}}[0:1//2:5, 5+1//8:1//8:10, 10+1//4:1//4:14, 14+1//4:1//4:15, 17:1//2:18])
test(FloatRange{Float64}[0:1//2:5., 5+1//8:1//8:10., 10+1//4:1//4:14., 14+1//4:1//4:15., 17:1//2:18.])
test(StepRangeLen{Float64}[0:1//2:5., 5+1//8:1//8:10., 10+1//4:1//4:14., 14+1//4:1//4:15., 17:1//2:18.])

# Empty test
rg = PiecewiseIncreasingRange(StepRange{Rational{Int},Rational{Int}}[])
Expand All @@ -63,9 +63,9 @@ rg = PiecewiseIncreasingRange(UnitRange{Int}[0:-1])
@test_throws ArgumentError PiecewiseIncreasingRange(StepRange{Rational{Int},Rational{Int}}[0:1//2:1, 3//4:1//2:4])
@test_throws ArgumentError PiecewiseIncreasingRange(StepRange{Rational{Int},Rational{Int}}[0:-1//2:-1, 10:1//2:8])
@test_throws ArgumentError PiecewiseIncreasingRange(StepRange{Rational{Int},Rational{Int}}[0:1//2:1, 10:-1//2:8])
@test_throws ArgumentError PiecewiseIncreasingRange(FloatRange{Float64}[0:0.5:1, 0.75:0.5:4])
@test_throws ArgumentError PiecewiseIncreasingRange(FloatRange{Float64}[0:-0.5:-1, 10:0.5:8])
@test_throws ArgumentError PiecewiseIncreasingRange(FloatRange{Float64}[0:0.5:1, 10:-0.5:8])
@test_throws ArgumentError PiecewiseIncreasingRange(StepRangeLen{Float64}[0:0.5:1, 0.75:0.5:4])
@test_throws ArgumentError PiecewiseIncreasingRange(StepRangeLen{Float64}[0:-0.5:-1, 10:0.5:8])
@test_throws ArgumentError PiecewiseIncreasingRange(StepRangeLen{Float64}[0:0.5:1, 10:-0.5:8])

# Test with empty ranges interspersed with non-empty ranges
rg = PiecewiseIncreasingRange(UnitRange{Int}[0:-1, 1:0, 0:-1, 1:3, 0:-1, 5:10])
Expand Down

0 comments on commit 48ae8bd

Please sign in to comment.