Skip to content

Commit 4fd38a2

Browse files
authored
fix deprecations (#32)
* fix deprecations * test on 0.6
1 parent 3a355e5 commit 4fd38a2

File tree

11 files changed

+91
-86
lines changed

11 files changed

+91
-86
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
language: julia
33
os:
44
- linux
5-
- osx
65
julia:
6+
- 0.6
77
- nightly
88
notifications:
99
email: false

REQUIRE

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
julia 0.6.0-pre
1+
julia 0.6
2+
Compat 0.30.0

src/BlockArrays.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export PseudoBlockArray, PseudoBlockMatrix, PseudoBlockVector, PseudoBlockVecOrM
1111

1212
import Base: @propagate_inbounds, Array
1313
using Base.Cartesian
14+
using Compat
1415

1516
include("abstractblockarray.jl")
1617

src/abstractblockarray.jl

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const AbstractBlockVecOrMat{T} = Union{AbstractBlockMatrix{T}, AbstractBlockVect
2323

2424
block2string(b, s) = string(join(map(string,b), '×'), "-blocked ", Base.dims2string(s))
2525
Base.summary(a::AbstractBlockArray) = string(block2string(nblocks(a), size(a)), " ", typeof(a))
26-
Base.similar{T}(block_array::AbstractBlockArray{T}) = similar(block_array, T)
26+
Base.similar(block_array::AbstractBlockArray{T}) where {T} = similar(block_array, T)
2727
Base.IndexStyle(::Type{<:AbstractBlockArray}) = IndexCartesian()
2828

2929
"""
@@ -47,12 +47,12 @@ julia> nblocks(A, 3, 2)
4747
"""
4848
nblocks(block_array::AbstractBlockArray, i::Int) = nblocks(block_array)[i]
4949

50-
function nblocks{N}(block_array::AbstractBlockArray, i::Vararg{Int, N})
50+
function nblocks(block_array::AbstractBlockArray, i::Vararg{Int, N}) where {N}
5151
if N == 0
5252
throw(error("nblocks(A) not implemented"))
5353
end
5454
b = nblocks(block_array)
55-
return ntuple(k-> b[i[k]], Val{N})
55+
return ntuple(k-> b[i[k]], Val(N))
5656
end
5757

5858

@@ -71,7 +71,7 @@ julia> blocksize(A, 2, 1, 3)
7171
(4, 1, 2)
7272
```
7373
"""
74-
function blocksize{T, N}(X, A::AbstractBlockArray{T,N}, ::Vararg{Int, N})
74+
function blocksize(X, A::AbstractBlockArray{T,N}, ::Vararg{Int, N}) where {T,N}
7575
throw(error("blocksize for ", typeof(A), "is not implemented"))
7676
end
7777

@@ -94,14 +94,14 @@ julia> A[Block(1, 1)]
9494
1.0 1.0
9595
```
9696
"""
97-
immutable Block{N, T}
97+
struct Block{N, T}
9898
n::NTuple{N, T}
9999
end
100100

101-
Block{N, T}(n::Vararg{T, N}) = Block{N, T}(n)
101+
Block(n::Vararg{T, N}) where {N,T} = Block{N, T}(n)
102102

103-
@inline function Block{N, T}(blocks::NTuple{N, Block{1, T}})
104-
Block{N, T}(ntuple(i -> blocks[i].n[1], Val{N}))
103+
@inline function Block(blocks::NTuple{N, Block{1, T}}) where {N,T}
104+
Block{N, T}(ntuple(i -> blocks[i].n[1], Val(N)))
105105
end
106106

107107
"""
@@ -131,7 +131,7 @@ julia> A[Block(1, 2)]
131131
5
132132
```
133133
"""
134-
function getblock{T, N}(A::AbstractBlockArray{T,N}, ::Vararg{Int, N})
134+
function getblock(A::AbstractBlockArray{T,N}, ::Vararg{Int, N}) where {T,N}
135135
throw("getblock for ", typeof(A), "is not implemented")
136136
end
137137

@@ -158,11 +158,11 @@ julia> x
158158
1.0 1.0
159159
```
160160
"""
161-
getblock!{T, N}(X, A::AbstractBlockArray{T,N}, ::Vararg{Int, N}) = throw("getblock! for ", typeof(A), "is not implemented")
161+
getblock!(X, A::AbstractBlockArray{T,N}, ::Vararg{Int, N}) where {T,N} = throw("getblock! for ", typeof(A), "is not implemented")
162162

163-
@inline getblock!{T, N}(X, A::AbstractBlockArray{T,N}, block::Block{N}) = getblock!(X, A, block.n...)
163+
@inline getblock!(X, A::AbstractBlockArray{T,N}, block::Block{N}) where {T,N} = getblock!(X, A, block.n...)
164164
@inline getblock!(X, A::AbstractBlockVector, block::Block{1}) = getblock!(X, A, block.n[1])
165-
@inline getblock!{T, N}(X, A::AbstractBlockArray{T, N}, block::Vararg{Block{1}, N}) = getblock!(X, A, (Block(block).n)...)
165+
@inline getblock!(X, A::AbstractBlockArray{T, N}, block::Vararg{Block{1}, N}) where {T,N} = getblock!(X, A, (Block(block).n)...)
166166

167167
"""
168168
setblock!(A, v, inds...)
@@ -184,24 +184,24 @@ julia> A
184184
3.0 4.0 │ 0.0
185185
```
186186
"""
187-
setblock!{T, N}(A::AbstractBlockArray{T,N}, v, ::Vararg{Int, N}) = throw("setblock! for ", typeof(A), "is not implemented")
187+
setblock!(A::AbstractBlockArray{T,N}, v, ::Vararg{Int, N}) where {T,N} = throw("setblock! for ", typeof(A), "is not implemented")
188188

189-
@inline setblock!{T, N}(A::AbstractBlockArray{T, N}, v, block::Block{N}) = setblock!(A, v, block.n...)
189+
@inline setblock!(A::AbstractBlockArray{T, N}, v, block::Block{N}) where {T,N} = setblock!(A, v, block.n...)
190190
@inline setblock!(A::AbstractBlockVector, v, block::Block{1}) = setblock!(A, v, block.n[1])
191-
@inline setblock!{T, N}(A::AbstractBlockArray{T, N}, v, block::Vararg{Block{1}, N}) = setblock!(A, v, (Block(blockindex).n)...)
191+
@inline setblock!(A::AbstractBlockArray{T, N}, v, block::Vararg{Block{1}, N}) where {T,N} = setblock!(A, v, (Block(block).n)...)
192192

193193

194194
"""
195195
BlockBoundsError([A], [inds...])
196196
197197
Thrown when a block indexing operation into a block array, `A`, tried to access an out-of-bounds block, `inds`.
198198
"""
199-
immutable BlockBoundsError <: Exception
199+
struct BlockBoundsError <: Exception
200200
a::Any
201201
i::Any
202202
BlockBoundsError() = new()
203203
BlockBoundsError(a::AbstractBlockArray) = new(a)
204-
BlockBoundsError(a::AbstractBlockArray, i::ANY) = new(a,i)
204+
BlockBoundsError(a::AbstractBlockArray, @nospecialize(i)) = new(a,i)
205205
end
206206

207207
function Base.showerror(io::IO, ex::BlockBoundsError)
@@ -232,15 +232,15 @@ ERROR: BlockBoundsError: attempt to access 2×2-blocked 2×3 BlockArrays.BlockAr
232232
[...]
233233
```
234234
"""
235-
@inline function blockcheckbounds{T, N}(A::AbstractBlockArray{T, N}, i::Vararg{Int, N})
235+
@inline function blockcheckbounds(A::AbstractBlockArray{T, N}, i::Vararg{Int, N}) where {T,N}
236236
if blockcheckbounds(Bool, A, i...)
237237
return
238238
else
239239
throw(BlockBoundsError(A, i))
240240
end
241241
end
242242

243-
@inline function blockcheckbounds{T, N}(::Type{Bool}, A::AbstractBlockArray{T, N}, i::Vararg{Int, N})
243+
@inline function blockcheckbounds(::Type{Bool}, A::AbstractBlockArray{T, N}, i::Vararg{Int, N}) where {T,N}
244244
n = nblocks(A)
245245
k = 0
246246
for idx in 1:N # using enumerate here will allocate
@@ -275,9 +275,9 @@ julia> Array(A)
275275
function Base.Array(A::AbstractBlockArray) end
276276

277277
# Convert to @generated...
278-
@propagate_inbounds Base.getindex{T, N}( block_arr::AbstractBlockArray{T, N}, block::Block{N}) = getblock(block_arr, block.n...)
279-
@propagate_inbounds Base.setindex!{T, N}(block_arr::AbstractBlockArray{T, N}, v, block::Block{N}) = setblock!(block_arr, v, block.n...)
278+
@propagate_inbounds Base.getindex( block_arr::AbstractBlockArray{T, N}, block::Block{N}) where {T,N} = getblock(block_arr, block.n...)
279+
@propagate_inbounds Base.setindex!(block_arr::AbstractBlockArray{T, N}, v, block::Block{N}) where {T,N} = setblock!(block_arr, v, block.n...)
280280
@propagate_inbounds Base.getindex( block_arr::AbstractBlockVector, block::Block{1}) = getblock(block_arr, block.n[1])
281281
@propagate_inbounds Base.setindex!(block_arr::AbstractBlockVector, v, block::Block{1}) = setblock!(block_arr, v, block.n[1])
282-
@inline Base.getindex{T, N}(block_arr::AbstractBlockArray{T,N}, block::Vararg{Block{1}, N}) = getblock(block_arr, (Block(block).n)...)
283-
@inline Base.setindex!{T, N}(block_arr::AbstractBlockArray{T,N}, v, block::Vararg{Block{1}, N}) = setblock!(block_arr, v, (Block(block).n)...)
282+
@inline Base.getindex(block_arr::AbstractBlockArray{T,N}, block::Vararg{Block{1}, N}) where {T,N} = getblock(block_arr, (Block(block).n)...)
283+
@inline Base.setindex!(block_arr::AbstractBlockArray{T,N}, v, block::Vararg{Block{1}, N}) where {T,N} = setblock!(block_arr, v, (Block(block).n)...)

src/blockarray.jl

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,17 @@ julia> BlockArray(Matrix{Float64}, [1,3], [2,2])
4949
#undef │ #undef #undef #undef │
5050
```
5151
"""
52-
@inline function BlockArray{T, N, R <: AbstractArray{T,N}}(::Type{R}, block_sizes::Vararg{Vector{Int}, N})
52+
@inline function BlockArray(::Type{R}, block_sizes::Vararg{Vector{Int}, N}) where {T,N,R <: AbstractArray{T, N}}
5353
BlockArray(R, BlockSizes(block_sizes...))
5454
end
5555

56-
function BlockArray{T, N, R <: AbstractArray{T,N}}(::Type{R}, block_sizes::BlockSizes{N})
56+
function BlockArray(::Type{R}, block_sizes::BlockSizes{N}) where {T,N,R <: AbstractArray{T, N}}
5757
n_blocks = nblocks(block_sizes)
5858
blocks = Array{R, N}(n_blocks)
5959
BlockArray{T,N,R}(blocks, block_sizes)
6060
end
6161

62-
function BlockArray{T, N}(arr::AbstractArray{T, N}, block_sizes::Vararg{Vector{Int}, N})
62+
function BlockArray(arr::AbstractArray{T, N}, block_sizes::Vararg{Vector{Int}, N}) where {T,N}
6363
for i in 1:N
6464
if sum(block_sizes[i]) != size(arr, i)
6565
throw(DimensionMismatch("block size for dimension $i: $(block_sizes[i]) does not sum to the array size: $(size(arr, i))"))
@@ -68,7 +68,7 @@ function BlockArray{T, N}(arr::AbstractArray{T, N}, block_sizes::Vararg{Vector{I
6868
BlockArray(arr, BlockSizes(block_sizes...))
6969
end
7070

71-
@generated function BlockArray{T, N}(arr::AbstractArray{T, N}, block_sizes::BlockSizes{N})
71+
@generated function BlockArray(arr::AbstractArray{T, N}, block_sizes::BlockSizes{N}) where {T,N}
7272
return quote
7373
block_arr = BlockArray(typeof(arr), block_sizes)
7474
@nloops $N i i->(1:nblocks(block_sizes, i)) begin
@@ -82,25 +82,25 @@ end
8282
end
8383

8484

85-
BlockArray{T, N, R <: AbstractArray{T,N}}(::Type{R}, block_sizes::Vararg{AbstractVector{Int}, N}) =
85+
BlockArray(::Type{R}, block_sizes::Vararg{AbstractVector{Int}, N}) where {T,N,R <: AbstractArray{T, N}} =
8686
BlockArray(R, Vector{Int}.(block_sizes)...)
8787

88-
BlockArray{T, N}(arr::AbstractArray{T, N}, block_sizes::Vararg{AbstractVector{Int}, N}) =
88+
BlockArray(arr::AbstractArray{T, N}, block_sizes::Vararg{AbstractVector{Int}, N}) where {T,N} =
8989
BlockArray(arr, Vector{Int}.(block_sizes)...)
9090

9191
################################
9292
# AbstractBlockArray Interface #
9393
################################
9494

9595
@inline nblocks(block_array::BlockArray) = nblocks(block_array.block_sizes)
96-
@inline blocksize{T, N}(block_array::BlockArray{T,N}, i::Vararg{Int, N}) = blocksize(block_array.block_sizes, i)
96+
@inline blocksize(block_array::BlockArray{T,N}, i::Vararg{Int, N}) where {T,N} = blocksize(block_array.block_sizes, i)
9797

98-
@inline function getblock{T, N}(block_arr::BlockArray{T,N}, block::Vararg{Int, N})
98+
@inline function getblock(block_arr::BlockArray{T,N}, block::Vararg{Int, N}) where {T,N}
9999
@boundscheck blockcheckbounds(block_arr, block...)
100100
block_arr.blocks[block...]
101101
end
102102

103-
@inline function Base.getindex{T, N}(block_arr::BlockArray{T,N}, blockindex::BlockIndex{N})
103+
@inline function Base.getindex(block_arr::BlockArray{T,N}, blockindex::BlockIndex{N}) where {T,N}
104104
@boundscheck checkbounds(block_arr.blocks, blockindex.I...)
105105
@inbounds block = block_arr.blocks[blockindex.I...]
106106
@boundscheck checkbounds(block, blockindex.α...)
@@ -113,24 +113,24 @@ end
113113
# AbstractArray Interface #
114114
###########################
115115

116-
@inline function Base.similar{T,N,T2}(block_array::BlockArray{T,N}, ::Type{T2})
116+
@inline function Base.similar(block_array::BlockArray{T,N}, ::Type{T2}) where {T,N,T2}
117117
BlockArray(similar(block_array.blocks, Array{T2, N}), copy(block_array.block_sizes))
118118
end
119119

120-
@generated function Base.size{T,N}(arr::BlockArray{T,N})
120+
@generated function Base.size(arr::BlockArray{T,N}) where {T,N}
121121
exp = Expr(:tuple, [:(arr.block_sizes[$i][end] - 1) for i in 1:N]...)
122122
return quote
123123
@inbounds return $exp
124124
end
125125
end
126126

127-
@inline function Base.getindex{T, N}(block_arr::BlockArray{T, N}, i::Vararg{Int, N})
127+
@inline function Base.getindex(block_arr::BlockArray{T, N}, i::Vararg{Int, N}) where {T,N}
128128
@boundscheck checkbounds(block_arr, i...)
129129
@inbounds v = block_arr[global2blockindex(block_arr.block_sizes, i)]
130130
return v
131131
end
132132

133-
@inline function Base.setindex!{T, N}(block_arr::BlockArray{T, N}, v, i::Vararg{Int, N})
133+
@inline function Base.setindex!(block_arr::BlockArray{T, N}, v, i::Vararg{Int, N}) where {T,N}
134134
@boundscheck checkbounds(block_arr, i...)
135135
@inbounds block_arr[global2blockindex(block_arr.block_sizes, i)] = v
136136
return block_arr
@@ -140,7 +140,7 @@ end
140140
# Indexing #
141141
############
142142

143-
function _check_setblock!{T,N}(block_arr::BlockArray{T, N}, v, block::NTuple{N, Int})
143+
function _check_setblock!(block_arr::BlockArray{T, N}, v, block::NTuple{N, Int}) where {T,N}
144144
for i in 1:N
145145
if size(v, i) != blocksize(block_arr.block_sizes, i, block[i])
146146
throw(DimensionMismatch(string("tried to assign $(size(v)) array to ", blocksize(block_arr, block...), " block")))
@@ -149,22 +149,22 @@ function _check_setblock!{T,N}(block_arr::BlockArray{T, N}, v, block::NTuple{N,
149149
end
150150

151151

152-
@inline function setblock!{T, N}(block_arr::BlockArray{T, N}, v, block::Vararg{Int, N})
152+
@inline function setblock!(block_arr::BlockArray{T, N}, v, block::Vararg{Int, N}) where {T,N}
153153
@boundscheck blockcheckbounds(block_arr, block...)
154154
@boundscheck _check_setblock!(block_arr, v, block)
155155
@inbounds block_arr.blocks[block...] = v
156156
return block_arr
157157
end
158158

159-
@propagate_inbounds function Base.setindex!{T,N}(block_array::BlockArray{T, N}, v, block_index::BlockIndex{N})
159+
@propagate_inbounds function Base.setindex!(block_array::BlockArray{T, N}, v, block_index::BlockIndex{N}) where {T,N}
160160
getblock(block_array, block_index.I...)[block_index.α...] = v
161161
end
162162

163163
########
164164
# Misc #
165165
########
166166

167-
@generated function Base.Array{T,N,R}(block_array::BlockArray{T, N, R})
167+
@generated function Base.Array(block_array::BlockArray{T, N, R}) where {T,N,R}
168168
# TODO: This will fail for empty block array
169169
return quote
170170
block_sizes = block_array.block_sizes
@@ -179,7 +179,7 @@ end
179179
end
180180
end
181181

182-
@generated function Base.copy!{T, N, R <: AbstractArray}(block_array::BlockArray{T, N, R}, arr::R)
182+
@generated function Base.copy!(block_array::BlockArray{T, N, R}, arr::R) where {T,N,R <: AbstractArray}
183183
return quote
184184
block_sizes = block_array.block_sizes
185185

src/blockindices.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ julia> a[BlockIndex((2,2), (2,3))]
2525
20
2626
```
2727
"""
28-
immutable BlockIndex{N}
28+
struct BlockIndex{N}
2929
I::NTuple{N, Int}
3030
α::NTuple{N, Int}
3131
end
@@ -34,7 +34,7 @@ end
3434
@inline BlockIndex(a::NTuple, b::Int) = BlockIndex(a, (b,))
3535
@inline BlockIndex(a::Int, b::NTuple) = BlockIndex((a,), b)
3636

37-
@generated function BlockIndex{M, N}(I::NTuple{N, Int}, α::NTuple{M, Int})
37+
@generated function BlockIndex(I::NTuple{N, Int}, α::NTuple{M, Int}) where {M,N}
3838
@assert M < N
3939
α_ex = Expr(:tuple, [k <= M ? :(α[$k]) : :(1) for k = 1:N]...)
4040
return quote
@@ -49,7 +49,7 @@ end
4949
5050
Converts from global indices `inds` to a `BlockIndex`.
5151
"""
52-
@generated function global2blockindex{N}(block_sizes::BlockSizes{N}, i::NTuple{N, Int})
52+
@generated function global2blockindex(block_sizes::BlockSizes{N}, i::NTuple{N, Int}) where {N}
5353
block_index_ex = Expr(:tuple, [:(_find_block(block_sizes, $k, i[$k])) for k = 1:N]...)
5454
I_ex = Expr(:tuple, [:(block_index[$k][1]) for k = 1:N]...)
5555
α_ex = Expr(:tuple, [:(block_index[$k][2]) for k = 1:N]...)
@@ -67,7 +67,7 @@ end
6767
6868
Converts from a block index to a tuple containing the global indices
6969
"""
70-
@generated function blockindex2global{N}(block_sizes::BlockSizes{N}, block_index::BlockIndex{N})
70+
@generated function blockindex2global(block_sizes::BlockSizes{N}, block_index::BlockIndex{N}) where {N}
7171
ex = Expr(:tuple, [:(block_sizes[$k, block_index.I[$k]] + block_index.α[$k] - 1) for k = 1:N]...)
7272
return quote
7373
$Expr(:meta, :inline)

src/blocksizes.jl

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,19 @@
33
##############
44

55
# Keeps track of the (cumulative) sizes of all the blocks in the `BlockArray`.
6-
immutable BlockSizes{N}
6+
struct BlockSizes{N}
77
cumul_sizes::NTuple{N, Vector{Int}}
88
# Takes a tuple of sizes, accumulates them and create a `BlockSizes`
99
end
1010

11-
function BlockSizes{N}(sizes::Vararg{Vector{Int}, N}) where {N}
12-
cumul_sizes = ntuple(k -> _cumul_vec(sizes[k]), Val{N})
11+
function BlockSizes(sizes::Vararg{Vector{Int}, N}) where {N}
12+
cumul_sizes = ntuple(k -> _cumul_vec(sizes[k]), Val(N))
1313
return BlockSizes(cumul_sizes)
1414
end
1515

16-
@inline BlockSizes{N}(sizes::Vararg{Vector{Int}, N}) = BlockSizes{N}(sizes...)
17-
1816
Base.:(==)(a::BlockSizes, b::BlockSizes) = a.cumul_sizes == b.cumul_sizes
1917

20-
function _cumul_vec{T}(v::Vector{T})
18+
function _cumul_vec(v::Vector{T}) where {T}
2119
v_cumul = similar(v, length(v) + 1)
2220
z = one(T)
2321
v_cumul[1] = z
@@ -34,12 +32,12 @@ end
3432
@propagate_inbounds blocksize(block_sizes::BlockSizes, i, j) = block_sizes[i, j+1] - block_sizes[i, j]
3533

3634
# ntuple with Val was slow here. @generated it is!
37-
@generated function blocksize{N}(block_sizes::BlockSizes{N}, i::NTuple{N, Int})
35+
@generated function blocksize(block_sizes::BlockSizes{N}, i::NTuple{N, Int}) where {N}
3836
exp = Expr(:tuple, [:(blocksize(block_sizes, $k, i[$k])) for k in 1:N]...)
3937
return exp
4038
end
4139

42-
function Base.show{N}(io::IO, block_sizes::BlockSizes{N})
40+
function Base.show(io::IO, block_sizes::BlockSizes{N}) where {N}
4341
if N == 0
4442
print(io, "[]")
4543
else
@@ -70,7 +68,7 @@ end
7068
return block, i - cum_size
7169
end
7270

73-
@generated function nblocks{N}(block_sizes::BlockSizes{N})
71+
@generated function nblocks(block_sizes::BlockSizes{N}) where {N}
7472
ex = Expr(:tuple, [:(nblocks(block_sizes, $i)) for i in 1:N]...)
7573
return quote
7674
@inbounds return $ex
@@ -81,15 +79,15 @@ end
8179

8280

8381
# ntuple is yet again slower
84-
@generated function Base.copy{N}(block_sizes::BlockSizes{N})
82+
@generated function Base.copy(block_sizes::BlockSizes{N}) where {N}
8583
exp = Expr(:tuple, [:(copy(block_sizes[$k])) for k in 1:N]...)
8684
return quote
8785
BlockSizes($exp)
8886
end
8987
end
9088

9189
# Computes the global range of an Array that corresponds to a given block_index
92-
@generated function globalrange{N}(block_sizes::BlockSizes{N}, block_index::NTuple{N, Int})
90+
@generated function globalrange(block_sizes::BlockSizes{N}, block_index::NTuple{N, Int}) where {N}
9391
indices_ex = Expr(:tuple, [:(block_sizes[$i, block_index[$i]]:block_sizes[$i, block_index[$i] + 1] - 1) for i = 1:N]...)
9492
return quote
9593
$Expr(:meta, :inline)

0 commit comments

Comments
 (0)