Skip to content

Commit

Permalink
alternate implementation of Range1 storing start,stop instead of star…
Browse files Browse the repository at this point in the history
…t,len

this Range1 could be the UnitRange of #5585, with Range1 deprecated

also intended to address #5469 (performance)
  • Loading branch information
JeffBezanson committed Mar 12, 2014
1 parent be68a11 commit c4f8295
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 96 deletions.
2 changes: 1 addition & 1 deletion base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ full(x::AbstractArray) = x
for fn in _numeric_conversion_func_names
@eval begin
$fn(r::Range ) = Range($fn(r.start), $fn(r.step), r.len)
$fn(r::Range1) = Range1($fn(r.start), r.len)
$fn(r::Range1) = Range1($fn(r.start), $fn(last(r)))
$fn(r::FloatRange) = FloatRange($fn(r.start), $fn(r.step), r.len, $fn(r.divisor))
end
end
Expand Down
4 changes: 2 additions & 2 deletions base/linalg/tridiag.jl
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ function solve!(X::StridedMatrix, M::Tridiagonal, B::StridedMatrix)
size(X) == size(B) || throw(DimensionMismatch(""))
m, n = size(B)
for j = 1:n
r = Range1((j-1)*m+1,m)
r = ((j-1)*m+1):((j-1)*m+m)
solve!(X, r, M, B, r)
end
X
Expand Down Expand Up @@ -345,7 +345,7 @@ function mult(X::StridedMatrix, M::Tridiagonal, B::StridedMatrix)
size(X) == size(B) || throw(DimensionMismatch(""))
m, n = size(B)
for j = 1:n
r = Range1((j-1)*m+1,m)
r = ((j-1)*m+1):((j-1)*m+m)
mult(X, r, M, B, r)
end
X
Expand Down
126 changes: 59 additions & 67 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,13 @@ immutable Range{T<:Real} <: Ranges{T}
end
Range{T}(start::T, step, len::Integer) = Range{T}(start, step, len)

immutable Range1{T<:Real} <: Ranges{T}
immutable Range1{T} <: Ranges{T}
start::T
len::Int

function Range1(start::T, len::Int)
if len < 0; error("length must be non-negative"); end
new(start, len)
end
Range1(start::T, len::Integer) = Range1(start, int(len))
stop::T

# TODO: this is a hack to elide the len<0 check for colon.
# should store start and stop for integer ranges instead
Range1(start::T, len::Integer, _) = new(start, int(len))
Range1(start, stop) = new(start, ifelse(stop>=start, stop, oftype(T, start-1)))
end
Range1{T}(start::T, len::Integer) = Range1{T}(start, len)
Range1{T}(start::T, stop::T) = Range1{T}(start, stop)

immutable FloatRange{T<:FloatingPoint} <: Ranges{T}
start::T
Expand All @@ -49,22 +41,7 @@ function colon{T<:Integer}(start::T, step::T, stop::T)
Range{T}(start, step, max(0, 1 + fld(stop-start, step)))
end

colon{T<:Integer}(start::T, stop::T) =
Range1{T}(start, ifelse(stop<start, 0, int(stop-start+1)))

if Int === Int32
colon{T<:Union(Int8,Int16,Int32,Uint8,Uint16)}(start::T, stop::T) =
Range1{T}(start,
ifelse(stop < start, 0,
checked_add(checked_sub(convert(Int,stop),convert(Int,start)),1)),
0) # hack to elide negative length check
else
colon{T<:Union(Int8,Int16,Int32,Int64,Uint8,Uint16,Uint32)}(start::T, stop::T) =
Range1{T}(start,
ifelse(stop < start, 0,
checked_add(checked_sub(convert(Int,stop),convert(Int,start)),1)),
0) # hack to elide negative length check
end
colon{T<:Integer}(start::T, stop::T) = Range1{T}(start, stop)

function colon{T<:Real}(start::T, step::T, stop::T)
step != 0 || error("step cannot be zero in colon syntax")
Expand All @@ -91,23 +68,22 @@ function colon{T<:Real}(start::T, step::T, stop::T)
end
Range(start, step, len)
end

function colon{T<:Real}(start::T, stop::T)
if stop < start
len = 0
else
if T <: FloatingPoint
nf = stop - start + 1
if T <: FloatingPoint
n = round(nf)
len = abs(n-nf) < eps(n)*3 ? itrunc(n) : itrunc(nf)
else
n = nf
len = itrunc(n)
n = round(nf)
len = abs(n-nf) < eps(n)*3 ? itrunc(n) : itrunc(nf)
stop = oftype(start, start+(len-1))
if stop == stop+1
# Range1 doesn't work beyond maxintfloat
error("end point ", stop, " is too large for Range1")
end
if n >= typemax(Int)
error("length ",n," is too large")
end
end
Range1(start, len)
Range1(start, stop)
end

colon(start::Real, step::Real, stop::Real) = colon(promote(start, step, stop)...)
Expand Down Expand Up @@ -161,13 +137,25 @@ similar(r::Ranges, T::Type, dims::Dims) = Array(T, dims)

