text:
_is_deepcopiable(g::GroupElement) = !isbits(g)

function isabelian end
function issolvable end
function isnilpotent end
function isperfect end

function derivedsubgroup end
function center end
function socle end
function sylowsubgroup end

function centralizer end
function normalizer end
function stabilizer end

@doc Markdown.doc"""
    index(H::Gr, G::Gr) where {Gr <: Group}

Return the index $|G : H|$, where $H \subseteq G$. If $H$ is not contained
in $G$, an error is thrown.
"""
function index end

@doc Markdown.doc"""
    left_coset_representatives(H::Gr, G::Gr) where {Gr <: Group}

Return representatives of the left cosets $h G$ where $h$ are elements of $H$.
If $H$ is not contained in $G$, an error is thrown.
"""
function left_coset_representatives end
function right_coset_representatives end


text:
#linesearch is an intermediary between NewtonStopLS and a 1D solver.
#change the name?
function linesearch(ϕ    :: LSModel,
                    ϕstp :: AbstractStopping,  # be more specific for stp
                    x₀   :: AbstractVector,
                    d    :: AbstractVector,
                    f₀   :: AbstractFloat,
                    ∇f₀  :: AbstractVector,
                    τ₀   :: Real,
                    τ₁   :: Real)
    # rebase the LSModel
    g₀ = dot(∇f₀,d)
    rebase!(ϕ, x₀, d, τ₀, f₀, g₀)

    # convert the Armijo and Wolfe criteria to an asymetric interval [α,β]
    α =  (τ₁-τ₀)*g₀
    β = -(τ₁+τ₀)*g₀

    # reuse the stopping
    reinit!(ϕstp)
    ϕstp.pb = ϕ
    # redefine the optimality_check function using α and β
    ϕstp.optimality_check =  (p,s) -> optim_check_LS(p,s,α,β)

    # optimize in the interval [0.0,Inf]
    ϕstp = min_1D(ϕ, 0.0, Inf, α, β, stp = ϕstp)

    # unpack the results
    t = ϕstp.current_state.x #Tanj: Shouldn't we check if ϕstp.meta.optimal = true?
    ft = ϕ.f   # must rely on the solver (min_1D) so that the last evaluation was
    gt = ϕ.∇f  # at the optimal step, so that the stored value for f and ∇f are valid

    return t, ft, gt, ϕstp
end

ext: jl


text:
######################################################################
# kmeans_trajectory.jl
######################################################################

function kmeans_trajectory(em::ErgodicManager, tm::TrajectoryManager)
	x0 = (tm.x0[1], tm.x0[2])
	return kmeans_trajectory(em, x0, tm.h, tm.N)
end

function kmeans_trajectory(em::ErgodicManager, x0::T2F, h::Float64, N::Int)
	# points is a vv_f
	points, ud0 = random_trajectory(N, h, x0)

	# show what the score was before we did the optimization
	ts = ergodic_score(em, points)
	println("ts (before) = ", round(ts,3))

	# create assignments and means matrix
	assignments = Array(Int, em.bins, em.bins)
	means = deepcopy(points)

	# TODO: select a termination condition...
	for i = 1:200
		assign_step(em, assignments, means)
		mean_step(em, assignments, means)
	end
	return means, ud0

end

function assign_step(em::ErgodicManager, assignments::Matrix{Int}, means::VVF)
	N = length(means)
	for xi = 1:em.bins
		for yi = 1:em.bins
			best_n = 1
			best_d = Inf
			for n = 1:N
				d = euc_dist_2(em, xi, yi, means[n])
				if d < best_d
					best_d = d
					best_n = n
				end
			end
			assignments[xi,yi] = best_n
		end
	end
end

# compute the mean of each cluster
function mean_step(em::ErgodicManager, assignments::Matrix{Int}, means::VVF)
	N = length(means)
	# let's clear the means vector
	for i = 1:N
		means[i][1] = 0.
		means[i][2] = 0.
	end

	# TODO: must we remake this every time
	mean_counts = zeros(N)

	for xi = 1:em.bins
		for yi = 1:em.bins
			i = assignments[xi,yi]
			phi_q = em.phi[xi,yi]
			means[i][1] += phi_q*(xi-0.5)*em.cell_size
			means[i][2] += phi_q*(yi-0.5)*em.cell_size
			mean_counts[i] += phi_q
		end
	end

	# normalize the means
	for i = 1:N
		mc = mean_counts[i]
		means[i][1] /= mc
		means[i][2] /= mc
	end
end

# returns the squared euclidean distance to some stuff
# TODO: give this a respectable name
function euc_dist_2(em::ErgodicManager, xi::Int, yi::Int,p::Vector{Float64})
	return euc_dist_2(em, xi, yi, p[1], p[2])
end
function euc_dist_2(em::ErgodicManager, xi::Int, yi::Int, xf::Float64, yf::Float64)
	xp = em.cell_size * (xi - 0.5)
	yp = em.cell_size * (yi - 0.5)
	dx = xf - xp
	dy = yf - yp

	return dx*dx + dy*dy
end

function random_trajectory(N::Int, h::Float64, x0::T2F)
	xd = [[x0[1], x0[2]] for i = 1:N+1]
	points = Array(Vector{Float64}, N)
	for i = 1:N
		points[i] = [rand(), rand()]
	end

	tsp_nn!(xd, points)

	ud = compute_controls(xd, h)

	return xd, ud
end


text:
using Test
using Peacock
using LinearAlgebra

using Peacock.Zoo
using Parameters

@testset "Band diagram tests" begin

    @testset "sample_path" begin
        # Generate a random path
        ks_orig = [[0.0,0.0]]
        for n in 1:3
            push!(ks_orig, ks_orig[end]+rand(2))
        end
        labels_orig = ["k$(n)" for n in 1:length(ks_orig)]
        # Sample it
        dk = 0.1
        ks, labels = Peacock.sample_path(ks_orig, labels=labels_orig, dk=dk)
        # Check same number of ks and labels
        @test length(ks) == length(labels)
        # Check k-point spacing is correct
        for (k1,k2) in zip(ks,ks[2:end])
            @test norm(k2-k1) <= dk
        end
        # Check labels match expected k positions
        for (k_orig,label_orig) in zip(ks_orig,labels_orig)
            n = findfirst([k==k_orig for k in ks])
            @test labels[n] == label_orig
        end
    end

end

@testset "Solver tests" begin

    @testset "Convolutions" begin
        # Create a test basis of plane waves
        b1, b2 = [0.3,0.2], [0.5,-0.1]
        ps, qs = Int[], Int[]
        for p in -1:2, q in -2:3
            push!(ps, p)
            push!(qs, q)
        end
        basis = Peacock.PlaneWaveBasis(b1, b2, ps, qs)
        # Convolution matrix of homogeneous array should be diagonal
        x = 1 + rand(ComplexF64)
        mat = fill(x, 10, 20)
        cmat = Peacock.convmat(mat, basis)
        @test all(diag(cmat) .≈ x)
    end

    @testset "Known examples" begin
        @testset "Homogeneous systems" begin
            # Create homogeneous geometry
            ep = 1 + rand()
            mu = 1 + rand()
            a1 = [1,0]
            a2 = [0,1]
            d1 = d2 = 0.01
            geometry = Geometry((x,y)->ep, (x,y)->mu, a1, a2, d1, d1)
            # Check for increasing Fourier space cutoffs
            for cutoff in [1,3,5]
                # test with norm(b1) == norm(b2)
                solver = Solver(geometry, cutoff)
                for polarisation in [TE,TM]
                    modes = solve(solver, [1,0], polarisation)
                    @test isapprox(modes[1].frequency, 1/sqrt(ep*mu))
                end
                # test with norm(b1) != norm(b2)
                solver = Solver(geometry, cutoff, cutoff+2)
                for polarisation in [TE,TM]
                    modes = solve(solver, [1,0], polarisation)
                    @test isapprox(modes[1].frequency, 1/sqrt(ep*mu))
                end
            end
        end
    end

    @testset "Lattice vectors" begin
        # Test conversion between real space and reciprocal space lattice vectors
        a1 = rand(2)
        a2 = rand(2)
        b1, b2 = Peacock.as_to_bs(a1,a2)
        @test isapprox(dot(a1,b1), 2pi)
        @test isapprox(dot(a2,b2), 2pi)
        @test isapprox(dot(a1,b2), 0, atol=1e-6)
        @test isapprox(dot(a2,b1), 0, atol=1e-6)
        a1_, a2_ = Peacock.bs_to_as(b1, b2)
        @test isapprox(a1, a1_)
        @test isapprox(a2, a2_)
    end

end

@testset "Wilson loop tests" begin

    @testset "Normalisations" begin
        # Create random data
        data = rand(ComplexF64, 5, 5)

        # Create random positive-definite Hermitian weighting
        weighting = I + 0.5rand(ComplexF64, 5, 5)
        weighting = weighting + weighting'

        # Test normalise
        data_ = Peacock.normalise(data, weighting=weighting)
        for n in 1:size(data_,2)
            weighted_norm = sqrt(abs(dot(data_[:,n], weighting*data_[:,n])))
            @test isapprox(weighted_norm, 1)
        end

        # Test orthonormalise
        data_ = Peacock.orthonormalise(data, weighting=weighting)
        for i in 1:size(data_,2), j in 1:size(data_,2)
            weighted_overlap = sqrt(abs(dot(data_[:,i], weighting*data_[:,j])))
            @test isapprox(weighted_overlap, float(i==j), atol=1e-6)
        end
    end

    @testset "Unitary approximations" begin
        # Create random unitary matrix
        M = rand(ComplexF64, 5, 5)  # random matrix
        M = M + M'                  # Hermitian matrix
        M = exp(1im * M)            # Unitary matrix
        @test Peacock.unitary_approx(M) ≈ M
    end

    @testset "Parallel transport gauge" begin
        # Load solver from the Zoo
        @unpack solver, polarisation = make_wu_topo(11)
        # Small Wilson loop of bands 1-3
        k0 = [0.3,0.2] # arbitrary
        ks = [k0, k0+[0.1,0], k0+[0.1,0.1], k0+[0,0.1]]
        spaces = HilbertSpace[]
        for k in ks
            modes = solve(solver, k, polarisation)
            space = HilbertSpace(modes[1:3])
            push!(spaces, space)
        end
        push!(spaces, spaces[1])
        vals, vecs, gauge = Peacock.wilson_gauge(spaces)
        # Parallel transport gauge: (unitary approximation of) the overlaps
        # of adjacent spaces should be equal to the identity
        for (a,b) in zip(gauge,gauge[2:end])
            @test Peacock.unitary_overlaps(a, b) ≈ I
        end
        # But even though unitary_overlaps(a,b)≈I around the loop
        # when we get to the end, we find we have accumulated a phase
        # given by the eigenvalues of the Wilson loop
        @test Peacock.overlaps(gauge[end], gauge[1]) ≈ diagm(0=>vals)
    end

    @testset "Known examples" begin

        @testset "Blanco de Paz 2019" begin
            # Load solver from the Zoo
            @unpack solver, polarisation = make_dePaz_frag(11)

            # Create a Wilson loop of bands 2&3 from k0 to k0+1
            function test_wilson_eigvals(solver, x::BrillouinZoneCoordinate, polarisation)
                k0 = Peacock.get_k(x, solver.basis)
                ts = range(0, stop=1, length=20)
                ks = [k0 + t*solver.basis.b2 for t in ts]
                spaces = HilbertSpace[]
                for k in ks
                    modes = solve(solver, k, polarisation)
                    space = HilbertSpace(modes[2:3])
                    push!(spaces, space)
                end
                return Peacock.wilson_eigvals(spaces, closed=false)
            end

            # Wilson loop of bands 2&3 from Γ to Γ+b1
            vals = test_wilson_eigvals(solver, BrillouinZoneCoordinate(0.0,0.0), polarisation)
            # Expected eigenvalues = [-1, -1]
            percentage_error = angle.(vals / -1) / 2pi * 100
            @test all(abs.(percentage_error) .< 1)

            # Wilson loop of bands 2&3 from M to M+b1
            vals = test_wilson_eigvals(solver, BrillouinZoneCoordinate(0.5,0.0), polarisation)
            # Expected eigenvalues = [+1, +1]
            percentage_error = angle.(vals / +1) / 2pi * 100
            @test all(abs.(percentage_error) .< 1)
        end

    end

end

