Skip to content

Commit

Permalink
LinSpace: make linspace return a range-like type.
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanKarpinski committed Apr 13, 2015
1 parent b7df3ad commit 42db285
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 73 deletions.
45 changes: 0 additions & 45 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -246,51 +246,6 @@ function one{T}(x::AbstractMatrix{T})
eye(T, m)
end

linspace(start::Integer, stop::Integer, n::Integer) =
linspace(float(start), float(stop), n)
function linspace(start::Real, stop::Real, n::Integer)
(start, stop) = promote(start, stop)
T = typeof(start)
a = Array(T, Int(n))
if n == 1
a[1] = start
return a
end
n -= 1
S = promote_type(T, Float64)
for i = 0:n
a[i+1] = start*(convert(S, (n-i))/n) + stop*(convert(S, i)/n)
end
a
end
linspace(start::Real, stop::Real) = linspace(start, stop, 100)

function linspace{T<:FloatingPoint}(start::T, stop::T, n::Int)
n == 1 && return [start]
n -= 1
a0, b = rat(start)
a = convert(T,a0)
if a/convert(T,b) == start
c0, d = rat(stop)
c = convert(T,c0)
if c/convert(T,d) == stop
e = lcm(b,d)
a *= div(e,b)
c *= div(e,d)
ne = convert(T,n*e)
if a*n/ne == start && c*n/ne == stop
return [ (a*(n-k) + c*k)/ne for k=0:n ]
end
end
end
return [ start*((n-k)/n) + stop*(k/n) for k=0:n ]
end
linspace(start::FloatingPoint, stop::FloatingPoint, n::Integer) =
linspace(promote(start, stop)..., Int(n))

logspace(start::Real, stop::Real, n::Integer) = 10.^linspace(start, stop, n)
logspace(start::Real, stop::Real) = logspace(start, stop, 50)

## Conversions ##

convert{T,n}(::Type{Array{T}}, x::Array{T,n}) = x
Expand Down
2 changes: 2 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -522,3 +522,5 @@ export float32_isvalid, float64_isvalid
@deprecate parseint(s,base) parse(Int, s, base)
@deprecate parseint(T::Type, s) parse(T, s)
@deprecate parseint(T::Type, s, base) parse(T, s, base)