length(r::Ranges) = integer(r.len)
size(r::Ranges) = (length(r),)
isempty(r::Ranges) = r.len==0
first(r::Ranges) = r.start
first(r::FloatRange) = r.start/r.divisor
last{T}(r::Range1{T}) = oftype(T, r.start + r.len-1)
last{T}(r::Range1{T}) = r.stop
last{T}(r::Range{T}) = oftype(T, r.start + (r.len-1)*r.step)
last{T}(r::FloatRange{T}) = oftype(T, (r.start + (r.len-1)*r.step)/r.divisor)

length{T<:Integer}(r::Range1{T}) = int(r.stop - r.start + 1)
length(r::Range1) = int(itrunc(r.stop - r.start)+1)

if Int === Int32
function length{T<:Union(Int8,Int16,Int32,Uint8,Uint16)}(r::Range1{T})
checked_add(checked_sub(convert(Int,r.stop), convert(Int,r.start)), 1)
end
else
function length{T<:Union(Int8,Int16,Int32,Int64,Uint8,Uint16,Uint32)}(r::Range1{T})
checked_add(checked_sub(convert(Int,r.stop), convert(Int,r.start)), 1)
end
end

step(r::Range) = r.step
step(r::Range1) = one(r.start)
step(r::FloatRange) = r.step/r.divisor
Expand All @@ -180,39 +168,42 @@ maximum(r::Ranges) = isempty(r) ? error("range must be non-empty") : step(r) > 0
ctranspose(r::Ranges) = [x for _=1, x=r]
transpose(r::Ranges) = r'

# Ranges are intended to be immutable
# Ranges are immutable
copy(r::Ranges) = r

getindex(r::Ranges, i::Real) = getindex(r, to_index(i))

function getindex{T}(r::Ranges{T}, i::Integer)
1 <= i <= r.len || error(BoundsError)
1 <= i <= length(r) || error(BoundsError)
oftype(T, r.start + (i-1)*step(r))
end
function getindex{T}(r::FloatRange{T}, i::Integer)
1 <= i <= r.len || error(BoundsError)
1 <= i <= length(r) || error(BoundsError)
oftype(T, (r.start + (i-1)*r.step)/r.divisor)
end

function getindex(r::Range1, s::Range1{Int})
if s.len > 0
if !(1 <= last(s) <= r.len)
sl = length(s)
if sl > 0
if !(1 <= last(s) <= length(r))
throw(BoundsError())
end
Range1(r[s.start], s.len)
st = r[s.start]
else
Range1(r.start + s.start-1, s.len)
st = oftype(r.start, r.start + s.start-1)
end
Range1(st, oftype(st,st+sl-1))
end

function getindex(r::Ranges, s::Ranges{Int})
if s.len > 0
if !(1 <= last(s) <= r.len)
sl = length(s)
if sl > 0
if !(1 <= last(s) <= length(r))
throw(BoundsError())
end
Range(r[s.start], step(r)*step(s), s.len)
Range(r[s.start], step(r)*step(s), sl)
else
Range(r.start + (s.start-1)*step(r), step(r)*step(s), s.len)
Range(r.start + (s.start-1)*step(r), step(r)*step(s), sl)
end
end

Expand All @@ -226,15 +217,14 @@ show{T<:FloatingPoint}(io::IO, r::Range{T}) = invoke(show, (IO,Any), io, r)

start(r::Ranges) = 0
next{T}(r::Range{T}, i) = (oftype(T, r.start + i*step(r)), i+1)
next{T}(r::Range1{T}, i) = (oftype(T, r.start + i), i+1)
next{T}(r::FloatRange{T}, i) = (oftype(T, (r.start + i*r.step)/r.divisor), i+1)
done(r::Ranges, i) = (length(r) <= i)

# though these look very similar to the above, for some reason LLVM generates
# much better code for these.
start{T<:Integer}(r::Range1{T}) = r.start
next{T<:Integer}(r::Range1{T}, i) = (i, oftype(T, i+1))
done{T<:Integer}(r::Range1{T}, i) = i==oftype(T, r.start+r.len)
start(r::Range1) = r.start
next{T}(r::Range1{T}, i) = (i, oftype(T, i+1))
done{T}(r::Range1{T}, i) = i==oftype(T, r.stop+1)

isequal{T<:Ranges}(r::T, s::T) =
(first(r)==first(s)) & (step(r)==step(s)) & (length(r)==length(s))
Expand Down Expand Up @@ -278,7 +268,7 @@ intersect{T<:Integer}(r::Range1{T}, i::Integer) = intersect(i, r)

function intersect{T1<:Integer, T2<:Integer}(r::Range1{T1}, s::Range{T2})
if length(s) == 0
Range1(first(r), 0)
Range1(first(r), oftype(T1, first(r)-1))
elseif step(s) == 0
intersect(first(s), r)
elseif step(s) < 0
Expand Down Expand Up @@ -392,38 +382,40 @@ end