# Check plotting functions for runtime errors, but does not test outputs
@testset "Plotting" begin

    @unpack geometry, solver, polarisation, K, G, M = make_wu_topo(7)

    @testset "Plot Geometry" begin
        plot(geometry)
    end

    @testset "Plot Solver" begin
        plot(solver)
    end

    @testset "Plot Mode" begin
        modes = solve(solver, G, polarisation)
        plot.(modes[1:3])
    end

    @testset "Plot band diagram" begin
        plot_band_diagram(solver, [K,G,M], polarisation, dk=1)
    end

    @testset "Plot Wilson loop winding" begin
        ks = [
            BrillouinZoneCoordinate(0.0, 0.0, "Γ"),
            BrillouinZoneCoordinate(0.5, 0.0, "M"),
            BrillouinZoneCoordinate(1.0, 0.0, "Γ")
        ]
        plot_wilson_loop_winding(solver, ks, polarisation, 1:3, dk_outer=1)
    end

end


text:
module SigDigits

using Decimals

include("sigfunctions.jl")

export use_sigdigits,
    use_sigdigits,
    sig_start,
    str_replace,
    count_digits,
    count_sigdigits,
    count_decimalplaces,
    count_nonzero_decimalplaces

end


text:
using FactCheck, MarketData

facts("signal") do

  context("foo") do
    @fact cl.values[1] => 105.22
  end

end


text:
@testset "pointer" begin

# inner constructors

voidptr_a = Ptr{Cvoid}(Int(0xDEADBEEF))
generic_voidptr_a = AMDGPU.DevicePtr{Cvoid,AS.Generic}(voidptr_a)
global_voidptr_a = AMDGPU.DevicePtr{Cvoid,AS.Global}(voidptr_a)
local_voidptr_a = AMDGPU.DevicePtr{Cvoid,AS.Local}(voidptr_a)

voidptr_b = Ptr{Cvoid}(Int(0xCAFEBABE))
generic_voidptr_b = AMDGPU.DevicePtr{Cvoid,AS.Generic}(voidptr_b)
global_voidptr_b = AMDGPU.DevicePtr{Cvoid,AS.Global}(voidptr_b)
local_voidptr_b = AMDGPU.DevicePtr{Cvoid,AS.Local}(voidptr_b)

intptr_b = convert(Ptr{Int}, voidptr_b)
generic_intptr_b = AMDGPU.DevicePtr{Int,AS.Generic}(intptr_b)
global_intptr_b = AMDGPU.DevicePtr{Int,AS.Global}(intptr_b)
local_intptr_b = AMDGPU.DevicePtr{Int,AS.Local}(intptr_b)

# outer constructors
@test AMDGPU.DevicePtr{Cvoid}(voidptr_a) == generic_voidptr_a
@test AMDGPU.DevicePtr(voidptr_a) == generic_voidptr_a

# getters
@test eltype(generic_voidptr_a) == Cvoid
@test eltype(global_intptr_b) == Int
@test AMDGPU.addrspace(generic_voidptr_a) == AS.Generic
@test AMDGPU.addrspace(global_voidptr_a) == AS.Global
@test AMDGPU.addrspace(local_voidptr_a) == AS.Local

# comparisons
@test generic_voidptr_a != global_voidptr_a
@test generic_voidptr_a != generic_intptr_b

@testset "conversions" begin

# between regular and device pointers

@test convert(Ptr{Cvoid}, generic_voidptr_a) == voidptr_a
@test convert(AMDGPU.DevicePtr{Cvoid}, voidptr_a) == generic_voidptr_a
@test convert(AMDGPU.DevicePtr{Cvoid,AS.Global}, voidptr_a) == global_voidptr_a

# between device pointers

@test_throws ArgumentError convert(typeof(local_voidptr_a), global_voidptr_a)
@test convert(typeof(generic_voidptr_a), generic_voidptr_a) == generic_voidptr_a
@test convert(typeof(global_voidptr_a), global_voidptr_a) == global_voidptr_a
@test Base.unsafe_convert(typeof(local_voidptr_a), global_voidptr_a) == local_voidptr_a

@test convert(typeof(global_voidptr_a), global_intptr_b) == global_voidptr_b
@test convert(typeof(generic_voidptr_a), global_intptr_b) == generic_voidptr_b
@test convert(typeof(global_voidptr_a), generic_intptr_b) == global_voidptr_b

@test convert(AMDGPU.DevicePtr{Cvoid}, global_intptr_b) == global_voidptr_b

end

end


text:
function wda_smacof(Δ; W = nothing, p = 2, X = nothing, anchors = nothing, ε = 1e-6, α = 0.9, Tmin = 1e-6, itmax = 100)
    n = size(Δ, 1)

    # Generate random initial mapping
    Xₖ = isnothing(X) ? rand(n, p) : X
    Dₖ = dist(Xₖ)

    if isnothing(W)
        W = ones(n, n)
    end
    I = findall(W .< 1e-4)

    # Initialize T
    Tₖ = (1 - eps()) *  maximum(Δ / sqrt(2 * p))
    Δₖ = zeros(size(Δ))

    V̇ = - Matrix(W)
    for i in 1:n
        V̇[i, i] = -1.0
        V̇[i, i] = sum(view(V̇, :, i))
    end
    B = zeros(n, n)

    # Storage
    Xᵢ = rand(n, p)
    rᵢ = zeros(n, p)
    dᵢ = zeros(n, p)
    Vd = zeros(n, p)
    k = 1
    while true
        if Tₖ < Tmin
            break
        end
        for i in eachindex(Δ)
            i in I && continue
            Δₖ[i] = max(0, Δ[i] - Tₖ * sqrt(2p))
        end

        σ₀ = 0.0
        σ₁ = stress(Δₖ, Dₖ, W)

        while abs(σ₁ - σ₀) > ε
            # fill!(B, 0.0)
            for i in eachindex(B)
                B[i] = - W[i] * Δₖ[i] / (Dₖ[i] + eps())
            end
            for i in 1:n
                B[i, i] = 0.0
                B[i, i] = - sum(view(B, :, i))
            end
            # Conjugate Gradient method
            # @timeit to "r" rᵢ[:] = B * Xₖ - V̇ * Xᵢ
            mul!(rᵢ, B, Xₖ)
            mul!(rᵢ, V̇, Xₖ, -1.0, 1.0)

            dᵢ[:] = rᵢ
            for i in 1:p
                βᵢ = 1 / dot(rᵢ, rᵢ)
                mul!(Vd, V̇, dᵢ)
                αᵢ = 1 / dot(dᵢ, Vd) / βᵢ
                axpy!(αᵢ, dᵢ, Xᵢ)
                mul!(rᵢ, V̇, dᵢ, αᵢ, 1.0)
                βᵢ *= dot(rᵢ, rᵢ)
                rmul!(dᵢ, βᵢ)
                axpy!(1.0, rᵢ, dᵢ)
                end
            copyto!(Xₖ, Xᵢ)
            dist!(Dₖ, Xₖ)
            σ₀ = σ₁
            σ₁ = stress(Δₖ, Dₖ, W)
        end

        Tₖ *= α
        k > itmax && break
        k += 1
        println(k, "\t", σ₁)
    end
    smacof(Δ, Xinit = Xₖ, anchors = anchors)
end


text:
## Gtk implementation

include("gtk-addons.jl")

## TODO
## * dialogs
## * others

XXX() = error("not defined")
XXX(msg) = println(msg)
## Icons
function get_icon(::MIME"application/x-gtk", o::StockIcon)
    if isa(o.nm, Nothing)
        Gtk.GtkImage()
    else
        file = Pkg.dir("JGUI", "icons", string(o.theme), string(o.nm) * ".png")
        @GtkImage(file)
    end
end
function get_icon(::MIME"application/x-gtk", o::FileIcon)
    @GtkImage(o.file)
end

## Widget methods
getEnabled(::MIME"application/x-gtk", o::Widget) = getproperty(o[:widget], :sensitive,Bool)
setEnabled(::MIME"application/x-gtk", o::Widget, value::Bool) = setproperty!(o[:widget], :sensitive, value)

getVisible(::MIME"application/x-gtk", o::Widget) =  getproperty(o[:widget], :visible, Bool)
function setVisible(::MIME"application/x-gtk", o::Widget, value::Bool)
    setproperty!(o[:widget], :visible,  value)
    showall(o[:widget])
end

function getSize(::MIME"application/x-gtk", o::Widget)
    [size(o[:block])...]
end

setSize(::MIME"application/x-gtk", o::Widget, value)  =  Gtk.G_.size_request(o[:block], value...)

getFocus(::MIME"application/x-gtk", o::Widget) = getproperty(o[:widget],:has_focus,Bool)

function setFocus(::MIME"application/x-gtk", o::Widget, value::Bool)
    value &&
    ccall((:gtk_widget_grab_focus, Gtk.libgtk), Void, (Ptr{Gtk.GObject},), o[:widget])
end

## Does not preserve types! (1,"one") -> [1, "one"]
getContext(::MIME"application/x-gtk", o::Widget) = o.attrs[:context]
function setContext(::MIME"application/x-gtk", o::Widget, ctx)
     o.attrs[:context] = ctx
end
## this is called when a custom context menu is requested. Use pt to add informationt to
## a widget's context
update_context(::MIME"application/x-qt", o::Widget, pt) = nothing

getWidget(::MIME"application/x-gtk", o::Widget) = o.o
getBlock(::MIME"application/x-gtk", o::Widget) = o.block

function setSizepolicy(::MIME"application/x-gtk", o::Widget, policies)
    o.attrs[:sizepolicy] = policies
    ## modify widget!
    if Gtk.gtk_version >= 3
        hexpand = policies[1] == :expand ? true : false
        vexpand = policies[2] == :expand ? true : false
        Gtk.G_.hexpand(o[:block], hexpand)
        Gtk.G_.vexpand(o[:block], vexpand)
    else
        "XXX"
    end
end

function align_gtk_widget(o::Widget; xscale=1, yscale=1)
    map_align = {:left => 0.0,
                 :top => 0.0,
                 :center => 0.5,
                 :right => 1.0,
                 :bottom => 1.0,
                 :justify => 0.5,
                 nothing => 0.5
                 }

    align = [map_align[k] for k in o[:alignment]]

    if Gtk.gtk_version >= 3
        o.block # XXX
#        Gtk.G_.halign(o[:block], align[1])
#        Gtk.G_.valign(o[:block], align[2])
    else
        al = GtkAlignment(align[1], align[2], xscale, yscale)

        if !isa(o[:spacing], Nothing)
            setproperty!(al, :left_padding, o[:spacing][1])
            setproperty!(al, :right_padding, o[:spacing][2])
        end
        if !isa(o[:spacing], Nothing)
            setproperty!(al, :top_padding, o[:spacing][2]) # is 2 right? XXX
            setproperty!(al, :bottom_padding, o[:spacing][2])
        end

        push!(al, o.block)
        al
    end
end
    ## Containers

## get Gtk layout
getLayout(::MIME"application/x-gtk", widget::Container) = widget[:widget][:layout]()
getLayout(widget::Container) = getLayout(widget.toolkit, widget)

## Window
function window(::MIME"application/x-gtk"; visible::Bool=true, kwargs...)
    widget = @GtkWindow("")
    !visible && setproperty!(widget, :visible, false)

    block = @GtkBox(true)
    push!(widget, block)

## XXX clean this up by setting size on object...
    menu_block = @GtkBox(false)
    ccall((:gtk_box_pack_start, Gtk.libgtk), Void,
          (Ptr{Gtk.GObject}, Ptr{Gtk.GObject}, Bool, Bool, Int),
          block, menu_block, false, false, 0)

    main_block = @GtkBox(false)
    push!(block, main_block)

    status_block = @GtkBox(false)
    ccall((:gtk_box_pack_start, Gtk.libgtk), Void,
          (Ptr{Gtk.GObject}, Ptr{Gtk.GObject}, Bool, Bool, Int),
          block, status_block, false, false, 0)

    (widget, block)
end

### window methods
function raise(::MIME"application/x-gtk", o::Window)
    setproperty!(o[:widget], :visible, true)
    showall(o[:widget])
end
lower(::MIME"application/x-gtk", o::Window) = setproperty!(o[:widget], :visible, false)
destroy_window(::MIME"application/x-gtk", o::Window) = Gtk.destroy(o[:widget])

## window properties
getTitle(::MIME"application/x-gtk", o::Window) = getproperty(o[:widget], :title, String)
function setTitle(::MIME"application/x-gtk", o::Window, value::String)
    setproperty!(o[:widget], :title, value)
end

## XXX
getPosition(::MIME"application/x-gtk", o::Window) = [o[:widget][:x](), o[:widget][:y]()]
setPosition(::MIME"application/x-gtk", o::Window, value::Vector{Int}) = o[:widget][:move](value[1], value[2])

## XXX
function getModal(::MIME"application/x-tcltk", o::Window)

end

## XXX
function setModal(::MIME"application/x-tcltk", o::Window, value::Bool)
end

function set_child(::MIME"application/x-gtk", parent::Window, child::Widget)
    if Gtk.gtk_version >= 3
        Gtk.G_.hexpand(child.block, true)
        Gtk.G_.vexpand(child.block, true)
    else
        "XXX"
    end
    Gtk.push!(parent.block[2], child.block)
    showall(parent.o)
end

## don't expand in all directions for box containers. Hacky!
function set_child(::MIME"application/x-gtk", parent::Window, child::BoxContainer)
    if child.attrs[:direction] == :horizontal
        Gtk.G_.vexpand(child.block, true)
    else
         Gtk.G_.hexpand(child.block, true)
    end
    Gtk.push!(parent.block[2], child.block)
    showall(parent.o)
end

## for BinContainer, only one child we pack and expand...
function set_child(::MIME"application/x-gtk", parent::BinContainer, child::Widget)
    if Gtk.gtk_version >= 3
        Gtk.G_.hexpand(child.block, true)
        Gtk.G_.vexpand(child.block, true)
    else
        "XXX"
    end
    Gtk.push!(parent[:widget], child.block)
    parent[:visible] && showall(parent[:widget])
end

## Container

## Label frame
function labelframe(::MIME"application/x-gtk", parent::BinContainer,
                    label::String; alignment::Union(Nothing, Symbol)=nothing)
    widget = @GtkFrame(label)

    if isa(alignment, Symbol)
        widget[:label_xalign] = alignment == :left ? 0.0 :(alignment == :right ? 1.0 : 0.5)
    end
    (widget, widget)
end

## Boxes
function boxcontainer(::MIME"application/x-gtk", parent::Container, direction)
    widget = @GtkBox(direction == :vertical)
    (widget, widget)
end

## set padx, pady for all the children
function setSpacing(::MIME"application/x-gtk", parent::BoxContainer, px::Vector{Int})
    setproperty!(parent[:widget], :spacing, px[1]) # first one only
end

##
function setMargin(::MIME"application/x-gtk", parent::BoxContainer, px::Vector{Int})
    setproperty!(parent[:widget], :border_width, px[1]) # first only
end

## stretch, strut, spacing XXX
function addspacing(::MIME"application/x-gtk", parent::BoxContainer, val::Int)
    box = parent[:widget]
    if parent.attrs[:direction] == :horizontal
        child = vbox(parent)
        child[:size] = [val, 1]
        push!(parent, child)
    else
        child = hbox(parent)
        child[:size] = [1, val]
        push!(parent, child)
    end
end

function addsstrut(::MIME"application/x-gtk", parent::BoxContainer, val::Int)
    box = parent[:widget]
    if parent.attrs[:direction] == :horizontal
        child = vbox(parent)
        child[:size] = [1, val]
        push!(parent, child)
    else
        child = hbox(parent)
        child[:size] = [val, 1]
        push!(parent, child)
    end
end
function addstretch(::MIME"application/x-gtk", parent::BoxContainer, val::Int)
    box = parent[:widget]
    if parent.attrs[:direction] == :horizontal
        child = vbox(parent)
        child[:sizepolicy] = (:expand, :fixed)
        push!(parent, child)
    else
        child = hbox(parent)
        child[:sizepolicy] = (:fixed, :expand)
        push!(parent, child)
    end
end

function insert_child(::MIME"application/x-gtk", parent::BoxContainer, index, child::Widget)

    ## XXX -- update this for v3
    ## we use Gtk.Alignment until deprecated
    expand, fill, padding = true, true, 0

    xscale, yscale = (parent[:direction] == :horizontal) ? (0.0, 1.0) : (1.0, 0.0)
    if parent[:direction] == :horizontal
        if child[:sizepolicy][1] == :expand
            expand=true
            xscale= 1.0
        end
    else
        if child[:sizepolicy][2] == :expand
            expand = true
            yscale = 1.0
        end
    end
    fill = expand

    child_widget = child[:block]
#    child_widget = align_gtk_widget(child, xscale=xscale, yscale=yscale)

    ccall((:gtk_box_pack_start, Gtk.libgtk),
              Void,
              (Ptr{Gtk.GObject},Ptr{Gtk.GObject}, Bool, Bool,Int64),
              parent[:widget], child_widget, expand, fill, padding)

    ccall((:gtk_box_reorder_child, Gtk.libgtk), Void,
          (Ptr{Gtk.GObject},Ptr{Gtk.GObject},Int64),
          parent[:widget], child_widget, index - 1)

    show(child_widget)
    setproperty!(child[:widget], :visible, getproperty(parent[:widget], :visible, Bool))

end

function remove_child(::MIME"application/x-gtk", parent::Container, child::Widget)
    delete!(parent[:widget], child[:widget])
end

####
## make a gridq
function grid(::MIME"application/x-gtk", parent::Container)
    ## should dispatch on gtk version type!
    if Gtk.gtk_version >= 3
        ## use ...
        widget = @GtkGrid()
    else
        widget = @GtkTable()
    end
    (widget, widget)
end

## size of grid
function grid_size(::MIME"application/x-gtk", widget::GridContainer)
    ## should dispatch on gtk version type!
    if Gtk.gtk_version >= 3
       widget.attrs[:size]      # internal book-keeping, no method
    else
        tbl = widget[:widget]
        [getproperty(tbl, :n_rows, Int), getproperty(tbl, :n_columns, Int)]
    end
end

## grid spacing
function setSpacing(::MIME"application/x-gtk", parent::GridContainer, px::Vector{Int})
    if Gtk.gtk_version >= 3
        ## use ... XXX

    else
        tbl = widget[:widget]
        setproperty(tbl, :column_spacing, px[1])
        setproperty(tbl, :row_spacing, px[2])
    end
end

##XXXXXXX
column_minimum_width(::MIME"application/x-gtk", object::GridContainer, j::Int, width::Int) = XXX("column_minimum_width")

row_minimum_height(::MIME"application/x-gtk", object::GridContainer, j::Int, height::Int) = XXX("row_minimum_width")

column_stretch(::MIME"application/x-gtk", object::GridContainer, j::Int, weight::Int) = XXX("Column_stretch")

row_stretch(::MIME"application/x-gtk", object::GridContainer, i::Int, weight::Int) = XXX("row_stretch")

## Need to do something to configure rows and columns
## grid add child
function grid_add_child(::MIME"application/x-gtk", parent::GridContainer, child::Widget, i, j)

    ## alignment???
    parent[:widget][j, i] = align_gtk_widget(child) # reversed!!!

    ## manage size for v3
    sz = parent.attrs[:size]
    sz[1] = max(sz[1], i...)
    sz[2] = max(sz[2], j...)
    parent.attrs[:size] = sz

    ## need to show XXX will be fixed
    for i in parent[:widget] show(i) end
end

## XXX  -- hack this in during add?
function grid_get_child_at(::MIME"application/x-gtk", parent::GridContainer, i::Int, j::Int)
    error("No method itemAtPosition in Gtk.")
end

##################################################
## XXX too much code duplication with grid
function formlayout(::MIME"application/x-gtk", parent::Container)
    if Gtk.gtk_version >= 3
        widget = @GtkGrid()
    else
        widget = @GtkTable()
    end
    (widget, widget)
end

## XXX unify alignment here...
function formlayout_add_child(::MIME"application/x-gtk", parent::FormLayout, child::Widget, label::Union(Nothing, String))
    if Gtk.gtk_version >= 3
        nrows = parent.attrs[:nrows]
        if isa(label, Nothing)
            label == ""
        end
        ## XXX spacing...
        lab = @GtkLabel(label)
        setproperty!(lab, :xalign, 1.0)
        parent[:widget][1, nrows+1] = lab
        parent[:widget][2, nrows+1] = child.block

        parent.attrs[:nrows] = nrows + 1

    else
        ## gtk2
        if length(parent[:widget]) == 0
            nrows = 0
        else
            nrows = getproperty(parent[:widget], :n_rows, Int)
        end

        if !isa(label, Nothing)
            label = @GtkLabel(label)
            al = @GtkAlignment(1.0, 0.0, 1.0, 1.0)
            setproperty!(al, :right_padding, 2)
            push!(al, label)
            parent[:widget][1, nrows+1] = al
        end
        parent[:widget][2, nrows+1] = align_gtk_widget(child, xscale=1.0, yscale=0.0)  ## reversed

    end
    map(showall,  parent[:widget])

end

function setSpacing(::MIME"application/x-gtk", object::FormLayout, px::Vector{Int})
    if Gtk.gtk_version >= 3
        ## use ... XXX see grid..

    else
        tbl = widget[:widget]
        setproperty(tbl, :column_spacing, px[1])
        setproperty(tbl, :row_spacing, px[2])
    end
end

## Notebook
function notebook(::MIME"application/x-gtk", parent::Container, model::Model)
    widget = @GtkNotebook()

    connect(model, "valueChanged", value -> Gtk.G_.current_page(widget, value-1))
    signal_connect(widget, :switch_page) do obj, ptr, page, args...
        setValue(model, int(page) + 1)
        false
    end

    (widget, widget)
end

## XXX icon, order?
function notebook_insert_child(::MIME"application/x-gtk", parent::NoteBook, child::Widget, i::Int, label::String)
    i = i > length(parent)  ? length(parent) : i-1
    insert!(parent[:widget], i + 1, child.block, label)

    map(show, parent[:widget])
end

function notebook_remove_child(::MIME"application/x-gtk", parent::NoteBook, child::Widget)
    ## no findfirst
    n = length(parent.children)
    index = filter(i -> parent.children[i] == child, 1:n)
    splice!(parent[:widget], index[1])
end

##################################################
## Widgets
function label(::MIME"application/x-gtk", parent::Container, model::Model)
    widget = @GtkLabel(string(getValue(model)))

    connect(model, "valueChanged") do value
        Gtk.G_.text(widget, string(value))
    end

    (widget, widget)
end

function setAlignment(::MIME"application/x-gtk", o::Label, value)
    als = [:left=>0.0, :right=>1.0, :center=>0.5, :justify=>0.5,
           :top=>0.0, :bottom=>1.0, nothing=>0.5
           ]

    setproperty!(o[:widget], :xalign, als[value[1]])
    setproperty!(o[:widget], :yalign, als[value[2]])
end

## separator
function separator(::MIME"application/x-gtk", parent::Container; orientation::Symbol=:horizontal)
    widget = @GtkLabel("----")
    return(widget, widget)
    ## XXX not yet in Gtk.jl
#    if orientation == :horizontal
#        widget = @GtkHSeparator()
#    else
#        widget = @GtkVSeparator()
#    end

    (widget, widget)
end

## Controls
function button(::MIME"application/x-gtk", parent::Container, model::Model)
    widget = @GtkButton(string(getValue(model)))
    connect(model, "valueChanged",
            value -> setproperty!(widget,:label, string(value)))
    signal_connect(widget, :clicked) do obj, args...
        notify(model, "clicked")
        nothing
    end

    (widget, widget)
end

## XXX
function setIcon(::MIME"application/x-gtk", widget::Button, icon::Union(Nothing, Icon); kwargs...)
    if isa(icon, Nothing)
        icon = @GtkImage()
    else
        if isa(icon.theme, Nothing)
            icon.theme = widget[:icontheme]
        end
        icon = get_icon(widget.toolkit, icon)
    end
    Gtk.G_.image(widget[:widget], icon)
    show(icon)
end