@deprecate linrange linspace
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export
IO,
IOBuffer,
IOStream,
LinSpace,
LocalProcess,
LowerTriangular,
MathConst,
Expand Down
65 changes: 57 additions & 8 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,44 @@ range(a::FloatingPoint, st::FloatingPoint, len::Integer) = FloatRange(a,st,len,o
range(a::Real, st::FloatingPoint, len::Integer) = FloatRange(float(a), st, len, one(st))
range(a::FloatingPoint, st::Real, len::Integer) = FloatRange(a, float(st), len, one(a))

linrange(a::Real, b::Real, len::Integer) =
len >= 2 ? range(a, (b-a)/(len-1), len) :
len == 1 && a == b ? range(a, zero((b-a)/(len-1)), 1) :
throw(ArgumentError("invalid range length"))
## linspace and logspace

immutable LinSpace{T<:FloatingPoint} <: Range{T}
start::T
stop::T
len::Int
divisor::T
end
LinSpace(start::FloatingPoint, stop::FloatingPoint, len::Real, divisor::Real) =
LinSpace{promote_type(typeof(start),typeof(stop),typeof(divisor))}(start,stop,len,divisor)

function linspace{T<:FloatingPoint}(start::T, stop::T, len::Int)
0 <= len || error("invalid length: linspace($start, $stop, $len)")
1 != len || start == stop || error("start != stop with len == 1: linspace($start, $stop, $len)")
n = len - 1
a0, b = rat(start)
a = convert(T,a0)
if 0 < n && a/convert(T,b) == start
c0, d = rat(stop)
c = convert(T,c0)
if c/convert(T,d) == stop
e = lcm(b,d)
a *= div(e,b)
c *= div(e,d)
ne = convert(T,n*e)
if a*n/ne == start && c*n/ne == stop
return LinSpace(a, c, len, ne)
end
end
end
return LinSpace(start, stop, len, max(1,n))
end
linspace(start::Real, stop::Real, len::Real) =
linspace(promote(FloatingPoint(start), FloatingPoint(stop))..., Int(len))

show(io::IO, r::LinSpace) = print(io, "linspace($(first(r)),$(last(r)),$(length(r)))")

logspace(start::Real, stop::Real, n::Integer=50) = 10.^linspace(start, stop, n)

## interface implementations

Expand All @@ -178,18 +212,21 @@ size(r::Range) = (length(r),)
isempty(r::StepRange) =
(r.start != r.stop) & ((r.step > zero(r.step)) != (r.stop > r.start))
isempty(r::UnitRange) = r.start > r.stop
isempty(r::FloatRange) = length(r)==0
isempty(r::FloatRange) = length(r) == 0
isempty(r::LinSpace) = length(r) == 0

step(r::StepRange) = r.step
step(r::UnitRange) = 1
step(r::FloatRange) = r.step/r.divisor
step(r::LinSpace) = (r.stop - r.start)/r.divisor

function length(r::StepRange)
n = Integer(div(r.stop+r.step - r.start, r.step))
isempty(r) ? zero(n) : n
end
length(r::UnitRange) = Integer(r.stop - r.start + 1)
length(r::FloatRange) = Integer(r.len)
length(r::LinSpace) = r.len

function length{T<:Union(Int,UInt,Int64,UInt64)}(r::StepRange{T})
isempty(r) && return zero(T)
Expand Down Expand Up @@ -220,11 +257,13 @@ let smallint = (Int === Int64 ?
end

first{T}(r::OrdinalRange{T}) = convert(T, r.start)
first(r::FloatRange) = r.start/r.divisor
first{T}(r::FloatRange{T}) = convert(T, r.start/r.divisor)
first{T}(r::LinSpace{T}) = convert(T, (r.len-1)*r.start/r.divisor)

last{T}(r::StepRange{T}) = r.stop
last(r::UnitRange) = r.stop
last{T}(r::FloatRange{T}) = convert(T, (r.start + (r.len-1)*r.step)/r.divisor)
last{T}(r::LinSpace{T}) = convert(T, (r.len-1)*r.stop/r.divisor)

minimum(r::UnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : first(r)
maximum(r::UnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : last(r)
Expand All @@ -241,8 +280,14 @@ copy(r::Range) = r
## iteration

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

start(r::LinSpace) = 1
done(r::LinSpace, i::Int) = length(r) < i
next{T}(r::LinSpace{T}, i::Int) =
(convert(T, ((r.len-i)*r.start + (i-1)*r.stop)/r.divisor), i+1)

# NOTE: For ordinal ranges, we assume start+step might be from a
# lifted domain (e.g. Int8+Int8 => Int); use that for iterating.
Expand All @@ -268,6 +313,10 @@ function getindex{T}(r::FloatRange{T}, i::Integer)
1 <= i <= length(r) || throw(BoundsError())
convert(T, (r.start + (i-1)*r.step)/r.divisor)
end
function getindex{T}(r::LinSpace{T}, i::Integer)
1 <= i <= length(r) || throw(BoundsError())
convert(T, ((r.len-i)*r.start + (i-1)*r.stop)/r.divisor)
end

function check_indexingrange(s, r)
sl = length(s)
Expand Down
40 changes: 20 additions & 20 deletions test/ranges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -232,22 +232,22 @@ end

# tricky floating-point ranges

@test [0.1:0.1:0.3;] == linspace(0.1,0.3,3) == [1:3;]./10
@test [0.0:0.1:0.3;] == linspace(0.0,0.3,4) == [0:3;]./10
@test [0.3:-0.1:-0.1;] == linspace(0.3,-0.1,5) == [3:-1:-1;]./10
@test [0.1:-0.1:-0.3;] == linspace(0.1,-0.3,5) == [1:-1:-3;]./10
@test [0.0:0.1:1.0;] == linspace(0.0,1.0,11) == [0:10;]./10
@test [0.0:-0.1:1.0;] == linspace(0.0,1.0,0) == []
@test [0.0:0.1:-1.0;] == linspace(0.0,-1.0,0) == []
@test [0.0:-0.1:-1.0;] == linspace(0.0,-1.0,11) == [0:-1:-10;]./10
@test [1.0:1/49:27.0;] == linspace(1.0,27.0,1275) == [49:1323;]./49
@test [0.0:0.7:2.1;] == linspace(0.0,2.1,4) == [0:7:21;]./10
@test [0.0:1.1:3.3;] == linspace(0.0,3.3,4) == [0:11:33;]./10
@test [0.1:1.1:3.4;] == linspace(0.1,3.4,4) == [1:11:34;]./10
@test [0.0:1.3:3.9;] == linspace(0.0,3.9,4) == [0:13:39;]./10
@test [0.1:1.3:4.0;] == linspace(0.1,4.0,4) == [1:13:40;]./10
@test [1.1:1.1:3.3;] == linspace(1.1,3.3,3) == [11:11:33;]./10
@test [0.3:0.1:1.1;] == linspace(0.3,1.1,9) == [3:1:11;]./10
@test [0.1:0.1:0.3;] == [linspace(0.1,0.3,3);] == [1:3;]./10
@test [0.0:0.1:0.3;] == [linspace(0.0,0.3,4);] == [0:3;]./10
@test [0.3:-0.1:-0.1;] == [linspace(0.3,-0.1,5);] == [3:-1:-1;]./10
@test [0.1:-0.1:-0.3;] == [linspace(0.1,-0.3,5);] == [1:-1:-3;]./10
@test [0.0:0.1:1.0;] == [linspace(0.0,1.0,11);] == [0:10;]./10
@test [0.0:-0.1:1.0;] == [linspace(0.0,1.0,0);] == []
@test [0.0:0.1:-1.0;] == [linspace(0.0,-1.0,0);] == []
@test [0.0:-0.1:-1.0;] == [linspace(0.0,-1.0,11);] == [0:-1:-10;]./10
@test [1.0:1/49:27.0;] == [linspace(1.0,27.0,1275);] == [49:1323;]./49
@test [0.0:0.7:2.1;] == [linspace(0.0,2.1,4);] == [0:7:21;]./10
@test [0.0:1.1:3.3;] == [linspace(0.0,3.3,4);] == [0:11:33;]./10
@test [0.1:1.1:3.4;] == [linspace(0.1,3.4,4);] == [1:11:34;]./10
@test [0.0:1.3:3.9;] == [linspace(0.0,3.9,4);] == [0:13:39;]./10
@test [0.1:1.3:4.0;] == [linspace(0.1,4.0,4);] == [1:13:40;]./10
@test [1.1:1.1:3.3;] == [linspace(1.1,3.3,3);] == [11:11:33;]./10
@test [0.3:0.1:1.1;] == [linspace(0.3,1.1,9);] == [3:1:11;]./10

@test [0.0:1.0:5.5;] == [0:10:55;]./10
@test [0.0:-1.0:0.5;] == []
Expand Down Expand Up @@ -275,7 +275,7 @@ for T = (Float32, Float64,),# BigFloat),
vals = T[a:s:a+(n-1)*s;]./den
r = start:step:stop
@test [r;] == vals
@test linspace(start, stop, n) == vals
n == 1 || @test [linspace(start, stop, length(r));] == vals
# issue #7420
n = length(r)
@test [r[1:n];] == [r;]
Expand Down Expand Up @@ -353,13 +353,13 @@ r = -0.004532318104333742:1.2597349521122731e-5:0.008065031416788989
@test_throws BoundsError r[0:10]
@test_throws BoundsError r[1:10000]

r = linrange(1/3,5/7,6)
r = linspace(1/3,5/7,6)
@test length(r) == 6
@test r[1] == 1/3
@test abs(r[end] - 5/7) <= eps(5/7)
r = linrange(0.25,0.25,1)
r = linspace(0.25,0.25,1)
@test length(r) == 1
@test_throws Exception linrange(0.25,0.5,1)
@test_throws Exception linspace(0.25,0.5,1)

# issue #7426
@test [typemax(Int):1:typemax(Int);] == [typemax(Int)]
Expand Down

0 comments on commit 42db285

Please sign in to comment.