## linear operations on ranges ##

-(r::Ranges) = Range(-r.start, -step(r), r.len)
-(r::Ranges) = Range(-r.start, -step(r), length(r))
-(r::FloatRange) = FloatRange(-r.start, -r.step, r.len, r.divisor)

+(x::Real, r::Range1) = Range1(x + r.start, r.len)
+(x::Real, r::Range1) = Range1(x + r.start, x + last(r))
+(x::Real, r::Range) = Range(x + r.start, r.step, r.len)
+(x::Real, r::FloatRange) = FloatRange(r.divisor*x + r.start, r.step, r.len, r.divisor)
+(r::Ranges, x::Real) = x + r
+(r::FloatRange, x::Real) = x + r

-(x::Real, r::Ranges) = Range(x - r.start, -step(r), r.len)
-(x::Real, r::Ranges) = Range(x - r.start, -step(r), length(r))
-(x::Real, r::FloatRange) = FloatRange(r.divisor*x - r.start, -r.step, r.len, r.divisor)
-(r::Range1, x::Real) = Range1(r.start-x, r.len)
-(r::Range1, x::Real) = Range1(r.start-x, last(r)-x)
-(r::Range , x::Real) = Range(r.start-x, r.step, r.len)
-(r::FloatRange, x::Real) = FloatRange(r.start - r.divisor*x, r.step, r.len, r.divisor)

.*(x::Real, r::Ranges) = Range(x*r.start, x*step(r), r.len)
.*(x::Real, r::Ranges) = Range(x*r.start, x*step(r), length(r))
.*(x::Real, r::FloatRange) = FloatRange(x*r.start, x*r.step, r.len, r.divisor)
.*(r::Ranges, x::Real) = x .* r
.*(r::FloatRange, x::Real) = x .* r

./(r::Ranges, x::Real) = Range(r.start/x, step(r)/x, r.len)
./(r::Ranges, x::Real) = Range(r.start/x, step(r)/x, length(r))
./(r::FloatRange, x::Real) = FloatRange(r.start/x, r.step/x, r.len, r.divisor)

# TODO: better implementations for FloatRanges?
function +(r1::Ranges, r2::Ranges)
r1.len == r2.len || error("argument dimensions must match")
Range(r1.start+r2.start, step(r1)+step(r2), r1.len)
r1l = length(r1)
r1l == length(r2) || error("argument dimensions must match")
Range(r1.start+r2.start, step(r1)+step(r2), r1l)
end

function -(r1::Ranges, r2::Ranges)
r1.len == r2.len || error("argument dimensions must match")
Range(r1.start-r2.start, step(r1)-step(r2), r1.len)
r1l = length(r1)
r1l == length(r2) || error("argument dimensions must match")
Range(r1.start-r2.start, step(r1)-step(r2), r1l)
end

## non-linear operations on ranges ##
Expand Down Expand Up @@ -462,7 +454,7 @@ function vcat{T}(rs::Ranges{T}...)
return a
end

reverse(r::Ranges) = Range(last(r), -step(r), r.len)
reverse(r::Ranges) = Range(last(r), -step(r), length(r))
reverse(r::FloatRange) = FloatRange(last(r), -r.step, r.len, r.divisor)

## sorting ##
Expand Down
4 changes: 0 additions & 4 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -866,10 +866,6 @@ typealias Foo2919 Int
type Baz2919; Foo2919::Foo2919; end
@test Baz2919(3).Foo2919 === 3

# issue #2959
@test 1.0:1.5 == 1.0:1.0:1.5 == 1.0:1.0
@test 1.0:(.3-.1)/.1 == 1.0:2.0

# issue #2982
module M2982
abstract U
Expand Down
29 changes: 7 additions & 22 deletions test/ranges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,28 +105,9 @@ r = (-4*int64(maxintfloat(is(Int,Int32) ? Float32 : Float64))):5
@test length(1:4:typemax(Int)) == div(typemax(Int),4) + 1

# overflow in length
@test_throws 0:typemax(Int)
@test_throws typemin(Int):typemax(Int)
@test_throws -1:typemax(Int)-1

# parity between ranges and for loops (see issue #5355)

@test length(2.0^53:(2.0^53+2)) == 3
let s = 0
r = 2.0^53:(2.0^53+2)
for i in r
s += 1
@test s <= 3
end
@test s == 3

s = 0
for i in 2.0^53:(2.0^53+2)
s += 1
@test s <= 3
end
@test s == 3
end
@test_throws length(0:typemax(Int))
@test_throws length(typemin(Int):typemax(Int))
@test_throws length(-1:typemax(Int)-1)

let s = 0
# loops ending at typemax(Int)
Expand Down Expand Up @@ -232,3 +213,7 @@ let
end
end
end

# issue #2959
@test 1.0:1.5 == 1.0:1.0:1.5 == 1.0:1.0
@test 1.0:(.3-.1)/.1 == 1.0:2.0

0 comments on commit c4f8295

Please sign in to comment.