## Linedit
function lineedit(::MIME"application/x-gtk", parent::Container, model::Model)
    widget = @GtkEntry()
    setproperty!(widget, :text, string(getValue(model)))
    connect(model, "valueChanged", value -> setproperty!(widget, :text, string(value)))

    ## SIgnals: keyrelease (keycode), activated (value), focusIn, focusOut, textChanged
    signal_connect(widget, :key_release_event) do obj, e, args...
        if e.keyval == Gtk.GdkKeySyms.Return
            notify(model, "editingFinished", getValue(model))
        else
            txt = getproperty(widget, :text, String)
            setValue(model, txt) # textChanged
            notify(model, "textChanged", txt)
            notify(model, "valueChanged", txt)
        end

        false
    end

    signal_connect(widget, :focus_out_event) do widget, e, args...
        notify(model, "focusOut", getValue(model))
        notify(model, "editingFinished", getValue(model))
        return(false)
    end
    signal_connect(widget, :focus_in_event) do widget, e, args...
        notify(model, "focusIn")
        return(false)
    end

    signal_connect(widget, :button_press_event) do widget, e, args...
        notify(model, "clicked")
        return(false)
    end

    connect(model, "placeholderTextChanged") do txt
        if Gtk.gtk_version >= 3
            setproperty!(widget, :placeholder_text, txt)
        end
    end

    (widget, widget)
end

## XXX needs GtkEntryCompletion XXX It is there now!
function setTypeahead(::MIME"application/x-gtk", obj::LineEdit, items::Vector)
    compl = @GtkEntryCompletion()
    ## make model, set as model
    mod = @GtkListStore(eltype(items))
    [push!(mod, (item,)) for item in items]
    Gtk.G_.model(compl, mod)
    Gtk.G_.text_column(compl, 0)
    ## set' completion
    Gtk.G_.completion(obj[:widget], compl)
end

## Text edit
function textedit(::MIME"application/x-gtk", parent::Container, model::Model)
    widget = @GtkTextView()
    block = @GtkScrolledWindow()
    [setproperty!(widget, x, true) for  x in [:hexpand, :vexpand]]
    push!(block, widget)

    buffer = getproperty(widget, :buffer, GtkTextBuffer)

    get_value() = join([i for i in buffer], "")

    set_text(buffer, value::String) = setproperty!(buffer, :text, value)
    set_text{T <: String}(buffer, value::Vector{T}) = set_text(buffer, join(value, "\n"))

    connect(model, "valueChanged", value -> set_text(buffer, value))
    signal_connect(buffer, :changed) do obj, args...
        model.value = get_value()

        return(nothing)
    end

    signal_connect(widget, :focus_out_event) do obj, e, args...
        notify(model, "focusOut", getValue(model))
        notify(model, "editingFinished", getValue(model))
        return(false)
    end
    signal_connect(widget, :focus_in_event) do obj, e, args...
        notify(model, "focusIn")
        return(false)
    end

    signal_connect(widget, :key_release_event) do obj, e, args...
        txt = get_value()
        setValue(model, txt) # textChanged
        notify(model, "textChanged", txt)
        return(false)
    end

    signal_connect(widget, :button_press_event) do obj, e, args...
        notify(model, "clicked")
        return(false)
    end

    (widget, block)
end

## used to add to non-editable text eidt
function push_textedit(::MIME"application/x-gtk", o::TextEdit, value::String)
    widget = o[:widget]
    buffer = getproperty(widget, :buffer, Gtk.GtkTextBuffer)
    setproperty!(widget, :editable, false)

    insert!(buffer, value)
end

function push_textedit(::MIME"application/x-gtk", o::TextEdit, value::CairoGraphics)
    widget = o[:widget]
    child = value[:widget]

    buffer = getproperty(widget, :buffer, Gtk.GtkTextBuffer)

    ## make an end iter
    enditer = Gtk.mutable(Gtk.GtkTextIter)
    ccall((:gtk_text_buffer_get_iter_at_offset, Gtk.libgtk),Void,
          (Ptr{Gtk.GObject},Ptr{Gtk.GtkTextIter},Cint),buffer,enditer,-1)

    anchor = ccall((:gtk_text_buffer_create_child_anchor, Gtk.libgtk),Ptr{Void},
                   (Ptr{Gtk.GObject},Ptr{Gtk.GtkTextIter}),buffer, enditer)

    ccall((:gtk_text_view_add_child_at_anchor, Gtk.libgtk),Void,
          (Ptr{Gtk.GObject},Ptr{Gtk.GObject},Ptr{Void}), widget, child, anchor)

    setproperty!(child, :visible, true)
end

## checkbox
function checkbox(::MIME"application/x-gtk", parent::Container, model::Model, label::Union(Nothing, String))
    widget = @GtkCheckButton()
    setproperty!(widget, :label, (isa(label, Nothing) ? "" : label))
    setproperty!(widget, :active, model.value)

    connect(model, "valueChanged", value -> setproperty!(widget, :active, value))
    signal_connect(widget, :toggled) do obj, args...
        setValue(model, getproperty(widget, :active,Bool))
    end
    (widget, widget)
end
getLabel(::MIME"application/x-gtk", o::CheckBox) = getproperty(o[:widget],:label,String)
setLabel(::MIME"application/x-gtk", o::CheckBox, value::String) = setproperty!(o[:widget],:label, string(value))

## radiogroup
## XXX We don't use radiogroup. This would be useful if we allowed user to
## change items for selection
function radiogroup(::MIME"application/x-gtk", parent::Container, model::VectorModel; orientation::Symbol=:horizontal)

    choices = map(string, copy(model.items))

    g = @GtkBox(orientation == :vertical)

    btns = [@GtkRadioButton(shift!(choices))]
    while length(choices) > 0
        push!(btns, @GtkRadioButton(btns[1], shift!(choices)))
    end
    map(u->push!(g, u), btns)

    selected = findfirst(model.items, model.value)
    setproperty!(btns[selected], :active, true)

    connect(model, "valueChanged") do value
        ## need to look up which button to set
        selected = findfirst(model.items, value)
        if selected == 0
            error("$value is not one of the labels")
        else
            setproperty!(btns[selected], :active, true)
        end
    end
    for btn in btns
        signal_connect(btn, :toggled) do obj, args...
            if getproperty(obj, :active, Bool)
                label = getproperty(obj, :label, String)
                ## label is string, item may be numeric...
                selected = findfirst(map(string, model.items), label)
                setValue(model, model.items[selected])
            end
        end
    end

    ## XXX must call vsibile here
    setproperty!(g, :visible,  true)
    showall(g)

    (g, g)
end

## buttongroup XXX
function buttongroup(::MIME"application/x-gtk", parent::Container, model::VectorModel; exclusive::Bool=false)
    block = @GtkBox(false)

    function exclusive_handler(btn, xs...)
        val =  getproperty(btn, :active, Bool)
        btns = collect(block)
        if !val
            ## set button state
            for b in btns
                setproperty!(b, :active, b==btn)
            end
            ## set widget state
            model[:value] = getproperty(btn, :label, String)
        end
        true                    # stop propogation of events
    end

    function non_exclusive_handler(btn)
        buttons = collect(block)
        chosen = Bool[getproperty(btn, :active, Bool) for btn in buttons]
        names = [getproperty(btn, :label, String) for btn in buttons]
        model[:value] = names[chosen]
    end

    choices = map(string, copy(model.items))
    for choice in choices
        btn = Gtk.@GtkToggleButton(choice)
        value = model[:value]
        if !isa(value, Nothing)
            if exclusive value = [value] end
            setproperty!(btn, :active, choice in value)
        end
        if exclusive
            signal_connect(exclusive_handler, btn, :button_press_event)
        else
            signal_connect(non_exclusive_handler, btn, :toggled)
        end

        push!(block, btn)
    end

    connect(model, "valueChanged") do values
        if exclusive
            values = [values]
        end

        buttons = collect(block)
        for button in buttons
            label = getproperty(button, :label, String)
            setproperty!(button, :active, label in values)
        end
    end

    (block, block)

end

##
function combobox(::MIME"application/x-gtk", parent::Container, model::VectorModel; editable::Bool=false)

    ## No way to get editable!
    widget = @GtkComboBoxText(editable)

    set_index(index) = ccall((:gtk_combo_box_set_active, Gtk.libgtk), Void,
                             (Ptr{Gtk.GObject}, Cint), widget, index-1)
    function set_value(value)
        if isa(value, Nothing)
            set_index(0)
            return
        end
        if getproperty(widget, :has_entry, Bool)
            entry = Gtk.G_.child(widget)
             Gtk.G_.text(entry, value)
        else
            i = findfirst(model.items, value) # need index
            set_index(i)
        end
    end

    ## queue like manipulation
    ## push! already defined. Need a length -- but that would need GtkTreeModel
    Base.shift!(widget::Gtk.GtkComboBoxText) = ccall((:gtk_combo_box_text_remove, Gtk.libgtk), Void, (Ptr{Gtk.GObject},Int), widget, 0)

    function set_items(items, old_items)
        while length(old_items) > 0
            shift!(old_items)
            shift!(widget)
        end

        for i in items
            push!(widget, string(i))
        end
## XXX        set_index(0)            # clear selection?
    end

    set_items(model.items, [])
    set_value(model.value)

    connect(model, "valueChanged", value -> set_value(value))
    connect(model, "itemsChanged", set_items)

    signal_connect(widget, :changed) do obj, args...
        ## are we editable?
        if getproperty(widget, :has_entry, Bool)
            ## get active text
            txt = bytestring(Gtk.G_.active_text(widget))
            setValue(model, txt)
        else
            index = getproperty(widget, :active, Int)
            if index == -1
                setValue(model, nothing)
            else
                setValue(model, model.items[index + 1])
            end
        end
        false
    end

    (widget, widget)
end

## slider
## model stores value, slider is in 1:n
function slider(::MIME"application/x-gtk", parent::Container, model::VectorModel; orientation::Symbol=:horizontal)
    items = model.items
    initial = model.value
    n = length(items)
    orient = orientation == :horizontal ? false : true

    widget = @GtkScale(orient, 1, n, 1)
    Gtk.G_.draw_value(widget, false)

    get_value() = Gtk.G_.value(widget)
    set_value(value) = Gtk.G_.value(widget, value)

    connect(model, "valueChanged") do value ## value is in model.items
        ## have to find index from items
        i = indmin(abs(model.items .- value))
        set_value(i)
    end

    tooltip_format(x) = string(round(x, 2))
    tooltip_format(x::Int) = string(x)
    tooltip_format(x::String) = x

    ## value is index
    signal_connect(widget, :value_changed) do obj, args...
        value = iround(get_value())
        model_value = model.items[value]
        Gtk.G_.tooltip_text(widget, tooltip_format(model_value))
        setValue(model, model_value)
        return(false)
    end
    (widget, widget)
end

## XXX
## out model stores index
function slider2d(::MIME"application/x-gtk", parent::Container, model::TwoDSliderModel)
    XXX("No slider2d in gtk")
end
getValue(::MIME"application/x-gtk", widget::Slider2D) = getValue(widget.model)
setValue(::MIME"application/x-gtk", widget::Slider2D, value) = setValue(widget.model, value)

## spinbox
function spinbox(::MIME"application/x-gtk", parent::Container, model::ItemModel, rng::Range)

    widget = @GtkSpinButton(rng)

    signal_connect(widget, :value_changed) do obj, args...
        value = getproperty(widget, :value, eltype(rng))
        setValue(model, value)
    end
    connect(model, "valueChanged", value -> setproperty!(widget, :value, value))

    (widget, widget)
end

## cairographic
function cairographic(::MIME"application/x-gtk", parent::Container,
                      model::EventModel; width::Int=480, height::Int=400)

    widget = @GtkCanvas(width, height)
    (widget, widget)
end

## Views XXX
## StoreProxyModel

function store_proxy_model(parent, store::Store)

    m = @GtkListStore(store.types...)

    ## add in any in store
    for record in store.items
        push!(m, tuple(record...))
    end

   ## connect model to store so that store changes propogate XXX
    connect(store.model, "rowInserted") do i
        record = store.items[i]
        push!(m, tuple(record...))
    end

    connect(store.model, "rowRemoved") do i
        splice!(m, i)
    end

    function rowUpdated(i::Int)
        record = store.items[i]
        map(j -> m[i,j] = record[j], 1:length(record))
    end
    connect(store.model, "rowUpdated", rowUpdated)

   ## return model
   m
end

## storeview XXX
function make_column{T <: String}(::Type{T}, nm::String, col::Int)
    cr =  @GtkCellRendererText()
    col = @GtkTreeViewColumn(nm, cr, {"text" => col-1})
    col
end

function make_column{T<:Number}(::Type{T}, nm::String, col::Int)
    cr =  @GtkCellRendererText()
    col = @GtkTreeViewColumn(nm, cr, {"text" => col-1})
    col
end

function make_column(::Type{Bool}, nm::String, col::Int)
    cr = @GtkCellRendererToggle()
    col = @GtkTreeViewColumn(nm, cr, {"active"=> col-1 })
end

function storeview(::MIME"application/x-gtk", parent::Container, store::Store, model::ItemModel; kwargs...)

    ## Set up view
    widget = @GtkTreeView()
    block = @GtkScrolledWindow()
    [setproperty!(widget, x, true) for  x in [:hexpand, :vexpand]]
    push!(block, widget)

    ## set up model
    m = store_proxy_model(widget, store)
    Gtk.G_.model(widget, m)

    ## selection model
    sel = Gtk.G_.selection(widget)
    ## add cell renderers

    for j in 1:length(store.types)
        col = make_column(store.types[j], "Column $j", j)
        signal_connect(col, :clicked) do _
            notify(model, "headerClicked", j)
        end

        push!(widget, col)
    end

    ## configure
#    widget[:setAlternatingRowColors](true)
#    widget[:horizontalHeader]()[:setStretchLastSection](true)
    Gtk.G_.headers_clickable(widget, true)

    ## notify JGUI model of changes to selection
    id = signal_connect(sel, :changed) do sel
#        indices = selected(widget) # XXX Errors XXX
        indices = Int[]
        setValue(model, indices; signal=false) # model holds selection, store.model.value the value
        notify(model, "selectionChanged", indices)
        false
    end

    # ## connect model to view on index changed
    connect(model, "valueChanged") do indices
        if Gtk.G_.mode(sel) == Gtk.GConstants.GtkSelectionMode.MULTIPLE
            unselectall!(widget)
        else
            if indices == [0] & Gtk.hasselection(sel)
                m, iter = selected(sel)
                ccall((:gtk_tree_selection_unselect_iter, Gtk.libgtk), Void,
                      (Ptr{Gtk.GObject}, Ptr{Gtk.GtkTreeIter}),
                      sel, iter)
                return
            end
        end

        indices = filter(x -> 0 < x <= length(store), indices)
        map(index -> select!(widget, index), indices)
    end

    ## clicked and doubleClicked
    ## issue with clicking on the header!
    signal_connect(widget, :button_press_event) do w, e
        row, col = tree_view_row_col_from_x_y(w, int(e.x), int(e.y))

        if row > 0
            if e.event_type == Gtk.GdkEventType.GDK_2BUTTON_PRESS
                notify(model, "doubleClicked", row, col)
            else
                notify(model, "clicked", row, col)
            end
        end
        return false
    end

    (widget, block)
end

function getSelectmode(::MIME"application/x-gtk", s::ModelView)
    sel = Gtk.G_.selection(s[:widget])
    if Gtk.G_.mode(sel) == Gtk.GConstants.GtkSelectionMode.SINGLE
        return :single
    else
        return :multiple
    end
end
## val is single, multiple
function setSelectmode(::MIME"application/x-gtk", s::ModelView, val::Symbol)
    sel =  Gtk.G_.selection(s[:widget])
    Gtk.G_.mode(sel, val == :single ? Gtk.GConstants.GtkSelectionMode.SINGLE : Gtk.GConstants.GtkSelectionMode.MULTIPLE)
end

## getWidths
function getWidths(::MIME"application/x-gtk", s::StoreView)
    n = size(s.store)[2]
    [Gtk.G_.min_width(Gtk.G_.column(s.o,i-1)) for i in 1:n]
end

function setWidths(::MIME"application/x-gtk", s::StoreView, widths::Vector{Int})
    for (i, width) in enumerate(widths)
        Gtk.G_.min_width(Gtk.G_.column(s.o,i-1), width)
    end
end

function getNames(::MIME"application/x-gtk", s::StoreView)
    get_tree_view_names(s[:widget])
end

function setNames(::MIME"application/x-gtk", s::StoreView, nms::Vector)
    set_tree_view_names(s[:widget], nms)
end

## heights
function getHeights(::MIME"application/x-gtk", s::ModelView)
    XXX("no heights")
end

function setHeights(::MIME"application/x-gtk", s::ModelView, heights::Vector{Int})
    XXX("no heights")
end

function getHeadervisible(::MIME"application/x-gtk", s::StoreView)
    Gtk.G_.headers_visible(s[:widget])
end
function setHeadervisible(::MIME"application/x-gtk", s::StoreView, val::Bool)
    XXX("Warning, this seems broken...")
     Gtk.G_.headers_visible(s[:widget], val)
end

function getRownamesvisible(::MIME"application/x-gtk", s::StoreView)
    XXX("no rownames")
end
function setRownamesvisible(::MIME"application/x-qt", s::StoreView, val::Bool)
    XXX("no rownames")
end

function setIcon(::MIME"application/x-qt", s::StoreView, i::Int, icon::Icon)
    XXX("no icons")
end

## Tree view
function store_proxy_model(::MIME"application/x-gtk", store::TreeStore)
    ## proxy gtkTreeStore to JGUI::TreeStore

    gtkstore = Gtk.@TreeStore(String, store.types...)

    ## Intial synchronize jgui store with gtkstore
    function add_children(parentnode; node=nothing)
        if length(parentnode.children) > 0
            for i in 1:length(parentnode.children)
                child = parentnode.children[i]
                cnode = push!(gtkstore, tuple(child.text, child.data...), node)
                add_children(child, node=cnode)
            end
        end
    end
    add_children(store)

    ## connect tree store to gtkstore
    connect(store.model, "insertNode") do parent, ind, child
        ## how to get index from parent!!!
    insert!(gtkstore, node_to_path(parent), child.data, how=:parent, where=:after)
    end

    connect(store.model, "removeNode") do parent, ind
        if isa(parent, Union(TreeStore, Nothing))
            path = [ind]
        else
            path = [node_to_path(parent), ind]
        end
        splice!(gtkstore, path)
    end

    connect(store.model, "updatedNode") do node
        ## XXX how to update in place? I can delete then remove, but this seems wrong
        ind = node.index
        splice!(gtkstore, ind)
        insert!(gtkstore, ind, node.data, how=:sibling, where=:after)
    end

    gtkstore
end

## tpl: a template for the type, otherwise from tr.children[1]
function treeview(::MIME"application/x-gtk", parent::Container, store::TreeStore, model::ItemModel)

    ## get store
    gtkstore = store_proxy_model(parent.toolkit, store)

    ## view of store
    widget = @GtkTreeView(Gtk.TreeModel(gtkstore))
    block = @GtkScrolledWindow()
    [setproperty!(widget, x, true) for  x in [:hexpand, :vexpand]]
    push!(block, widget)

    ## make cell renderers
    ## key is a string
    rend = Gtk.@CellRendererText()
    col = Gtk.@TreeViewColumn("", rend, {"text" => 0})
    push!(widget, col)
    ## others
    for j in 1:length(store)
        col = make_column(store.types[j], "Column $j", j)
        signal_connect(col, :clicked) do _
            notify(model, "headerClicked", j)
        end
        push!(widget, col)
    end

    ## XXX
    function node_to_item(node)
        path_to_item(node_to_path(node))
    end
    function item_to_path(item)
        path = Int[]
        parent = item[:parent]()
        while !isa(parent, Nothing)
            unshift!(path, parent[:indexOfChild](item) + 1)
            item = parent
            parent = parent[:parent]()
        end
        unshift!(path, widget[:indexOfTopLevelItem](item) + 1)
        path
    end

    ##
    function expandNode(tview, gtkstore, node)
        ind = node_to_path(node)
        path = Gtk.path(Gtk.TreeModel(gtkstore), Gtk.iter_from_index(gtkstore, ind)[])
        ccall((:gtk_tree_view_expand_row, Gtk.libgtk), Bool,
              (Ptr{GObject}, Ptr{Gtk.GtkTreePath}, Cint),
              tview, path, false)
    end
    function collapseNode(tview, gtkstore, node)
        ind = node_to_path(node)
        path = Gtk.path(Gtk.TreeModel(gtkstore), Gtk.iter_from_index(gtkstore, ind)[])
        ccall((:gtk_tree_view_collapse_row, Gtk.libgtk), Bool,
              (Ptr{GObject}, Ptr{Gtk.GtkTreePath}),
              tview, path)
    end

    ###########################
    ## connect model and widget
    connect(model, "expandNode", expandNode)
    connect(model, "collapseNode", collapseNode)

    connect(model, "valueChanged") do value
        ## XXX set selection. Fails!! selection is matching
        return()
        selection = Gtk.G_.selection(widget)
        function path_from_index(index)
            ind = join(index - 1, ":")
            ccall((:gtk_tree_path_new_from_string,Gtk.libgtk), GtkTreePath, (Ptr{Uint8}, ), bytestring(ind))
        end
        path = path_from_index(value)
        ccall((:gtk_tree_selection_select_path, Gtk.libgtk), Void,
              (Gtk.GtkTreeSelection, Gtk.GtkTreePath), selection, path)

    end

    ##

    ## connect widget to model
    ## selection changed, set model
    selection = Gtk.G_.selection(widget)
    signal_connect(selection, :changed) do sel
        ## XXX this function call is a hack to be addressed at Gtk level
        index = JGUI.gtk_jgui_tree_selected(widget)
        notify(store.model, "valueChanged", index)
        false
    end

    ## connect expand node event to model
    signal_connect(widget, :row_activated) do view, path, col
        ## XXX this function call is a hack to be addressed at Gtk level
        index = JGUI.gtk_jgui_tree_selected(widget)
        notify(model, "activated", index)
        false
    end

    ## connect expand node event to model
    signal_connect(widget, :row_expanded) do view, path, col
        ## XXX this function call is a hack to be addressed at Gtk level
        index = JGUI.gtk_jgui_tree_selected(widget)
        notify(model, "nodeExpand", index)
        false
    end

    signal_connect(widget, :row_collapsed) do view, path, col
        ## XXX this function call is a hack to be addressed at Gtk level
        index = JGUI.gtk_jgui_tree_selected(widget)
        notify(model, "nodeCollapse", index)
    end

    ## more issues?
    signal_connect(widget, :button_press_event) do widget, event
        ## look up path, column
        ## check state for double, single click
        ## notify
        if event.event_type in [Gtk.GdkEventType.GDK_2BUTTON_PRESS, Gtk.GdkEventType.GDK_BUTTON_PRESS]
            double = event.event_type == Gtk.GdkEventType.GDK_2BUTTON_PRESS
            signal = double ? "doubleClicked" : "clicked"
            index = JGUI.gtk_jgui_tree_selected(widget)
            column = 0          # XXX Get column!!!
            notify(model, signal, index, column)
        end
        false
    end

    (widget, block)
end

## Properties
function getNames(::MIME"application/x-gtk", tr::TreeView)
    tview = tr[:widget]
    n = length(tr.store)
    function get_name(i)
        col = Gtk.G_.column(tview, i)
        bytestring(Gtk.G_.title(col))
    end
    String[get_name(i) for i in 1:n]
end

function setNames{T<:String}(::MIME"application/x-gtk", tr::TreeView, nms::Vector{T})
    tview = tr[:widget]
    @assert length(tview.store) == length(names)
    for (i, nm) in enumerate(names)
        col = Gtk.G_.column(tview, i)
        Gtk.G_.title(col, nm)
    end

end

function getKeyname(::MIME"application/x-gtk", tr::TreeView)
    tview = tr[:widget]
    col = Gtk.G_.column(tview, 0)
    bytestring(Gtk.G_.title(col))
end
function setKeyname(::MIME"application/x-gtk", tr::TreeView, nm::String)
    tview = tr[:widget]
    col = Gtk.G_.column(tview, 0)
    Gtk.G_.title(col, nm)
end

## keywidth is first column
function getKeywidth(::MIME"application/x-gtk", tr::TreeView)
    tview = tr[:widget]
    col = Gtk.G_.column(tview, 0)
    Gtk.G_.width(col)
end
function setKeywidth(::MIME"application/x-gtk", tr::TreeView, width::Int)
    tview = tr[:widget]
    col = Gtk.G_.column(tview, 0)
    Gtk.G_.min_width(col, width)
end
function getWidths(::MIME"application/x-gtk", tr::TreeView)
    tview = tr[:widget]
    widths = Int[]
    for (i, width) in enumerate(widths)
        col = Gtk.G_.column(tview, i)
        push!(widths, Gtk.G_.width(col))
    end
    widths
end
function setWidths(::MIME"application/x-gtk", tr::TreeView, widths::Vector{Int})
    @assert length(tr.store) == length(widths)
    tview = tr[:widget]
    for (i, width) in enumerate(widths)
        col = Gtk.G_.column(tview, i)
        Gtk.G_.min_width(col, width)
    end

end
function getHeights(::MIME"application/x-gtk", tr::TreeView)
    "XXX"  ## height is only fixed or floating in gtk(???)
end
function setHeights(::MIME"application/x-gtk", tr::TreeView, heights::Vector{Int})
    "XXX"
end

function setIcon(::MIME"application/x-gtk", tr::TreeView, path::Vector{Int}, icon::Icon)
    tview = tr[:widget]
    XXX("Set icons")
    ## XXX  do I need a column in our gtkstore for icon?
end

## Images

## place to put a png image
function imageview(::MIME"application/x-gtk", parent::Container)
    widget = @GtkImage()
    (widget, widget)
end

function setImage(::MIME"application/x-gtk", o::ImageView, img::String)
    Gtk.G_.from_file(o[:widget], img)
end

##################################################
##
## Dialogs
## XXX Need to do these ... XXX
function dialog(::MIME"application/x-qt", parent::Widget, model;
                buttons::Vector{Symbol}=[:ok],
                default::Union(Symbol, Nothing)=:ok,
                title::String="")

    dlg = Qt.QDialog(parent[:widget])

    if !isa(title, Nothing)
        dlg[:setWindowTitle](title)
    end

    button_box = Qt.QDialogButtonBox(dlg)
    defined_btns = {
                    :ok      => button_box[:Ok],
                    :cancel  => button_box[:Cancel],
                    :close   => button_box[:Close],
                    :apply  => button_box[:Apply],
                    :reset   => button_box[:Reset],
                    :help    => button_box[:Help],
                    }

    button_box[:setStandardButtons](sum(map(k -> int(defined_btns[k]), buttons)))
    if !isa(default, Nothing)
        button = button_box[:button](defined_btns[default])
        button[:setDefault](true)
    end

    ## Layout button box
    lyt = Qt.QVBoxLayout(dlg)
    dlg[:setLayout](lyt)
    lyt[:addWidget](button_box)

    (dlg, dlg)
end

## properties
getTitle(::MIME"application/x-qt", parent::Dialog) = dlg[:widget][:windowTitle]()
setTitle(::MIME"application/x-qt", parent::Dialog, value::String) = dlg[:widget][:setWindowTitle](value)

## make deafult button a property?

function set_child(::MIME"application/x-qt", parent::Dialog, child::Widget)
    lyt = parent[:layout]
#    child[:sizepolicy] = (:expand, :expand)
    lyt[:insertWidget](0, child.block, 100)
end

## add here so that we have dlg object, not QDialog
function add_bindings(::MIME"application/x-qt", dlg::Dialog)
    n = dlg[:widget][:layout]()[:count]()
    button_box = dlg[:widget][:layout]()[:itemAt](n-1)[:widget]()
     ## need to connect buttons and close dialog
    f(x, s) = () -> begin
        dlg.state = s
        dlg[:widget][:done](1)
        if !isa(x, Nothing)
            notify(dlg.model, x)
        end
        s
    end
    qconnect(button_box, :accepted,      f("accepted", :accept))
    qconnect(button_box, :rejected,      f("rejected", :reject))
    qconnect(button_box, :helpRequested, f(nothing,    :help))
    qconnect(button_box, :clicked) do button
        notify(dlg.model, "finished", button) # need to see what button is
    end

end
function show_dialog(::MIME"application/x-qt", dlg::Dialog, value::Bool)
    if value
        dlg[:widget][:show]()
        convert(Function, dlg[:widget][:raise])()
    else
        dlg[:widget][:hide]()
    end
end

function setModal(::MIME"application/x-qt", dlg::Dialog, value::Bool)
    if value
        convert(Function, dlg[:widget][:exec])()
    end
end
function setModaless(::MIME"application/x-qt", dlg::Dialog, value::Bool)
    convert(Function, dlg[:widget][:open])()
end
#function destroy(::MIME"application/x-qt", dlg::Dialog)
#    convert(Function, dlg[:widget][:destroy])()
#end

### special dialogs
##
## Returns file, directory, files or nothing
function filedialog(::MIME"application/x-qt",
                    parent::Widget;
                    mode::Symbol=:open, ## :open, :multiple, :directory, :save
                    message::String="",
                    title::String="",
                    filetypes::Union(Nothing, Vector{Tuple})=nothing)

    dlg = (isa(parent, Nothing) ? Qt.QFileDialog() : Qt.QFileDialog(parent[:widget]))
    ## title
    dlg[:setWindowTitle](title)

    ## mode
    file_mode = {:open => :ExistingFile,
                 :multiple => :ExistingFiles,
                 :directory => :Directory,
                 :save => :AnyFile
                 }
    dlg[:setFileMode](dlg[file_mode[mode]])

    ##  Filter
    ## do filetypes an array [(name, ext)]
    if !isa(filetypes, Nothing)
        push!(filetypes, ("All files", "*.*"))
        dlg[:setNameFilters](["$val ($descr)" for (val, descr) in filetypes])
    end

    dlg[:show]()
    convert(Function, dlg[:raise])()
    ret = convert(Function, dlg[:exec])()
    if ret != 0
        files = dlg[:selectedFiles]()
        length(files) == 0 && return(nothing)
        mode == :multiple ? files : files[1]
    else
        nothing
    end
end

function messagebox(::MIME"application/x-qt", parent::Widget, text::String; icon::Union(Nothing, String)=nothing,
                    title::Union(Nothing, String)=nothing,
                    informativeText::Union(Nothing, String)=nothing)

    msgbox = Qt.QMessageBox(parent[:widget])
    msgbox[:setText](text)

    if !isa(icon, Nothing)
        icons = {:question=>:Question,
                 :info =>   :Information,
                 :warning=> :Warning,
                 :critical=>:Critical}
        msgbox[:setIcon](msgbox[icons[icon]])
    end
    if !isa(title, Nothing)
        msgbox[:setWindowTitle](title)
    end
    if !isa(informativeText, Nothing)
        msgbox[:setInformativeText](informativeText)
    end
    if !isa(icon, Nothing)
        args["icon"] = icons[icon]
    end

    msgbox[:show]()
    convert(Function, msgbox[:raise])()
    convert(Function, msgbox[:exec])()
end

function confirmbox(::MIME"application/x-qt", parent::Widget, text::String; icon::Union(Nothing, Symbol)=nothing,
                    title::Union(Nothing, String)=nothing,
                    informativeText::Union(Nothing, String)=nothing) # detail

    msgbox = Qt.QMessageBox(parent[:widget])
    msgbox[:setText](text)

    if !isa(icon, Nothing)
        icons = {:question=>:Question,
                 :info =>   :Information,
                 :warning=> :Warning,
                 :critical=>:Critical}
        msgbox[:setIcon](msgbox[icons[icon]])
    end
    if !isa(title, Nothing)
        msgbox[:setWindowTitle](title)
    end
    if !isa(informativeText, Nothing)
        msgbox[:setInformativeText](informativeText)
    end
    if !isa(icon, Nothing)
        args["icon"] = icons[icon]
    end

    msgbox[:setStandardButtons](int(msgbox[:Ok]) + int(msgbox[:Cancel]))

    msgbox[:show]()
    convert(Function, msgbox[:raise])()
    ret = convert(Function, msgbox[:exec])()
    ret == int(msgbox[:Ok]) ? :accept : :reject
end

##################################################
## Menus

## XXX there is not GtkAction, or GAction in Gtk (yet), until
## then we follow tk
function action(::MIME"application/x-gtk", parent)
    nothing
end

## XXX Need to do work here, as at present action no knows about its proxies XXX
getEnabled(::MIME"application/x-gtk", action::Action) = nothing
setEnabled(::MIME"application/x-gtk", action::Action, value::Bool) = nothing

## not tk specific bits to add to actions
setLabel(::MIME"application/x-gtk", action::Action, value::String) = nothing
setIcon(::MIME"application/x-gtk", action::Action, value::Icon) = nothing
setShortcut(::MIME"application/x-gtk", action::Action, value::String) = nothing
setTooltip(::MIME"application/x-gtk", action::Action, value::String) = nothing
setCommand(::MIME"application/x-gtk", action::Action, value::Function) = nothing

## menus
function menubar(::MIME"application/x-gtk", parent::Window)
    widget = Gtk.@GtkMenuBar()
    Gtk.G_.hexpand(widget, true)
    push!(parent.block[1], widget)
    showall(parent.block[1])
    widget
end

## toplevel menu item
function menu(::MIME"application/x-gtk", parent::MenuBar, label)
    item = Gtk.@GtkMenuItem(label)
    mitem = Gtk.@GtkMenu(item)
    push!(parent[:widget], item)
    showall(parent[:widget])
    mitem
end

## submenu
function menu(::MIME"application/x-gtk", parent::Menu, label)
    item = @GtkMenuItem(label)
    mitem = @GtkMenu(item)
    push!(parent[:widget], item)
    showall(parent[:widget])
    mitem
end

## popup
function menu(::MIME"application/x-gtk", parent::Widget)
    XXX("add me")
    ## needs to bind to thrid mouse event, ...
    parent[:widget][:setContextMenuPolicy](PySide.QtCore["Qt"][:CustomContextMenu])
    m = PySide.Qt.QMenu(parent[:widget])

    qconnect(parent[:widget], :customContextMenuRequested) do pt
        update_context(parent.toolkit, parent, pt)
        m[:popup](parent[:widget][:mapToGlobal](pt))
    end

    m
end

## add actions
function addAction(::MIME"application/x-gtk", parent::Menu, action::Action)
    item = @GtkMenuItem(action.label)
    if !isa(action.tooltip, Nothing)
        Gtk.G_.tooltip_text(item, action.tooltip)
    end
    signal_connect(item, :activate) do widget
        action.command()
    end

    push!(parent[:widget], item)
    showall(parent[:widget])
end

function addAction(::MIME"application/x-gtk", parent::Menu, value::Separator)
    push!(parent[:widget], Gtk.@SeparatorMenuItem())
    showall(parent[:widget])
end

function addAction(::MIME"application/x-gtk", parent::Menu, value::RadioGroup)
    XXX("Not implemented in Gtk.jl")
    widget = Qt.QActionGroup(parent[:widget])
    widget[:setExclusive](true)
    for item in value.model.items
        action = Qt.QAction(item, parent[:widget])
        action[:setCheckable](true)
        widget[:addAction](action)
        parent[:widget][:addAction](action)
    end
    qconnect(widget, :triggered) do action
        notify(value.model, "valueChanged", action[:text]())
    end
end

function addAction(::MIME"application/x-gtk", parent::Menu, value::CheckBox)
    XXX("Not implemented in Gtk.jl")
    widget = Qt.QAction(value[:label], parent[:widget])
    widget[:setCheckable](true)
    ## widget[:setIcon](...)
    qconnect(widget, :changed) do
        notify(value.model, "valueChanged", widget[:isChecked]())
    end

    parent[:widget][:addAction](widget)
end

## manipulate

function Display(::MIME"application/x-gtk", self::ManipulateObject, x::Any; kwargs...)
    if isa(x, Nothing) return end
    oa = self.output_area

    if length(children(oa)) > 0 && isa(oa.children[1], TextEdit)
        te = oa.children[1]
        te[:value] = string(x)
        return
    elseif length(children(oa)) > 0
        pop!(oa)
    end
    ## add one
    te = textedit(oa)
    te[:sizepolicy] = (:expand, :expand)
    push!(oa, te)
    te[:value] = string(x)
end

function Display(::MIME"application/x-gtk", self::ManipulateObject, x::Mustache.MustacheTokens; context=nothing, kwargs...)
    if isa(x, Nothing) return end
    oa = self.output_area

    if length(children(oa)) > 0 && isa(oa.children[1], TextEdit)
        te = oa.children[1]
    elseif length(children(oa)) > 0
        te = pop!(oa)
    else
        te = textedit(oa)
        te[:sizepolicy] = (:expand, :expand)
        push!(oa, te)
    end
    te[:value] = ""

    function add_tpl(tpl)
        ## how to show a mustache template
        for (t, l, b, e) in tpl.tokens
            if t == "text"
                push!(te, l)
            else ## if type == "name"
                ## look up and render accordingly
                obj = context.(symbol(l))
                if isa(obj, Winston.FramedPlot)
                    c = cairographic(oa)
                    display(c, obj)
                    push!(te, c)
                else
                    push!(te, string(obj))
                end
            end
        end
    end

    add_tpl(x)
end


text:
module SBP_GPU

# Write your package code here.
function test_function()
    println("Hello Julia's World!")
end

include("deriv_ops.jl")
include("deriv_ops_beta.jl")
include("deriv_ops_GPU.jl")

# export D2x, D2x_GPU_shared, tester_function_v3
export Bx, BxSx, BxSx_tran, By, BySy, BySy_tran, D2x, D2y, Dx, Dy, Hx, Hxinv, Hy, Hyinv, FACEtoVOL, VOLtoFACE
export Bx_beta!, BxSx_beta!, BxSx_tran_beta!, By_beta!, BySy_beta!, BySy_tran_beta!, D2x_beta!, D2y_beta!, Dx_beta!, Dy_beta!, Hx_beta!, Hxinv_beta!, Hy_beta!, Hyinv_beta!, FACEtoVOL_beta!, VOLtoFACE_beta!
export Bx_GPU_shared, BxSx_GPU_shared, By_GPU_shared, BySy_GPU_shared, BySy_tran_GPU_shared, D2x_GPU_shared, D2y_GPU_shared, Dx_GPU_shared, Dy_GPU_shared, Hx_GPU_shared, Hxinv_GPU_shared, Hy_GPU_shared

export tester_function, tester_function_v2, tester_function_v3
export D2x_GPU, Dx_GPU
export D2x_GPU_naive, D2y_GPU_naive

end


text:
## TYPES
"""
Compact structure for 2D point.
"""
struct Point
    x::Float64
    y::Float64
end
Point() = Point(0.0,0.0)
Point(v::V) where {V <: AbstractVector} = Point(v[1], v[2])
Point(t::Tuple{Float64,Float64}) = Point(t[1], t[2])

function point_dist(p1::Point, p2::Point, l::Real=2)
    s = SVector{2,Float64}(p1.x-p2.x, p1.y-p2.y)
    return norm(s,l)
end

function point_norm(p::Point, l::Real=2)
    s = SVector{2,Float64}(p.x,p.y)
    return norm(s,l)
end

function equal(p1::Point, p2::Point)
    return (isapprox(p1.x,p2.x) && isapprox(p1.y,p2.y))
end

function interpolate(p1::Point,p2::Point,frac::Float64)

    xval = p1.x + frac*(p2.x-p1.x)
    yval = p1.y + frac*(p2.y-p1.y)

    return Point(xval,yval)

end


text:
"""
    initialize_sbm_model(config::Config)

Initial part of the SBM model concept. Reads the input settings and data as defined in the
Config object. Will return a Model that is ready to run.
"""
function initialize_sbm_model(config::Config)

    # unpack the paths to the NetCDF files
    tomldir = dirname(config)
    static_path = joinpath(tomldir, config.input.path_static)
    cyclic_path = joinpath(tomldir, config.input.path_static)
    dynamic_path = joinpath(tomldir, config.input.path_forcing)
    instate_path = joinpath(tomldir, config.state.path_input)
    output_path = joinpath(tomldir, config.output.path)

    Δt = Second(config.timestepsecs)
    sizeinmetres = get(config.model, "sizeinmetres", false)
    reinit = get(config.model, "reinit", true)
    do_snow = get(config.model, "snow", false)
    do_reservoirs = get(config.model, "reservoirs", false)
    do_lakes = get(config.model, "lakes", false)
    do_pits = get(config.model, "pits", false)

    kw_river_tstep = get(config.model, "kw_river_tstep", 0)
    kw_land_tstep = get(config.model, "kw_land_tstep", 0)

    nc = NCDataset(static_path)
    dims = dimnames(nc[param(config, "input.subcatchment")])

    # There is no need to permute the dimensions of the data, since the active indices are
    # correctly calculated in both ways.
    # The dimension order only needs to be known for interpreting the LDD directions
    # and creating the coordinate maps.
    dims_xy = dims[2] in ("y", "lat")

    subcatch_2d = ncread(nc, param(config, "input.subcatchment"); allow_missing = true)
    # indices based on catchment
    inds, rev_inds = active_indices(subcatch_2d, missing)
    n = length(inds)
    modelsize_2d = size(subcatch_2d)

    river_2d = ncread(nc, param(config, "input.river_location"); type = Bool, fill = false)
    river = river_2d[inds]
    riverwidth_2d =
        ncread(nc, param(config, "input.lateral.river.width"); type = Float64, fill = 0)
    riverwidth = riverwidth_2d[inds]
    riverlength_2d =
        ncread(nc, param(config, "input.lateral.river.length"); type = Float64, fill = 0)
    riverlength = riverlength_2d[inds]

    # read x, y coordinates and calculate cell length [m]
    y_nc = "y" in keys(nc.dim) ? ncread(nc, "y") : ncread(nc, "lat")
    x_nc = "x" in keys(nc.dim) ? ncread(nc, "x") : ncread(nc, "lon")
    if dims_xy
        y = permutedims(repeat(y_nc, outer = (1, length(x_nc))))[inds]
    else
        y = repeat(y_nc, outer = (1, length(x_nc)))[inds]
    end
    cellength = abs(mean(diff(x_nc)))

    xl = fill(mv, n)
    yl = fill(mv, n)
    riverfrac = fill(mv, n)

    for i = 1:n
        xl[i] = sizeinmetres ? cellength : lattometres(y[i])[1] * cellength
        yl[i] = sizeinmetres ? cellength : lattometres(y[i])[2] * cellength
        riverfrac[i] =
            river[i] ? min((riverlength[i] * riverwidth[i]) / (xl[i] * yl[i]), 1.0) : 0.0
    end

    sbm = initialize_sbm(nc, config, riverfrac, xl, yl, inds)

    inds_riv, rev_inds_riv = active_indices(river_2d, 0)
    nriv = length(inds_riv)

    # reservoirs
    pits = zeros(Bool, modelsize_2d)
    if do_reservoirs
        reservoirs, resindex, reservoir, pits = initialize_simple_reservoir(config, nc, inds_riv, nriv, pits)
    else
        reservoir = ()
    end

    # lakes
    if do_lakes
        lakes, lakeindex, lake, pits = initialize_natural_lake(config, nc, inds_riv, nriv, pits)
    else
        lake = ()
    end

    ldd_2d = ncread(nc, param(config, "input.ldd"); allow_missing = true)
    ldd = ldd_2d[inds]
    if do_pits
        pits_2d = ncread(nc, param(config, "input.pits"); type = Bool, fill = false)
        ldd = set_pit_ldd(pits_2d, ldd, inds)
    end

    # lateral part sbm
    khfrac = ncread(
        nc,
        param(config, "input.lateral.subsurface.ksathorfrac", nothing);
        sel = inds,
        defaults = 1.0,
        type = Float64,
    )
    βₗ = ncread(nc, param(config, "input.lateral.land.slope"); sel = inds, type = Float64)
    clamp!(βₗ, 0.00001, Inf)
    kh₀ = khfrac .* sbm.kv₀
    dl = fill(mv, n)
    dw = fill(mv, n)
    sw = fill(mv, n)

    for i = 1:n
        dl[i] = detdrainlength(ldd[i], xl[i], yl[i])
        dw[i] = (xl[i] * yl[i])/dl[i]
        sw[i] = river[i] ? max(dw[i] - riverwidth[i], 0.0) : dw[i]
    end

    ssf = LateralSSF{Float64}(
        kh₀ = kh₀,
        f = sbm.f,
        zi = sbm.zi,
        soilthickness = sbm.soilthickness,
        θₑ = sbm.θₛ .- sbm.θᵣ,
        Δt = 1.0,
        βₗ = βₗ,
        dl = dl .* 1000.0,
        dw = dw .* 1000.0,
        ssf = ((kh₀ .* βₗ) ./ sbm.f) .* (exp.(-sbm.f .* sbm.zi) - exp.(-sbm.f .* sbm.soilthickness)) .* dw .*1000.0,
        wb_pit = pits[inds],
    )

    n_land = ncread(
        nc,
        param(config, "input.lateral.land.n", nothing);
        sel = inds,
        defaults = 0.072,
        type = Float64,
    )

    olf = SurfaceFlow(
        sl = βₗ,
        n = n_land,
        dl = dl,
        Δt = tosecond(Δt),
        its = kw_land_tstep > 0 ? Int(cld(tosecond(Δt), kw_land_tstep)) : kw_land_tstep,
        width = sw,
        wb_pit = pits[inds],
    )

    pcr_dir = dims_xy ? permute_indices(pcrdir) : pcrdir
    graph = flowgraph(ldd, inds, pcr_dir)

    riverslope = ncread(
        nc,
        param(config, "input.lateral.river.slope");
        sel = inds_riv,
        type = Float64,
    )
    clamp!(riverslope, 0.00001, Inf)
    riverlength = riverlength_2d[inds_riv]
    riverwidth = riverwidth_2d[inds_riv]
    n_river = ncread(
        nc,
        param(config, "input.lateral.river.n", nothing);
        sel = inds_riv,
        defaults = 0.036,
        type = Float64,
    )
    ldd_riv = ldd_2d[inds_riv]
    if do_pits
        ldd_riv = set_pit_ldd(pits_2d, ldd_riv, inds_riv)
    end
    graph_riv = flowgraph(ldd_riv, inds_riv, pcr_dir)

    # the indices of the river cells in the land(+river) cell vector
    index_river = filter(i -> !isequal(river[i], 0), 1:n)
    frac_toriver = fraction_runoff_toriver(graph, ldd, index_river, βₗ, n)

    rf = SurfaceFlow(
        sl = riverslope,
        n = n_river,
        dl = riverlength,
        Δt = tosecond(Δt),
        its = kw_river_tstep > 0 ? ceil(Int(tosecond(Δt) / kw_river_tstep)) : kw_river_tstep,
        width = riverwidth,
        reservoir_index = do_reservoirs ? resindex : fill(0, nriv),
        lake_index = do_lakes ? lakeindex : fill(0, nriv),
        reservoir = do_reservoirs ? reservoirs : nothing,
        lake = do_lakes ? lakes : nothing,
        rivercells = river,
    )

    state_ncnames = ncnames(config.state)

    reader = prepare_reader(dynamic_path, cyclic_path, config)

    modelmap = (vertical = sbm, lateral = (subsurface = ssf, land = olf, river = rf))
    indices_reverse = (
        land = rev_inds,
        river = rev_inds_riv,
        reservoir = isempty(reservoir) ? nothing : reservoir.reverse_indices,
        lake = isempty(lake) ? nothing : lake.reverse_indices,
    )
    writer = prepare_writer(
        config,
        reader,
        output_path,
        modelmap,
        state_ncnames,
        indices_reverse,
        x_nc,
        y_nc,
        dims_xy,
        nc,
        maxlayers = sbm.maxlayers,
    )

    # for each domain save the directed acyclic graph, the traversion order,
    # and the indices that map it back to the two dimensional grid
    # for reservoirs and lakes this is information is also available (except the graph)
    # from the initialization functions
    land = (
        graph = graph,
        order = topological_sort_by_dfs(graph),
        indices = inds,
        reverse_indices = rev_inds,
    )
    river = (
        graph = graph_riv,
        order = topological_sort_by_dfs(graph_riv),
        indices = inds_riv,
        reverse_indices = rev_inds_riv,
    )

    model = Model(
        config,
        (; land, river, reservoir, lake, index_river, frac_toriver),
        (subsurface = ssf, land = olf, river = rf),
        sbm,
        Clock(config.starttime, 1, Δt),
        reader,
        writer,
    )

    # read and set states in model object if reinit=false
    if reinit == false
        set_states(
            instate_path,
            model,
            state_ncnames;
            type = Float64
        )
        @unpack lateral, vertical = model
        # update zi for vertical sbm and α for river and overland flow
        zi = max.(0.0, vertical.soilthickness .- vertical.satwaterdepth ./ vertical.θₑ)
        vertical.zi .= zi
        lateral.river.α .= lateral.river.alpha_term .* pow.(lateral.river.width .+ 2.0 .* lateral.river.h, lateral.river.alpha_pow)
        lateral.land.α .= lateral.land.alpha_term .* pow.(lateral.land.width .+ 2.0 .* lateral.land.h, lateral.land.alpha_pow)
    end

    # make sure the forcing is already loaded
    # it's fine to run twice, and may help catching errors earlier
    update_forcing!(model)
    if haskey(config.input, "cyclic")
        update_cyclic!(model)
    end
    return model
end

"update SBM model for a single timestep"
function update(model::Model{N,L,V,R,W}) where {N,L,V<:SBM,R,W}
    @unpack lateral, vertical, network, clock, config = model

    inds_riv = network.index_river
    kinwave_it = get(config.model, "kin_wave_iteration", false)

    update_forcing!(model)
    if haskey(config.input, "cyclic")
        update_cyclic!(model)
    end

    # extract water levels h_av [m] from the land and river domains
    # this is used to limit open water evaporation
    vertical.waterlevel_land .= lateral.land.h_av .* 1000.0
    vertical.waterlevel_river[inds_riv] .= lateral.river.h_av .* 1000.0

    # vertical sbm concept is updated until snow state, after that (optional)
    # snow transport is possible
    update_until_snow(vertical, config)

    # lateral snow transport
    if get(config.model, "masswasting", false)
        snowflux_frac =
            min.(0.5, lateral.land.sl ./ 5.67) .* min.(1.0, vertical.snow ./ 10000.0)
        maxflux = snowflux_frac .* vertical.snow
        vertical.snow .= accucapacityflux(network.land, vertical.snow, maxflux)
        vertical.snowwater .= accucapacityflux(network.land, vertical.snowwater, vertical.snowwater .* snowflux_frac)
    end

    # update vertical sbm concept until recharge [mm] to the saturated store
    update_until_recharge(vertical, config)

    # exchange of recharge between vertical sbm concept and subsurface flow domain
    lateral.subsurface.recharge .= vertical.recharge
    lateral.subsurface.recharge .*= lateral.subsurface.dw
    lateral.subsurface.zi .= vertical.zi

    # update lateral subsurface flow domain
    update(lateral.subsurface, network.land, network.frac_toriver, lateral.river.rivercells)

    # update vertical sbm concept (runoff, ustorelayerdepth and satwaterdepth)
    update_after_lateralflow(
        vertical,
        lateral.subsurface.zi,
        lateral.subsurface.exfiltwater,
    )

    # update overland flow based on vertical runoff [mm] from vertical sbm concept
    lateral.land.qlat .=
        (vertical.runoff .* vertical.xl .* vertical.yl .* 0.001) ./ lateral.land.Δt ./
        lateral.land.dl
    update(
        lateral.land,
        network.land,
        frac_toriver = network.frac_toriver,
        river = lateral.river.rivercells,
        do_iter = kinwave_it,
    )

    # update river domain with net runoff from vertical sbm concept, overland flow
    # and lateral subsurface flow to the river cells
    net_runoff_river =
        (vertical.net_runoff_river[inds_riv] .* vertical.xl[inds_riv] .*
        vertical.yl[inds_riv] .* 0.001) ./ vertical.Δt
    lateral.river.qlat .=
        (
            lateral.subsurface.to_river[inds_riv] ./ 1.0e9 ./ lateral.river.Δt .+
            lateral.land.to_river[inds_riv] .+ net_runoff_river
        ) ./ lateral.river.dl
    update(lateral.river, network.river, do_iter = kinwave_it, doy = dayofyear(clock.time))

    write_output(model)

    # update the clock
    advance!(clock)

    return model
end


text:
# by lazarusA # HIDE
# using GLMakie # HIDE
using CairoMakie
CairoMakie.activate!() # HIDE
let
    x = 0:0.05:1
    y = x.^2
    fig = Figure(resolution = (700, 450))
    ax = Axis(fig, xlabel = "x", ylabel = "y")
    linea = lines!(x, y, color = :dodgerblue)
    fillB = band!(x, fill(0,length(x)), y; color = (:dodgerblue, 0.1))
    leg = Legend(fig, [[linea, fillB]], ["Label"], halign = :left, valign = :top,
                tellheight = false, tellwidth = false, margin = (10, 10, 10, 10))
    fig[1, 1] = ax
    fig[1, 1] = leg
    fig
    #save("FilledLine.png"), fig, px_per_unit = 2.0)
    save(joinpath(@__DIR__, "output", "FilledLine.png"), fig, px_per_unit = 2.0) # HIDE
end
using Pkg # HIDE
Pkg.status("CairoMakie") # HIDE

max_stars_repo_name: MisterBiggs/BeautifulMakie

max_issues_repo_name: MisterBiggs/BeautifulMakie

max_forks_repo_name: MisterBiggs/BeautifulMakie


text:
### Initial conditions

# body mass ratio
Z = 1

# fertilisation gradient
allI_K = [1:1:20;]

# temperature gradient
allT = [0:1:40;] .+ 273.15

# time span for each simulation
tspan = (0.0, 315360000000.0)

# define extinction threshold
threshold = 10e-12

#### data frame to store outputs
df3 = DataFrame(
    Z = [],
    I_K = [],
    T = [],
    K = [],
    r = [],
    x_2 = [],
    y_21 = [],
    B0_21 = [],
    maxB1 = [],
    maxB2 = [],
    maxB3 = [],
    sp_survival = []
)

### Simulation loops
T = 300.15
I_K = 3
Z = 1

for I_K in allI_K
    for T in allT

        # generate parameters specific to this combo of Z, I_K and T
        p = ModelParameters(param, T, I_K, Z)

        # Calculate starting biomasses
        u0 = [p.K[1]/2, p.K[1]/2, p.K[1]/2]

        # Define the problem
        prob = ODEProblem(BEFW, u0, tspan, p)

        # Solve the problem
        sol = solve(prob, maxiters = 1e7)

        ###### Output metrics ######
        # turn vector output of biomasses into matrix
        matrix_u = hcat(sol.u...)' # 56×3 adjoint(::Matrix{Float64}) with eltype Float64:

        ### Biomass extremes
        maxB1 = maximum(matrix_u[:74,1])
        maxB2 = maximum(matrix_u[:,2])
        maxB3 = maximum(matrix_u[:,3])

        ### Number of surviving species
        # final biomasses
        final_u = matrix_u[length(sol.t),:]

        # count number of species remaining that are above extinction threshold
        survived = count(final_u .> threshold)

        ### Push outputs to DataFrame
        push!(df3, [
            Z,
            I_K,
            T,
            p.K[1],
            p.r[1],
            p.x[2],
            p.y[2,1],
            p.B0[2,1],
            maxB1,
            maxB2,
            maxB3,
            survived
        ])

        # print some stuff - see how the simulation is progressing
        println(("Z = $Z", "I_K = $I_K", "T = $T"))

    end
end

df
df2

max_stars_repo_name: hanamayall/simple-feeding-DR

max_issues_repo_name: hanamayall/simple-feeding-DR

max_forks_repo_name: hanamayall/simple-feeding-DR


text:
using DataDeps

register(DataDep(
  "seeds",
  "http://archive.ics.uci.edu/ml/datasets/seeds",
  "http://archive.ics.uci.edu/ml/machine-learning-databases/00236/seeds_dataset.txt",
  "1f3f83c0d8485ae9148061389d19628607e3f5660e3d6f40ec9102fb398bb12f",
  post_fetch_method=(path -> begin
    UCIData.process_dataset(path,
        target_index=8,
        feature_indices=1:7,
        delim='\t',
        ignorerepeated=true,
    )
  end),
))

max_stars_repo_name: JackDunnNZ/uci-data

max_issues_repo_name: JackDunnNZ/uci-data

max_forks_repo_name: JackDunnNZ/uci-data


text:
mutable struct MSetAlgosSolver <: MSetSolver
  m # Int
  n_arms # Int

  function MSetAlgosSolver()
    return new(nothing, nothing)
  end
end

copy(::MSetAlgosSolver) = MSetAlgosSolver()

function build!(solver::MSetAlgosSolver, m::Int, n_arms::Int)
  solver.m = m
  solver.n_arms = n_arms
end

has_lp_formulation(::MSetAlgosSolver) = false
approximation_ratio(::MSetAlgosSolver) = 1.0
approximation_term(::MSetAlgosSolver) = 0.0
approximation_ratio_budgeted(::MSetAlgosSolver) = 1.0 # TODO: redirect to the package.
approximation_term_budgeted(::MSetAlgosSolver) = 0.0
supports_solve_budgeted_linear(::MSetAlgosSolver) = true
supports_solve_all_budgeted_linear(::MSetAlgosSolver) = true

function _rewards_weights_dict_to_vectors(rewards::Dict{Int, Float64}, weights::Dict{Int, Int})
  objects = vcat(collect(keys(rewards)), collect(keys(weights)))
  unique!(objects)
  sort!(objects)

  rewards_vector = [rewards[o] for o in objects]
  weights_vector = [weights[o] for o in objects]
  return rewards_vector, weights_vector
end

function solve_linear(solver::MSetAlgosSolver, weights::Dict{Int, Float64})
  objects = collect(keys(weights))
  sort!(objects)
  weights_vector = [weights[o] for o in objects]

  return msets_greedy(MSetInstance(weights_vector, solver.m)).items
end

function solve_budgeted_linear(solver::MSetAlgosSolver, rewards::Dict{Int, Float64}, weights::Dict{Int, Int}, budget::Int)
  rewards_vector, weights_vector = _rewards_weights_dict_to_vectors(rewards, weights)
  i = BudgetedMSetInstance(rewards_vector, weights_vector, solver.m, budget=budget)
  return budgeted_msets_dp(i).items
end

function solve_all_budgeted_linear(solver::MSetAlgosSolver, rewards::Dict{Int, Float64}, weights::Dict{Int, Int}, max_budget::Int)
  rewards_vector, weights_vector = _rewards_weights_dict_to_vectors(rewards, weights)
  i = BudgetedMSetInstance(rewards_vector, weights_vector, solver.m, budget=max_budget)
  s = budgeted_msets_dp(i)
  return items_all_budgets(s, max_budget)
end


text:
function FindGridCell(Cell,Grid)
end


text:
using Pkg.GitTools

isurl(path::AbstractString) = startswith(path, "http://") || startswith(path, "https://")

"""
    get_registered_file(name)

Fetch registered file path from Artifacts.toml, based on the artifact `name`.

"""
function get_registered_file(name)
    global ARTIFACTS_TOML
    hash = artifact_hash(name, ARTIFACTS_TOML)
    isnothing(hash) && error("$name not registered.")
    if !artifact_exists(hash)
        println("Artifact doesn't exist. Downloading....")
        get_artifact(name)
        hash = artifact_hash(name, ARTIFACTS_TOML)
    end
    artifact_path(hash)
end

"""
    get_artifact(name)

Utility function to download/install the artifact in case not already installed.
"""
function get_artifact(name)
    meta = Artifacts.artifact_meta(name, ARTIFACTS_TOML)
    file_path = meta["download"][1]["url"]
    if isurl(file_path)
        hash = artifact_hash(name, ARTIFACTS_TOML)

        if hash==nothing || !artifact_exists(hash)
            file_hash = create_artifact() do artifact_dir
                tarball = download("$file_path", joinpath(artifact_dir, string(name, ".txt")))
                global tarball_hash = bytes2hex(GitTools.blob_hash(tarball))
            end

            # register if not in artifact.toml
            if isnothing(artifact_hash(name, ARTIFACTS_TOML))
                bind_artifact!(ARTIFACTS_TOML, name, file_hash; download_info=[(file_path, tarball_hash)])
            end
        end
    else
        file_name = split(file_path, "/")[end]
        path = joinpath(@__DIR__, file_path[1:end-length(file_name)])
        register_custom_file(name, file_name, path)
    end
end

"""
    register_custom_file(artifact_name, file_name, path)

Function to register custom file under `artifact_name` in Artifacts.toml. `path` expects path of the directory where the file `file_name` is stored. Stores the complete path to the file as Artifact URL.

# Example

```
register_custom_file('custom', 'xyz.txt','./folder/folder/')

```
Note: In case this gives permission denied error, change the Artifacts.toml file permissions using
`chmod(path_to_file_in_julia_installation , 0o764)`or similar.
"""
function register_custom_file(artifact_name, file_name, path)
    file_path = joinpath(path, file_name)
    isfile(file_path) || error("Can't register to $artifact_name: file $file_path not found.")
    filegetter = function (dest)
        cp(file_path, dest)
    end

    file_hash = create_artifact() do artifact_dir
        tarball = filegetter(joinpath(artifact_dir, file_name))
        global tarball_hash = bytes2hex(GitTools.blob_hash(tarball))
    end

    # register if not in artifact.toml
    if isnothing(artifact_hash(artifact_name, ARTIFACTS_TOML))
        bind_artifact!(ARTIFACTS_TOML, artifact_name, file_hash; download_info=[(file_path, tarball_hash)])
    end
end