Skip to content
Snippets Groups Projects
Unverified Commit c86d3118 authored by Michiel Cottaar's avatar Michiel Cottaar
Browse files

Fix tests

parent d26c9e97
No related branches found
No related tags found
1 merge request!2Define variables through new @defvar macro
...@@ -66,7 +66,7 @@ _mult(g1::AbstractVector, g2::AbstractVector) = g1 .* permutedims(g2) ...@@ -66,7 +66,7 @@ _mult(g1::AbstractVector, g2::AbstractVector) = g1 .* permutedims(g2)
end end
function split_gradient(cgb::ConstantGradient, times::VariableType...) function split_gradient(cgb::ConstantGradient, times::VariableType...)
durations = [times[1], [t[2] - t[1] for t in zip(times[1:end-1], times[2:end])]..., duration(cgb) - times[end]] durations = [times[1], [t[2] - t[1] for t in zip(times[1:end-1], times[2:end])]..., variables.duration(cgb) - times[end]]
if cgb isa ConstantGradient1D if cgb isa ConstantGradient1D
return [ConstantGradient1D(cgb.gradient_strength, cgb.orientation, d, cgb.group) for d in durations] return [ConstantGradient1D(cgb.gradient_strength, cgb.orientation, d, cgb.group) for d in durations]
else else
......
...@@ -57,8 +57,8 @@ end ...@@ -57,8 +57,8 @@ end
@defvar gradient qvec(ig::InstantGradient3D) = ig.qvec @defvar gradient qvec(ig::InstantGradient3D) = ig.qvec
variables.duration.f(::InstantGradient) = 0. @defvar duration(::InstantGradient) = 0.
variables.effective_time.f(::InstantGradient) = 0. @defvar effective_time(::InstantGradient) = 0.
@defvar gradient bmat_gradient(::InstantGradient, qstart=nothing) = zero(SMatrix{3, 3, Float64, 3}) @defvar gradient bmat_gradient(::InstantGradient, qstart=nothing) = zero(SMatrix{3, 3, Float64, 3})
make_generic(ig::InstantGradient) = ig make_generic(ig::InstantGradient) = ig
......
...@@ -31,7 +31,7 @@ struct GenericPulse <: RFPulseComponent ...@@ -31,7 +31,7 @@ struct GenericPulse <: RFPulseComponent
@assert all(time .>= 0) @assert all(time .>= 0)
if isnothing(effective_time) if isnothing(effective_time)
test_res = new(Float64.(time), Float64.(amplitude), Float64.(phase), 0.) test_res = new(Float64.(time), Float64.(amplitude), Float64.(phase), 0.)
effective_time = time_halfway_flip(test_res) effective_time = variables.time_halfway_flip(test_res)
end end
return new(Float64.(time), Float64.(amplitude), Float64.(phase), Float64(effective_time)) return new(Float64.(time), Float64.(amplitude), Float64.(phase), Float64(effective_time))
end end
...@@ -56,12 +56,12 @@ function GenericPulse(pulse::GenericPulse, t1::Number, t2::Number) ...@@ -56,12 +56,12 @@ function GenericPulse(pulse::GenericPulse, t1::Number, t2::Number)
pnew = pulse.phase[use] pnew = pulse.phase[use]
if !(t1 tnew[1]) if !(t1 tnew[1])
pushfirst!(tnew, t1) pushfirst!(tnew, t1)
pushfirst!(anew, amplitude(pulse, t1)) pushfirst!(anew, variables.amplitude(pulse, t1))
pushfirst!(pnew, phase(pulse, t1)) pushfirst!(pnew, variables.phase(pulse, t1))
elseif !(t2 tnew[end]) elseif !(t2 tnew[end])
push!(tnew, t2) push!(tnew, t2)
push!(anew, amplitude(pulse, t2)) push!(anew, variables.amplitude(pulse, t2))
push!(pnew, phase(pulse, t2)) push!(pnew, variables.phase(pulse, t2))
end end
return GenericPulse(tnew .- t1, anew, pnew, pulse.effective_time - t1) return GenericPulse(tnew .- t1, anew, pnew, pulse.effective_time - t1)
end end
......
...@@ -99,7 +99,7 @@ end ...@@ -99,7 +99,7 @@ end
end end
@defvar begin @defvar begin
amplitude(pulse::SincPulse, time::Number) = variables.amplitude(pulse) * normalised_function(abs((time - variables.effective_time(pulse))) / variables.lobe_duration(pulse), N_left(pulse), N_right(pulse); apodise=pulse.apodise) amplitude(pulse::SincPulse, time::Number) = variables.amplitude(pulse) * normalised_function(abs((time - variables.effective_time(pulse))) / variables.lobe_duration(pulse), variables.N_left(pulse), variables.N_right(pulse); apodise=pulse.apodise)
phase(pulse::SincPulse, time::Number) = variables.phase(pulse) + variables.frequency(pulse) * (time - variables.effective_time(pulse)) * 360. phase(pulse::SincPulse, time::Number) = variables.phase(pulse) + variables.frequency(pulse) * (time - variables.effective_time(pulse)) * 360.
frequency(pulse::SincPulse, time::Number) = variables.frequency(pulse) frequency(pulse::SincPulse, time::Number) = variables.frequency(pulse)
end end
......
...@@ -37,6 +37,7 @@ end_time(block::Tuple{<:VariableType, <:AbstractBlock}) = variables.duration(blo ...@@ -37,6 +37,7 @@ end_time(block::Tuple{<:VariableType, <:AbstractBlock}) = variables.duration(blo
effective_time(container::ContainerBlock, index, indices...) = start_time(container, index) + variables.effective_time(container[index], indices...) effective_time(container::ContainerBlock, index, indices...) = start_time(container, index) + variables.effective_time(container[index], indices...)
effective_time(block::Tuple{<:VariableType, <:AbstractBlock}) = block[1] + variables.effective_time(block[2]) effective_time(block::Tuple{<:VariableType, <:AbstractBlock}) = block[1] + variables.effective_time(block[2])
end end
""" """
effective_time(container, indices...) effective_time(container, indices...)
...@@ -46,7 +47,7 @@ This will crash if the component does not have an [`effective_time`](@ref) (e.g. ...@@ -46,7 +47,7 @@ This will crash if the component does not have an [`effective_time`](@ref) (e.g.
Also see [`variables.duration`](@ref), [`start_time`](@ref), and [`end_time`](@ref) Also see [`variables.duration`](@ref), [`start_time`](@ref), and [`end_time`](@ref)
""" """
effective_time variables.effective_time
""" """
...@@ -147,8 +148,8 @@ function get_gradient end ...@@ -147,8 +148,8 @@ function get_gradient end
Returns all the times that the sequence will readout. Returns all the times that the sequence will readout.
""" """
readout_times variables.readout_times
iter(component::Tuple{<:Number, <:ReadoutComponent}, ::Val{:readout}) = [(time, nothing) for time in readout_times(component[2])] iter(component::Tuple{<:Number, <:ReadoutComponent}, ::Val{:readout}) = [(time, nothing) for time in variables.readout_times(component[2])]
end end
\ No newline at end of file
...@@ -232,7 +232,7 @@ Computes the area under the curve for the gradient waveform in [`BaseBuildingBlo ...@@ -232,7 +232,7 @@ Computes the area under the curve for the gradient waveform in [`BaseBuildingBlo
If `first_event` is set to something else than `nothing`, only the gradient waveform after this RF pulse/Readout will be considered. If `first_event` is set to something else than `nothing`, only the gradient waveform after this RF pulse/Readout will be considered.
Similarly, if `last_event` is set to something else than `nothing`, only the gradient waveform up to this RF pulse/Readout will be considered. Similarly, if `last_event` is set to something else than `nothing`, only the gradient waveform up to this RF pulse/Readout will be considered.
""" """
qvec variables.qvec
function edge_times(bb::BaseBuildingBlock) function edge_times(bb::BaseBuildingBlock)
res = Float64[] res = Float64[]
......
...@@ -170,12 +170,12 @@ struct SliceSelect{N} <: BaseTrapezoid{N} ...@@ -170,12 +170,12 @@ struct SliceSelect{N} <: BaseTrapezoid{N}
pulse :: RFPulseComponent pulse :: RFPulseComponent
end end
function SliceSelect(pulse::RFPulseComponent; orientation=nothing, rise_time=nothing, group=nothing, slew_rate=nothing, variables...) function SliceSelect(pulse::RFPulseComponent; orientation=nothing, rise_time=nothing, group=nothing, slew_rate=nothing, vars...)
res = SliceSelect( res = SliceSelect(
Trapezoid(; orientation=orientation, rise_time=rise_time, flat_time=duration(pulse), group=group, slew_rate=slew_rate), Trapezoid(; orientation=orientation, rise_time=rise_time, flat_time=variables.duration(pulse), group=group, slew_rate=slew_rate),
pulse pulse
) )
set_simple_constraints!(res, variables) set_simple_constraints!(res, vars)
return res return res
end end
...@@ -225,7 +225,7 @@ function LineReadout(adc::ADC; ramp_overlap=nothing, orientation=nothing, group= ...@@ -225,7 +225,7 @@ function LineReadout(adc::ADC; ramp_overlap=nothing, orientation=nothing, group=
end end
Base.keys(::LineReadout) = (Val(:rise), Val(:adc), Val(:flat), Val(:fall)) Base.keys(::LineReadout) = (Val(:rise), Val(:adc), Val(:flat), Val(:fall))
Base.getindex(lr::LineReadout, ::Val{:adc}) = ((1 - ramp_overlap(lr)) * rise_time(lr), lr.adc) Base.getindex(lr::LineReadout, ::Val{:adc}) = ((1 - variables.ramp_overlap(lr)) * variables.rise_time(lr), lr.adc)
@defvar begin @defvar begin
ramp_overlap(lr::LineReadout) = lr.ramp_overlap ramp_overlap(lr::LineReadout) = lr.ramp_overlap
......
...@@ -27,7 +27,7 @@ The RF pulses cause mappings between these different states as described below. ...@@ -27,7 +27,7 @@ The RF pulses cause mappings between these different states as described below.
- `:refocus`/`:invert`/180: Flips the sign of the spin state (i.e., +longitudinal <-> -longitudinal, +transverse <-> -transverse) - `:refocus`/`:invert`/180: Flips the sign of the spin state (i.e., +longitudinal <-> -longitudinal, +transverse <-> -transverse)
- `:excite`/90: Takes spin state one step along the following sequence +longitudinal -> +transverse -> -longitudinal -> -transverse -> +longitudinal - `:excite`/90: Takes spin state one step along the following sequence +longitudinal -> +transverse -> -longitudinal -> -transverse -> +longitudinal
- `:neg_excite`/270/-90: Inverse step compared with `:excite`. - `:neg_excite`/270/-90: Inverse step compared with `:excite`.
- `readout_index`: After encountering the number of pulses as defined in `pulse_effects`, continue the `PathWay` until the readout given by `index` is reached. If set to 0 the `PathWay` is terminated immediately after the last RF pulse. - `readout_index`: After encountering the number of pulses as defined in `pulse_effects`, continue the `Pathway` until the readout given by `index` is reached. If set to 0 the `Pathway` is terminated immediately after the last RF pulse.
- `group`: which gradient grouping to consider for the `qvec` and `bmat`. If not set, all gradients will be considered (using their current alignment). - `group`: which gradient grouping to consider for the `qvec` and `bmat`. If not set, all gradients will be considered (using their current alignment).
## Attributes ## Attributes
...@@ -169,7 +169,7 @@ variables.bval ...@@ -169,7 +169,7 @@ variables.bval
""" """
get_pathway(sequence) get_pathway(sequence)
Gets the main [`PathWay`](@ref) that spins are expected to experience in the sequence. Gets the main [`Pathway`](@ref) that spins are expected to experience in the sequence.
Multiple pathways might be returned as an array or (named)tuple. Multiple pathways might be returned as an array or (named)tuple.
""" """
......
...@@ -92,7 +92,7 @@ Main module containing all the MRIBuilder sequence variables. ...@@ -92,7 +92,7 @@ Main module containing all the MRIBuilder sequence variables.
All variables are available as members of this module, e.g. All variables are available as members of this module, e.g.
`variables.echo_time` returns the echo time variable. `variables.echo_time` returns the echo time variable.
New variables can be defined using [`@defvar`](@ref). New variables can be defined using `@defvar`.
Set constraints on variables by passing them on as keywords during the sequence generation, Set constraints on variables by passing them on as keywords during the sequence generation,
e.g., `seq=SpinEcho(echo_time=70)`. e.g., `seq=SpinEcho(echo_time=70)`.
...@@ -360,9 +360,6 @@ Returns the gradient orientation. ...@@ -360,9 +360,6 @@ Returns the gradient orientation.
function gradient_orientation end function gradient_orientation end
@defvar function effective_time end
function (var::Variable)(block::AbstractBlock, args...; kwargs...) function (var::Variable)(block::AbstractBlock, args...; kwargs...)
if !applicable(var.f, block, args...) && !isnothing(var.getter) if !applicable(var.f, block, args...) && !isnothing(var.getter)
apply_to = var.getter(block) apply_to = var.getter(block)
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
end end
# Single ADC event # Single ADC event
@test length(readout_times(seq)) == 1024 @test length(variables.readout_times(seq)) == 1024
@test variables.readout_times(seq)[1] 0.22 + 5 + 0.02 + 0.5 * 0.3125 @test variables.readout_times(seq)[1] 0.22 + 5 + 0.02 + 0.5 * 0.3125
@test variables.readout_times(seq)[end] 0.22 + 5 + 0.02 + 1023.5 * 0.3125 @test variables.readout_times(seq)[end] 0.22 + 5 + 0.02 + 1023.5 * 0.3125
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
0.02 + # RF ringdown time (added by block duration) 0.02 + # RF ringdown time (added by block duration)
29.730 # Delay until ADC start (to get start at ADC at TE=30) 29.730 # Delay until ADC start (to get start at ADC at TE=30)
) )
@test length(readout_times(seq)) == 8192 @test length(variables.readout_times(seq)) == 8192
@test variables.readout_times(seq)[1] start_adc + 0.5 * 0.03125 @test variables.readout_times(seq)[1] start_adc + 0.5 * 0.03125
@test variables.readout_times(seq)[end] start_adc + 8191.5 * 0.03125 @test variables.readout_times(seq)[end] start_adc + 8191.5 * 0.03125
end end
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
seq_json = read_sequence(io, format=:serialize) seq_json = read_sequence(io, format=:serialize)
@test variables.duration(seq_orig) == variables.duration(seq_json) @test variables.duration(seq_orig) == variables.duration(seq_json)
@test length(seq_orig) == length(seq_json) @test length(seq_orig) == length(seq_json)
@test all(duration.(seq_orig) .== duration.(seq_json)) @test all(variables.duration.(seq_orig) .== variables.duration.(seq_json))
@test iszero(length(iter_instant_gradients(seq_json))) @test iszero(length(iter_instant_gradients(seq_json)))
@test iszero(length(iter_instant_pulses(seq_json))) @test iszero(length(iter_instant_pulses(seq_json)))
@test all(variables.readout_times(seq_json) .== variables.readout_times(seq_orig)) @test all(variables.readout_times(seq_json) .== variables.readout_times(seq_orig))
...@@ -120,7 +120,7 @@ ...@@ -120,7 +120,7 @@
seq_json = read_sequence(io; format=:serialize) seq_json = read_sequence(io; format=:serialize)
@test variables.duration(seq_orig) == variables.duration(seq_json) @test variables.duration(seq_orig) == variables.duration(seq_json)
@test length(seq_orig) == length(seq_json) @test length(seq_orig) == length(seq_json)
@test all(duration.(seq_orig) .== duration.(seq_json)) @test all(variables.duration.(seq_orig) .== variables.duration.(seq_json))
@test length(iter_instant_gradients(seq_json)) == length(iter_instant_gradients(seq_json)) @test length(iter_instant_gradients(seq_json)) == length(iter_instant_gradients(seq_json))
@test length(iter_instant_pulses(seq_json)) == length(iter_instant_pulses(seq_json)) @test length(iter_instant_pulses(seq_json)) == length(iter_instant_pulses(seq_json))
@test all(variables.readout_times(seq_json) .== variables.readout_times(seq_orig)) @test all(variables.readout_times(seq_json) .== variables.readout_times(seq_orig))
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
@testset "SincPulse" begin @testset "SincPulse" begin
sp = SincPulse(amplitude=1., frequency=2., phase=0., lobe_duration=10., Nzeros=(2, 1)) sp = SincPulse(amplitude=1., frequency=2., phase=0., lobe_duration=10., Nzeros=(2, 1))
@test duration(sp) 30. @test variables.duration(sp) 30.
@test variables.amplitude(sp, 20) 1. @test variables.amplitude(sp, 20) 1.
@test variables.amplitude(sp, 0) 0. atol=1e-8 @test variables.amplitude(sp, 0) 0. atol=1e-8
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
seq = DiffusionSpinEcho(TE=:min, bval=1.) seq = DiffusionSpinEcho(TE=:min, bval=1.)
@test length(seq) == 9 @test length(seq) == 9
grad_duration = variables.TE(seq) / 2 grad_duration = variables.TE(seq) / 2
@test all(isapprox.(duration.(seq), [0., 0., grad_duration, 0., 0., 0., grad_duration, 0., 0.], atol=1e-6)) @test all(isapprox.(variables.duration.(seq), [0., 0., grad_duration, 0., 0., 0., grad_duration, 0., 0.], atol=1e-6))
@test length([iter_instant_pulses(seq)...]) == 2 @test length([iter_instant_pulses(seq)...]) == 2
@test length([iter_instant_gradients(seq)...]) == 0. @test length([iter_instant_gradients(seq)...]) == 0.
@test variables.bval(seq) 1. @test variables.bval(seq) 1.
...@@ -38,24 +38,24 @@ ...@@ -38,24 +38,24 @@
@testset "Maximise b-value" begin @testset "Maximise b-value" begin
seq = DiffusionSpinEcho(TE=80., bval=:max) seq = DiffusionSpinEcho(TE=80., bval=:max)
@test length(seq) == 9 @test length(seq) == 9
@test all(isapprox.(duration.(seq), [0., 0., 40., 0., 0., 0., 40., 0., 0.], atol=1e-4, rtol=1e-4)) @test all(isapprox.(variables.duration.(seq), [0., 0., 40., 0., 0., 0., 40., 0., 0.], atol=1e-4, rtol=1e-4))
@test length([iter_instant_pulses(seq)...]) == 2 @test length([iter_instant_pulses(seq)...]) == 2
@test length([iter_instant_gradients(seq)...]) == 0. @test length([iter_instant_gradients(seq)...]) == 0.
@test variables.TE(seq) 80. @test variables.TE(seq) 80.
@test variables.duration(seq) 80. @test variables.duration(seq) 80.
@test 4.8 < bval(seq) < 4.9 @test 4.8 < variables.bval(seq) < 4.9
@test variables.rise_time(seq[:gradient]) min_rise_time rtol=1e-4 @test variables.rise_time(seq[:gradient]) min_rise_time rtol=1e-4
@test all(isapprox.(edge_times(seq, tol=1e-3), [0., min_rise_time, 40. - min_rise_time, 40, 40 + min_rise_time, 80 - min_rise_time, 80.], atol=1e-4)) @test all(isapprox.(edge_times(seq, tol=1e-3), [0., min_rise_time, 40. - min_rise_time, 40, 40 + min_rise_time, 80 - min_rise_time, 80.], atol=1e-4))
# can also maximise q-value # can also maximise q-value
seq2 = DiffusionSpinEcho(TE=80., qval=:max) seq2 = DiffusionSpinEcho(TE=80., qval=:max)
@test all(isapprox.(duration.(seq), duration.(seq2), atol=1e-4, rtol=1e-4)) @test all(isapprox.(variables.duration.(seq), variables.duration.(seq2), atol=1e-4, rtol=1e-4))
@test variables.TE(seq) variables.TE(seq2) atol=1e-4 rtol=1e-4 @test variables.TE(seq) variables.TE(seq2) atol=1e-4 rtol=1e-4
@test variables.bval(seq) variables.bval(seq2) atol=1e-4 rtol=1e-4 @test variables.bval(seq) variables.bval(seq2) atol=1e-4 rtol=1e-4
end end
@testset "Set diffusion time Δ" begin @testset "Set diffusion time Δ" begin
seq = DiffusionSpinEcho(TE=80., Δ=70., qval=:max) seq = DiffusionSpinEcho(TE=80., Δ=70., qval=:max)
@test all(isapprox.(duration.(seq), [0., 0., 10., 30., 0., 30., 10., 0., 0.], atol=1e-4, rtol=1e-4)) @test all(isapprox.(variables.duration.(seq), [0., 0., 10., 30., 0., 30., 10., 0., 0.], atol=1e-4, rtol=1e-4))
@test variables.Δ(seq) 70. @test variables.Δ(seq) 70.
@test variables.TE(seq) 80. @test variables.TE(seq) 80.
@test variables.duration(seq) 80. @test variables.duration(seq) 80.
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
end end
@testset "Set gradient duration" begin @testset "Set gradient duration" begin
seq = DiffusionSpinEcho(TE=80., gradient=(duration=10., ), bval=:max) seq = DiffusionSpinEcho(TE=80., gradient=(duration=10., ), bval=:max)
@test all(isapprox.(duration.(seq), [0., 0., 10., 30., 0., 30., 10., 0., 0.], atol=1e-4, rtol=1e-4)) @test all(isapprox.(variables.duration.(seq), [0., 0., 10., 30., 0., 30., 10., 0., 0.], atol=1e-4, rtol=1e-4))
@test variables.Δ(seq) 70. rtol=1e-4 @test variables.Δ(seq) 70. rtol=1e-4
@test variables.TE(seq) 80. @test variables.TE(seq) 80.
@test variables.duration(seq) 80. @test variables.duration(seq) 80.
...@@ -79,33 +79,33 @@ ...@@ -79,33 +79,33 @@
@testset "slice-select DW-SE" begin @testset "slice-select DW-SE" begin
seq = DiffusionSpinEcho(duration=:min, bval=2., slice_thickness=2.) seq = DiffusionSpinEcho(duration=:min, bval=2., slice_thickness=2.)
@test length(seq) == 9 @test length(seq) == 9
@test duration(seq[1]) > 1. @test variables.duration(seq[1]) > 1.
for index in 1:9 for index in 1:9
if index in [2, 4, 8, 9] if index in [2, 4, 8, 9]
@test abs(duration(seq[index])) < 1e-6 @test abs(variables.duration(seq[index])) < 1e-6
else else
@test abs(duration(seq[index])) > 1 @test abs(variables.duration(seq[index])) > 1
end end
end end
@test duration(seq[:gradient]) duration(seq[:gradient2]) @test variables.duration(seq[:gradient]) variables.duration(seq[:gradient2])
@test variables.bval(seq) 2. @test variables.bval(seq) 2.
@test length(readout_times(seq)) == 1 @test length(variables.readout_times(seq)) == 1
@test variables.readout_times(seq)[1] > variables.TE(seq) @test variables.readout_times(seq)[1] > variables.TE(seq)
end end
@testset "voxel-wise DW-SE" begin @testset "voxel-wise DW-SE" begin
seq = DiffusionSpinEcho(duration=:min, bval=2., voxel_size=2., fov=(20, 20, 20)) seq = DiffusionSpinEcho(duration=:min, bval=2., voxel_size=2., fov=(20, 20, 20))
@test length(seq) == 9 @test length(seq) == 9
@test duration(seq[1]) > 1. @test variables.duration(seq[1]) > 1.
for index in 1:9 for index in 1:9
if index in [2, 6, 8] if index in [2, 6, 8]
@test abs(duration(seq[index])) < 1e-6 @test abs(variables.duration(seq[index])) < 1e-6
else else
@test abs(duration(seq[index])) > 1 @test abs(variables.duration(seq[index])) > 1
end end
end end
@test duration(seq[:gradient]) duration(seq[:gradient2]) @test variables.duration(seq[:gradient]) variables.duration(seq[:gradient2])
@test variables.bval(seq) 2. @test variables.bval(seq) 2.
@test length(readout_times(seq)) > 50 @test length(variables.readout_times(seq)) > 50
@test 67 < variables.TE(seq) < 68 @test 67 < variables.TE(seq) < 68
@test 72 < variables.duration(seq) < 73 @test 72 < variables.duration(seq) < 73
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment