text:
#= ######################################################
includes
###################################################### =#

using DelimitedFiles

# using GLMakie
using CairoMakie
# CairoMakie.activate!(type = "svg")
# filetype = ".svg"
CairoMakie.activate!(type = "png")
filetype = ".png"

include("./epseff2D.jl")
include("./models2D.jl")
include("./plot2D.jl")

#= ######################################################
inside functions
###################################################### =#

function setup_legend(leg)
    leg.rowgap = -4 # default: 3
    leg.groupgap = 8 # default: 16
    leg.patchlabelgap = 5 # default: 5
    leg.patchsize = (20.0f0, 20.0f0) # default: (20.0f0, 20.0f0)
    leg.padding = (3.0f0, 3.0f0, 1.0f0, 1.0f0) # default: (10.0f0, 10.0f0, 8.0f0, 8.0f0)
end

function setup_axis_epseff(ax)
    linewidth = 2.0
    setup_axis(ax, linewidth)
    ax.xlabel = "epsilon"
    ax.ylabel = "epsilon effective"
    ax.aspect = DataAspect()
    xlims!(ax, (0, 1))
    ylims!(ax, (0, 1))
    ax.xticks = 0:0.2:1.0
    ax.yticks = 0:0.2:1.0
    return linewidth
end

function plot_epseff(case, val, filename)
    # 2D plot
    fig = Figure(resolution = (300, 300), font = "Arial", fontsize = 12)
    ax = fig[1, 1] = Axis(fig)
    linewidth = setup_axis_epseff(ax)
    # colors = [:red, :green, :blue, :orange, :cyan, :purple1]
    # colors = to_colormap(:rainbow, size(case,1))
    colors = to_colormap(:Dark2_8, size(case,1))
    plt_lin = Vector(undef,(size(case,1)))
    for i = 1:size(case,1)
        plt_lin[i] = lines!(ax, val[:,(2*i-1)], val[:,(2*i)], linewidth = linewidth, color = colors[i])
    end
    # legend
    leg_string = ["phi = $(case[i,1])" for i = 1:size(case,1)]
    leg = axislegend(ax, plt_lin, leg_string, position = :rb, orientation = :vertical)
    setup_legend(leg)
    save(filename*filetype, fig, px_per_unit = 8)
end

function calc_vfmat(m; n = 30)
    vfmat = zeros(Float64, m.nelem, m.nelem)
    existing_vf!(m, vfmat)
    dx, dy = create_tiles(m, n)
    @time t_occ = check_tile_occupation(m, dx, dy, n)
    @time blocking_vf_with_tiles!(m, vfmat, dx, dy, n, t_occ)
    calculating_vf!(m, vfmat, normit = true)
    return vfmat
end

function plot_cases(case, m, arr, filename; lim = (nothing, nothing), refcase = 1)
    fig = Figure(resolution = (700, 700), font = "Arial", fontsize = 12)
    for i = 1:size(case,1)
        ax = fig[arr[i,1], arr[i,2]] = Axis(fig)
        linewidth = 2.0
        setup_axis(ax, linewidth)
        ax.title = "phi = $(case[i,1])"
        ax.xlabel = "X in m"
        ax.ylabel = "Y in m"
        ax.limits = lim
        ax.aspect = DataAspect()
        colors = Vector{Any}(undef,18)
        colors[:] .= :black
        plot_model(fig, ax, m[refcase], showleg = false, colors = colors, linewidth = 0)
        plot_model(fig, ax, m[i], showleg = false, colors = colors, linewidth = linewidth)
    end
    save(filename*filetype, fig, px_per_unit = 8)
end

function set_arrangement(n_cases; col = 2)
    # arrangement in figure
    arr = Matrix{Int64}(undef, n_cases, 2)
    c_col = 0
    c_row = 1
    for i = 1:n_cases
        if c_col >= col
            c_col = 1
            c_row += 1
        else
            c_col += 1
        end
        arr[i,1] = c_row
        arr[i,2] = c_col
    end
    return arr
end

#= ######################################################
cases calculation
###################################################### =#

function epsilon_effective_round_groove()
    # infinetely long round groove
    case = [0.01 3.56;
            0.05 17.2;
            0.1 33.15;
            0.2 62.55;
            0.5 142.79;
            0.9999 357.19] # cant calculate phi = 1.0
    epsilon_ink = 0.01
    eps_eff = Matrix{Float64}(undef,round(Integer,1.0/epsilon_ink),2*size(case,1))
    for i = 1:size(case,1)
        m = model_circle_with_opening_line(2.0, 360, round(Integer,case[i,2]))
        vfmat = calc_vfmat(m)
        eps_eff_t = get_epsilon_effective(m, vfmat, 2, epsilon_ink = epsilon_ink)
        eps_eff[:,(2*i-1)] = eps_eff_t[:,1]
        eps_eff[:,(2*i)] = eps_eff_t[:,2]
    end
    plot_epseff(case, eps_eff, "fig_epseff_round_groove")
end

function epsilon_effective_v_groove()
    # infinetely long V groove
    case = [1E-3 0.1; # cant calc with phi = 0
            0.224 25.9;
            0.316 36.8;
            0.447 53.1;
            0.592 72.6;
            0.707 90;
            0.9999 178.4] # cant calculate phi = 1.0
    epsilon_ink = 0.01
    eps_eff = Matrix{Float64}(undef,round(Integer,1.0/epsilon_ink),2*size(case,1))
    for i = 1:size(case,1)
        m = model_isosceles_triangle(3.0, case[i,2], 0.01)
        vfmat = calc_vfmat(m)
        eps_eff_t = get_epsilon_effective(m, vfmat, 3, epsilon_ink = epsilon_ink)
        eps_eff[:,(2*i-1)] = eps_eff_t[:,1]
        eps_eff[:,(2*i)] = eps_eff_t[:,2]
    end
    plot_epseff(case, eps_eff, "fig_epseff_v_groove")
end

function epsilon_effective_square_groove()
    # square groove
    h = 2.0
    case = Matrix{Float64}(undef,10,2) # L/h, L
    for i = 1:10
        case[i,1] = i
        case[i,2] = i * h
    end
    epsilon_ink = 0.01
    eps_eff = Matrix{Float64}(undef,round(Integer,1.0/epsilon_ink),2*size(case,1))
    for i = 1:size(case,1)
        m = model_rectangle(h, case[i,2], 0.05)
        vfmat = calc_vfmat(m)
        eps_eff_t = get_epsilon_effective(m, vfmat, 3, epsilon_ink = epsilon_ink)
        eps_eff[:,(2*i-1)] = eps_eff_t[:,1]
        eps_eff[:,(2*i)] = eps_eff_t[:,2]
    end
    plot_epseff(case, eps_eff, "fig_epseff_square_groove")
end

function epsilon_effective_elbow_piece()
    # elbow piece
    case = [0.707 1.0 1.0;
            0.850 1.0 0.2]
    epsilon_ink = 0.01
    eps_eff = Matrix{Float64}(undef,round(Integer,1.0/epsilon_ink),2*size(case,1))
    for i = 1:(size(case,1))
        m = model_right_triangle(case[i,2], case[i,3], 0.01)
        vfmat = calc_vfmat(m)
        eps_eff_t = get_epsilon_effective(m, vfmat, 3, epsilon_ink = epsilon_ink)
        eps_eff[:,(2*i-1)] = eps_eff_t[:,1]
        eps_eff[:,(2*i)] = eps_eff_t[:,2]
    end
    plot_epseff(case, eps_eff, "fig_epseff_elbow_piece")
end

function epsilon_effective_trapez()
    # trapez
    a = 2.0
    b = 3.0
    case = collect(1:10)
    epsilon_ink = 0.01
    eps_eff = Matrix{Float64}(undef,round(Integer,1.0/epsilon_ink),2*size(case,1))
    for i = 1:(size(case,1))
        m = model_trapez(a, b, case[i,1], 0.05)
        vfmat = calc_vfmat(m)
        eps_eff_t = get_epsilon_effective(m, vfmat, 4, epsilon_ink = epsilon_ink)
        eps_eff[:,(2*i-1)] = eps_eff_t[:,1]
        eps_eff[:,(2*i)] = eps_eff_t[:,2]
    end
    plot_epseff(case, eps_eff, "fig_epseff_trapez")
end

function epsilon_effective_cosinus()
    # cosinus wave
    case = [0.5 1.0;
            1.0 1.0;
            1.5 1.0;
            2.0 1.0;
            2.5 1.0;
            3.0 1.0]
    epsilon_ink = 0.01
    eps_eff = Matrix{Float64}(undef,round(Integer,1.0/epsilon_ink),2*size(case,1))
    for i = 1:size(case,1)
        m = model_cosinus(case[i,1], case[i,2], 100)
        vfmat = calc_vfmat(m)
        eps_eff_t = get_epsilon_effective(m, vfmat, 2, epsilon_ink = epsilon_ink)
        eps_eff[:,(2*i-1)] = eps_eff_t[:,1]
        eps_eff[:,(2*i)] = eps_eff_t[:,2]
    end
    plot_epseff(case, eps_eff, "fig_epseff_cosinus")
end

#= ######################################################
cases plot
###################################################### =#

function plot_epseff_round_groove()
    # infinetely long round groove
    # plot all cases in one figure
    case = [0.01 3.56;
            0.05 17.2;
            0.1 33.15;
            0.2 62.55;
            0.5 142.79;
            0.9999 357.19] # cant calculate phi = 1.0
    m = Vector{Model}(undef,size(case,1))
    for i = 1:size(case,1)
        m[i] = model_circle_with_opening_line(2.0, 360, round(Integer,case[i,2]))
    end
    arr = set_arrangement(size(case,1), col = 2)
    plot_cases(case, m, arr, "fig_plot_round_groove")
end

function plot_epseff_v_groove()
    # infinetely long V groove
    # plot all cases in one figure
    case = [1E-3 0.1; # cant calc with phi = 0
            0.224 25.9;
            0.316 36.8;
            0.447 53.1;
            0.592 72.6;
            0.707 90;
            0.9999 178.4] # cant calculate phi = 1.0
    m = Vector{Model}(undef,size(case,1))
    for i = 1:size(case,1)
        m[i] = model_isosceles_triangle(3.0, case[i,2], 0.01)
    end
    arr = set_arrangement(size(case,1), col = 2)
    plot_cases(case, m, arr, "fig_plot_v_groove", refcase = 6)
end

function plot_epseff_square_groove()
    # square groove
    # plot all cases in one figure
    h = 2.0
    case = Matrix{Float64}(undef,10,2) # L/h, L
    for i = 1:10
        case[i,1] = i
        case[i,2] = i * h
    end
    m = Vector{Model}(undef,size(case,1))
    for i = 1:size(case,1)
        m[i] = model_rectangle(h, case[i,2], 0.05)
    end
    arr = set_arrangement(size(case,1), col = 5)
    plot_cases(case, m, arr, "fig_plot_square_groove", refcase = 8)
end

function plot_epseff_elbow_piece()
    # elbow piece
    # plot all cases in one figure
    case = [0.707 1.0 1.0;
            0.850 1.0 0.2]
    m = Vector{Model}(undef,size(case,1))
    for i = 1:size(case,1)
        m[i] = model_right_triangle(case[i,2], case[i,3], 0.01)
    end
    arr = set_arrangement(size(case,1), col = 2)
    plot_cases(case, m, arr, "fig_plot_elbow_piece", refcase = 1)
end

function plot_epseff_trapez()
    # trapez
    # plot all cases in one figure
    a = 2.0
    b = 3.0
    case = collect(1:10)
    m = Vector{Model}(undef,size(case,1))
    for i = 1:size(case,1)
        m[i] = model_trapez(a, b, case[i,1], 0.05)
    end
    arr = set_arrangement(size(case,1), col = 2)
    plot_cases(case, m, arr, "fig_plot_trapez", refcase = 1)
end

function plot_epseff_cosinus()
    # cosinus wave
    # plot all cases in one figure
    case = [0.5 1.0;
            1.0 1.0;
            1.5 1.0;
            2.0 1.0;
            2.5 1.0;
            3.0 1.0]
    m = Vector{Model}(undef,size(case,1))
    for i = 1:size(case,1)
        m[i] = model_cosinus(case[i,1], case[i,2], 100)
    end
    arr = set_arrangement(size(case,1), col = 2)
    plot_cases(case, m, arr, "fig_plot_cosinus", refcase = 1)
end


text:
"""
    DiagnosticVariable

Currently only holds information about a diagnostics variable.

Standard names are from:
http://cfconventions.org/Data/cf-standard-names/71/build/cf-standard-name-table.html

TODO: will expand to include definition (code) as well.
"""
struct DiagnosticVariable
    short::String
    units::String
    long::String
    standard::String
end
const Variables = OrderedDict{String, DiagnosticVariable}()

"""
    setup_variables()

Called at module initialization to define all currently defined diagnostic
variables.
"""
function setup_variables()
    Variables["u"] =
        DiagnosticVariable("u", "m s**-1", "zonal wind", "eastward_wind")
    Variables["v"] =
        DiagnosticVariable("v", "m s**-1", "meridional wind", "northward_wind")
    Variables["w"] = DiagnosticVariable(
        "w",
        "m s**-1",
        "vertical wind",
        "upward_air_velocity",
    )
    Variables["rho"] =
        DiagnosticVariable("rho", "kg m**-3", "air density", "air_density")
    Variables["temp"] =
        DiagnosticVariable("temp", "K", "air temperature", "air_temperature")
    Variables["pres"] =
        DiagnosticVariable("pres", "Pa", "air pressure", "air_pressure")
    Variables["thd"] = DiagnosticVariable(
        "thd",
        "K",
        "dry potential temperature",
        "air_potential_temperature",
    )
    Variables["thv"] = DiagnosticVariable(
        "thv",
        "K",
        "virtual potential temperature",
        "virtual_potential_temperature",
    )
    Variables["et"] = DiagnosticVariable(
        "et",
        "J kg**-1",
        "total specific energy",
        "specific_dry_energy_of_air",
    )
    Variables["ei"] = DiagnosticVariable(
        "ei",
        "J kg**-1",
        "internal specific energy",
        "internal_energy",
    )
    Variables["ht"] = DiagnosticVariable(
        "ht",
        "J kg**-1",
        "specific enthalpy based on total energy",
        "",
    )
    Variables["hi"] = DiagnosticVariable(
        "hi",
        "J kg**-1",
        "specific enthalpy based on internal energy",
        "atmosphere_enthalpy_content",
    )
    Variables["vort"] = DiagnosticVariable(
        "vort",
        "s**-1",
        "vertical component of relative velocity",
        "atmosphere_relative_velocity",
    )
end

ext: jl

max_stars_repo_name: leios/CLIMA

max_issues_repo_name: leios/CLIMA

max_forks_repo_name: leios/CLIMA


text:
module Circuit

using PyCall

import ..@pyfunc
import ..@pyclass

#const qulacs = PyNULL()
const circuit = PyNULL()

const circuit_class = [
    :QuantumCircuitOptimizer,
]

#=
for class in circuit_class
    @eval begin
        struct $class
            pyobj::PyObject
            $(class)(args...) = new(qulacs.circuit.$(class)(args...))
        end

        PyObject(t::$(class)) = t.pyobj

        function Base.propertynames(t::$(class))
            propertynames(getfield(t, :pyobj))
        end

        function Base.getproperty(t::$(class), s::Symbol)
            if s ∈ fieldnames($(class))
                return getfield(t, s)
            else
                return getproperty(getfield(t, :pyobj), s)
            end
        end

        export $(class)
    end
end
=#

for class in circuit_class
    @eval begin
        @pyclass circuit $(class)
        export $(class)
    end
end
function __init__()
    #copy!(qulacs, pyimport("qulacs"))
    copy!(circuit, pyimport("qulacs.circuit"))
end

end # module


text:
using Plots
using LaTeXStrings

# Probability of v < x, given m and T
p(x;m = 1., T = 298.15, R = 8.3145) = -exp(-m*x^2/(2*R*T)) + 1

# Collect x
x = collect(0:0.1:200)

# Set y with m = 1, T = 298.15K, R = 8.3145 J/Kmol
y = p.(x)

# plot
plot(x,y,label="")
plot!(xlabel=L"x / m s^{-1}",ylabel=L"p~(v<x)")

xl = 70
yl = p(xl)
plot!([-10,xl],[yl,yl],label="",color="black",linestyle=:dash)
plot!([xl,xl],[-10,yl],label="",color="black",linestyle=:dash)
scatter!([xl],[yl],label="",color="black",markerstyle=:circle)

annotate!(xl,-0.07,text(L"v~(p_x)",8))
annotate!(-12,yl,text(L"p_x",8))

plot!(xlim=[0,200],ylim=[0,1])

plot!(size=(400,400),framestyle=:box)

savefig("vinverse.pdf")


text:
using Documenter, SatelliteTasking

include("src/makeplots.jl")

makedocs(
    modules   = [SatelliteTasking],
    doctest   = false,
    clean     = true,
    linkcheck = true,
    format    = Documenter.HTML(),
    sitename  = "SatelliteTasking.jl",
    authors   = "Duncan Eddy",
    pages     = Any[
        "Home" => "index.md",
        "Modules" => Any[
            "Data Structures" => "modules/data_structures.md",
            "modules/collection.md",
            "modules/simulation.md",
            "Satellite Planning" => Any[
                "modules/satellite_planning/graph.md",
                "modules/satellite_planning/milp.md",
                "modules/satellite_planning/mdp.md",
            ],
            "Analysis" => Any[
                "modules/analysis/collect_uncertainty.md",
            ]
        ],
        "Function Index" => "function_index.md",
    ]
)

deploydocs(
    repo = "github.com/sisl/SatelliteTasking.jl",
    devbranch = "master",
    devurl = "latest",
    deps = makeplots,
)


text:
### Input widgets

"""
    init_wobsval([T], observable, value; default=nothing) -> observable, value

Return a suitable initial state for `observable` and `value` for a
widget. Any but one of these argument can be `nothing`. A new `observable`
will be created if the input `observable` is `nothing`. Passing in a
pre-existing `observable` will return the same observable, either setting the
observable to `value` (if specified as an input) or extracting and
returning its current value (if the `value` input is `nothing`).

Optionally specify the element type `T`; if `observable` is a
`Observables.Observable`, then `T` must agree with `eltype(observable)`.
"""
init_wobsval(::Nothing, ::Nothing; default=nothing) = _init_wobsval(nothing, default)
init_wobsval(::Nothing, value; default=nothing) = _init_wobsval(typeof(value), nothing, value)
init_wobsval(observable, value; default=nothing) = _init_wobsval(eltype(observable), observable, value)
init_wobsval(::Type{T}, ::Nothing, ::Nothing; default=nothing) where {T} =
    _init_wobsval(T, nothing, default)
init_wobsval(::Type{T}, observable, value; default=nothing) where {T} =
    _init_wobsval(T, observable, value)

_init_wobsval(::Nothing, value) = _init_wobsval(typeof(value), nothing, value)
_init_wobsval(::Type{T}, ::Nothing, ::Nothing) where {T} = error("must supply an initial value")
_init_wobsval(::Type{T}, ::Nothing, value) where {T} = Observable{T}(value), value
_init_wobsval(::Type{T}, observable::Observable{T}, ::Nothing) where {T} =
    _init_wobsval(T, observable, observable[])
function _init_wobsval(::Type{T}, observable::Observable{T}, value) where T
    setindex!(observable, value)
    observable, value
end

"""
    init_observable2widget(widget::GtkWidget, id, observable) -> updatesignal
    init_observable2widget(getter, setter, widget::GtkWidget, id, observable) -> updatesignal

Update the "display" value of the Gtk widget `widget` whenever `observable`
changes. `id` is the observable handler id for updating `observable` from the
widget, and is required to prevent the widget from responding to the
update by firing `observable`.

If `updatesignal` is garbage-collected, the widget will no longer
update. Most likely you should either `preserve` or store
`updatesignal`.
"""
function init_observable2widget(getter::Function,
                                setter!::Function,
                                widget::GtkWidget,
                                id, observable)
    on(observable; weak=true) do val
        if signal_handler_is_connected(widget, id)
            signal_handler_block(widget, id)  # prevent "recursive firing" of the handler
            curval = getter(widget)
            curval != val && setter!(widget, val)
            signal_handler_unblock(widget, id)
            nothing
        end
    end
end
init_observable2widget(widget::GtkWidget, id, observable) =
    init_observable2widget(defaultgetter, defaultsetter!, widget, id, observable)

defaultgetter(widget) = Gtk.G_.value(widget)
defaultsetter!(widget,val) = Gtk.G_.value(widget, val)

"""
    ondestroy(widget::GtkWidget, preserved)

Create a `destroy` callback for `widget` that terminates updating dependent signals.
"""
function ondestroy(widget::GtkWidget, preserved::AbstractVector)
    signal_connect(widget, "destroy") do widget
        empty!(preserved)
    end
    nothing
end

########################## Slider ############################

struct Slider{T<:Number} <: InputWidget{T}
    observable::Observable{T}
    widget::GtkScaleLeaf
    id::Culong
    preserved::Vector{Any}

    function Slider{T}(observable::Observable{T}, widget, id, preserved) where T
        obj = new{T}(observable, widget, id, preserved)
        gc_preserve(widget, obj)
        obj
    end
end
Slider(observable::Observable{T}, widget::GtkScaleLeaf, id, preserved) where {T} =
    Slider{T}(observable, widget, id, preserved)

medianidx(r) = (ax = axes(r)[1]; return (first(ax)+last(ax))÷2)
# differs from median(r) in that it always returns an element of the range
medianelement(r::AbstractRange) = r[medianidx(r)]

slider(observable::Observable, widget::GtkScaleLeaf, id, preserved = []) =
    Slider(observable, widget, id, preserved)

"""
    slider(range; widget=nothing, value=nothing, observable=nothing, orientation="horizontal")

Create a slider widget with the specified `range`. Optionally provide:
  - the GtkScale `widget` (by default, creates a new one)
  - the starting `value` (defaults to the median of `range`)
  - the (Observables.jl) `observable` coupled to this slider (by default, creates a new observable)
  - the `orientation` of the slider.
"""
function slider(range::AbstractRange{T};
                widget=nothing,
                value=nothing,
                observable=nothing,
                orientation="horizontal",
                syncsig=true,
                own=nothing) where T
    obsin = observable
    observable, value = init_wobsval(T, observable, value; default=medianelement(range))
    if own === nothing
        own = observable != obsin
    end
    if widget === nothing
        widget = GtkScale(lowercase(first(orientation)) == 'v',
                          first(range), last(range), step(range))
        Gtk.G_.size_request(widget, 200, -1)
    else
        adj = Gtk.Adjustment(widget)
        Gtk.G_.lower(adj, first(range))
        Gtk.G_.upper(adj, last(range))
        Gtk.G_.step_increment(adj, step(range))
    end
    Gtk.G_.value(widget, value)

    ## widget -> observable
    id = signal_connect(widget, "value_changed") do w
        observable[] = defaultgetter(w)
    end

    ## observable -> widget
    preserved = []
    if syncsig
        push!(preserved, init_observable2widget(widget, id, observable))
    end
    if own
        ondestroy(widget, preserved)
    end

    Slider(observable, widget, id, preserved)
end

# Adjust the range on a slider
# Is calling this `setindex!` too much of a pun?
function Base.setindex!(s::Slider, (range,value)::Tuple{AbstractRange, Any})
    first(range) <= value <= last(range) || error("$value is not within the span of $range")
    adj = Gtk.Adjustment(widget(s))
    Gtk.G_.lower(adj, first(range))
    Gtk.G_.upper(adj, last(range))
    Gtk.G_.step_increment(adj, step(range))
    Gtk.G_.value(widget(s), value)
end
Base.setindex!(s::Slider, range::AbstractRange) = setindex!(s, (range, s[]))

######################### Checkbox ###########################

struct Checkbox <: InputWidget{Bool}
    observable::Observable{Bool}
    widget::GtkCheckButtonLeaf
    id::Culong
    preserved::Vector{Any}

    function Checkbox(observable::Observable{Bool}, widget, id, preserved)
        obj = new(observable, widget, id, preserved)
        gc_preserve(widget, obj)
        obj
    end
end

checkbox(observable::Observable, widget::GtkCheckButtonLeaf, id, preserved=[]) =
    Checkbox(observable, widget, id, preserved)

"""
    checkbox(value=false; widget=nothing, observable=nothing, label="")

Provide a checkbox with the specified starting (boolean)
`value`. Optionally provide:
  - a GtkCheckButton `widget` (by default, creates a new one)
  - the (Observables.jl) `observable` coupled to this checkbox (by default, creates a new observable)
  - a display `label` for this widget
"""
function checkbox(value::Bool; widget=nothing, observable=nothing, label="", own=nothing)
    obsin = observable
    observable, value = init_wobsval(observable, value)
    if own === nothing
        own = observable != obsin
    end
    if widget === nothing
        widget = GtkCheckButton(label)
    end
    Gtk.G_.active(widget, value)

    id = signal_connect(widget, "clicked") do w
        observable[] = Gtk.G_.active(w)
    end
    preserved = []
    push!(preserved, init_observable2widget(w->Gtk.G_.active(w),
                                            (w,val)->Gtk.G_.active(w, val),
                                            widget, id, observable))
    if own
        ondestroy(widget, preserved)
    end

    Checkbox(observable, widget, id, preserved)
end
checkbox(; value=false, widget=nothing, observable=nothing, label="", own=nothing) =
    checkbox(value; widget=widget, observable=observable, label=label, own=own)

###################### ToggleButton ########################

struct ToggleButton <: InputWidget{Bool}
    observable::Observable{Bool}
    widget::GtkToggleButtonLeaf
    id::Culong
    preserved::Vector{Any}

    function ToggleButton(observable::Observable{Bool}, widget, id, preserved)
        obj = new(observable, widget, id, preserved)
        gc_preserve(widget, obj)
        obj
    end
end

togglebutton(observable::Observable, widget::GtkToggleButtonLeaf, id, preserved=[]) =
    ToggleButton(observable, widget, id, preserved)

"""
    togglebutton(value=false; widget=nothing, observable=nothing, label="")

Provide a togglebutton with the specified starting (boolean)
`value`. Optionally provide:
  - a GtkCheckButton `widget` (by default, creates a new one)
  - the (Observables.jl) `observable` coupled to this button (by default, creates a new observable)
  - a display `label` for this widget
"""
function togglebutton(value::Bool; widget=nothing, observable=nothing, label="", own=nothing)
    obsin = observable
    observable, value = init_wobsval(observable, value)
    if own === nothing
        own = observable != obsin
    end
    if widget === nothing
        widget = GtkToggleButton(label)
    end
    Gtk.G_.active(widget, value)

    id = signal_connect(widget, "clicked") do w
        setindex!(observable, Gtk.G_.active(w))
    end
    preserved = []
    push!(preserved, init_observable2widget(w->Gtk.G_.active(w),
                                        (w,val)->Gtk.G_.active(w, val),
                                        widget, id, observable))
    if own
        ondestroy(widget, preserved)
    end

    ToggleButton(observable, widget, id, preserved)
end
togglebutton(; value=false, widget=nothing, observable=nothing, label="", own=nothing) =
    togglebutton(value; widget=widget, observable=observable, label=label, own=own)

######################### Button ###########################

struct Button <: InputWidget{Nothing}
    observable::Observable{Nothing}
    widget::Union{GtkButtonLeaf,GtkToolButtonLeaf}
    id::Culong

    function Button(observable::Observable{Nothing}, widget, id)
        obj = new(observable, widget, id)
        gc_preserve(widget, obj)
        obj
    end
end

button(observable::Observable, widget::Union{GtkButtonLeaf,GtkToolButtonLeaf}, id) =
    Button(observable, widget, id)

"""
    button(label; widget=nothing, observable=nothing)
    button(; label=nothing, widget=nothing, observable=nothing)

Create a push button with text-label `label`. Optionally provide:
  - a GtkButton `widget` (by default, creates a new one)
  - the (Observables.jl) `observable` coupled to this button (by default, creates a new observable)
"""
function button(;
                label::Union{Nothing,String,Symbol}=nothing,
                widget=nothing,
                observable=nothing,
                own=nothing)
    obsin = observable
    if observable === nothing
        observable = Observable(nothing)
    end
    if own === nothing
        own = observable != obsin
    end
    if widget === nothing
        widget = GtkButton(label)
    end

    id = signal_connect(widget, "clicked") do w
        setindex!(observable, nothing)
    end

    Button(observable, widget, id)
end
button(label::Union{String,Symbol}; widget=nothing, observable=nothing, own=nothing) =
    button(; label=label, widget=widget, observable=observable, own=own)

######################## Textbox ###########################

struct Textbox{T} <: InputWidget{T}
    observable::Observable{T}
    widget::GtkEntryLeaf
    id::Culong
    preserved::Vector{Any}
    range

    function Textbox{T}(observable::Observable{T}, widget, id, preserved, range) where T
        obj = new{T}(observable, widget, id, preserved, range)
        gc_preserve(widget, obj)
        obj
    end
end
Textbox(observable::Observable{T}, widget::GtkEntryLeaf, id, preserved, range) where {T} =
    Textbox{T}(observable, widget, id, preserved, range)

textbox(observable::Observable, widget::GtkButtonLeaf, id, preserved = []) =
    Textbox(observable, widget, id, preserved)

"""
    textbox(value=""; widget=nothing, observable=nothing, range=nothing, gtksignal=:activate)
    textbox(T::Type; widget=nothing, observable=nothing, range=nothing, gtksignal=:activate)

Create a box for entering text. `value` is the starting value; if you
don't want to provide an initial value, you can constrain the type
with `T`. Optionally specify the allowed range (e.g., `-10:10`)
for numeric entries, and/or provide the (Observables.jl) `observable` coupled
to this text box. Finally, you can specify which Gtk observable (e.g.
`activate`, `changed`) you'd like the widget to update with.
"""
function textbox(::Type{T};
                 widget=nothing,
                 value=nothing,
                 range=nothing,
                 observable=nothing,
                 syncsig=true,
                 own=nothing,
                 gtksignal::String="activate") where T
    if T <: AbstractString && range !== nothing
        throw(ArgumentError("You cannot set a range on a string textbox"))
    end
    obsin = observable
    observable, value = init_wobsval(T, observable, value; default="")
    if own === nothing
        own = observable != obsin
    end
    if widget === nothing
        widget = GtkEntry()
    end
    set_gtk_property!(widget, "text", value)

    id = signal_connect(widget, gtksignal) do w
        setindex!(observable, entrygetter(w, observable, range))
    end

    preserved = []
    function checked_entrysetter!(w, val)
        val ∈ range || throw(ArgumentError("$val is not within $range"))
        entrysetter!(w, val)
    end
    if syncsig
        push!(preserved, init_observable2widget(w->entrygetter(w, observable, range),
                                                range === nothing ? entrysetter! : checked_entrysetter!,
                                                widget, id, observable))
    end
    own && ondestroy(widget, preserved)

    Textbox(observable, widget, id, preserved, range)
end
function textbox(value::T;
                 widget=nothing,
                 range=nothing,
                 observable=nothing,
                 syncsig=true,
                 own=nothing,
                 gtksignal="activate") where T
    textbox(T; widget=widget, value=value, range=range, observable=observable, syncsig=syncsig, own=own, gtksignal=gtksignal)
end

entrygetter(w, ::Observable{<:AbstractString}, ::Nothing) =
    get_gtk_property(w, "text", String)
function entrygetter(w, observable::Observable{T}, range) where T
    val = tryparse(T, get_gtk_property(w, "text", String))
    if val === nothing
        nval = observable[]
        # Invalid entry, restore the old value
        entrysetter!(w, nval)
    else
        nval = nearest(val, range)
        if val != nval
            entrysetter!(w, nval)
        end
    end
    nval
end
nearest(val, ::Nothing) = val
function nearest(val, r::AbstractRange)
    i = round(Int, (val - first(r))/step(r)) + 1
    ax = axes(r)[1]
    r[clamp(i, first(ax), last(ax))]
end

entrysetter!(w, val) = set_gtk_property!(w, "text", string(val))

######################### Textarea ###########################

struct Textarea <: InputWidget{String}
    observable::Observable{String}
    widget::GtkTextViewLeaf
    id::Culong
    preserved::Vector{Any}

    function Textarea(observable::Observable{String}, widget, id, preserved)
        obj = new(observable, widget, id, preserved)
        gc_preserve(widget, obj)
        obj
    end
end

"""
    textarea(value=""; widget=nothing, observable=nothing)

Creates an extended text-entry area. Optionally provide a GtkTextView `widget`
and/or the (Observables.jl) `observable` associated with this widget. The
`observable` updates when you type.
"""
function textarea(value::String="";
                  widget=nothing,
                  observable=nothing,
                  syncsig=true,
                  own=nothing)
    obsin = observable
    observable, value = init_wobsval(observable, value)
    if own === nothing
        own = observable != obsin
    end
    if widget === nothing
        widget = GtkTextView()
    end
    buf = widget[:buffer, GtkTextBuffer]
    buf[String] = value
    sleep(0.01)   # without this, we more frequently get segfaults...not sure why

    id = signal_connect(buf, "changed") do w
        setindex!(observable, w[String])
    end

    preserved = []
    if syncsig
        # GtkTextBuffer is not a GtkWidget, so we have to do this manually
        push!(preserved, on(observable; weak=true) do val
                  signal_handler_block(buf, id)
                  curval = get_gtk_property(buf, "text", String)
                  curval != val && set_gtk_property!(buf, "text", val)
                  signal_handler_unblock(buf, id)
                  nothing
              end)
    end
    own && ondestroy(widget, preserved)

    Textarea(observable, widget, id, preserved)
end

##################### SelectionWidgets ######################

struct Dropdown <: InputWidget{String}
    observable::Observable{String}
    mappedsignal::Observable{Any}
    widget::GtkComboBoxTextLeaf
    id::Culong
    preserved::Vector{Any}

    function Dropdown(observable::Observable{String}, mappedsignal::Observable, widget, id, preserved)
        obj = new(observable, mappedsignal, widget, id, preserved)
        gc_preserve(widget, obj)
        obj
    end
end

"""
    dropdown(choices; widget=nothing, value=first(choices), observable=nothing, label="", with_entry=true, icons, tooltips)

Create a "dropdown" widget. `choices` can be a vector (or other iterable) of
options. These options might either be a list of strings, or a list of `choice::String => func` pairs
so that an action encoded by `func` can be taken when `choice` is selected.

Optionally specify
  - the GtkComboBoxText `widget` (by default, creates a new one)
  - the starting `value`
  - the (Observables.jl) `observable` coupled to this slider (by default, creates a new observable)
  - whether the widget should allow text entry

# Examples

    dd = dropdown(["one", "two", "three"])

To link a callback to the dropdown, use

    dd = dropdown(("turn red"=>colorize_red, "turn green"=>colorize_green))
    on(dd.mappedsignal) do cb
        cb(img)                     # img is external data you want to act on
    end

`cb` does not fire for the initial value of `dd`; if this is desired, manually execute
`dd[] = dd[]` after defining this action.

`dd.mappedsignal` is a function-observable only for the pairs syntax for `choices`.
"""
function dropdown(; choices=nothing,
                  widget=nothing,
                  value=juststring(first(choices)),
                  observable=nothing,
                  label="",
                  with_entry=true,
                  icons=nothing,
                  tooltips=nothing,
                  own=nothing)
    obsin = observable
    observable, value = init_wobsval(String, observable, value)
    if own === nothing
        own = observable != obsin
    end
    if widget === nothing
        widget = GtkComboBoxText()
    end
    if choices !== nothing
        empty!(widget)
    else
        error("Pre-loading the widget is not yet supported")
    end
    allstrings = all(x->isa(x, AbstractString), choices)
    allstrings || all(x->isa(x, Pair), choices) || throw(ArgumentError("all elements must either be strings or pairs, got $choices"))
    str2int = Dict{String,Int}()
    int2str = Dict{Int,String}()
    getactive(w) = int2str[get_gtk_property(w, :active, Int)]
    setactive!(w, val) = set_gtk_property!(w, "active", str2int[val])
    k = -1
    for c in choices
        str = juststring(c)
        push!(widget, str)
        str2int[str] = (k+=1)
        int2str[k] = str
    end
    if value == nothing
        value = juststring(first(choices))
    end
    setactive!(widget, value)

    id = signal_connect(widget, "changed") do w
        setindex!(observable, getactive(w))
    end

    preserved = []
    push!(preserved, init_observable2widget(getactive, setactive!, widget, id, observable))
    mappedsignal = Observable{Any}(nothing)
    if !allstrings
        choicedict = Dict(choices...)
        map!(mappedsignal, observable) do val
            choicedict[val]
        end
    end
    if own
        ondestroy(widget, preserved)
    end

    Dropdown(observable, mappedsignal, widget, id, preserved)
end

function Base.precompile(w::Dropdown)
    return invoke(precompile, Tuple{Widget}, w) & precompile(w.mappedsignal)
end

function dropdown(choices; kwargs...)
    dropdown(; choices=choices, kwargs...)
end

juststring(str::AbstractString) = String(str)
juststring(p::Pair{String}) = p.first
pairaction(str::AbstractString) = x->nothing
pairaction(p::Pair{String,F}) where {F<:Function} = p.second

# """
# radiobuttons: see the help for `dropdown`
# """
# radiobuttons(opts; kwargs...) =
#     Options(:RadioButtons, opts; kwargs...)

# """
# selection: see the help for `dropdown`
# """
# function selection(opts; multi=false, kwargs...)
#     if multi
#         options = getoptions(opts)
#         #observable needs to be of an array of values, not just a single value
#         observable = Observable(collect(values(options))[1:1])
#         Options(:SelectMultiple, options; observable=observable, kwargs...)
#     else
#         Options(:Select, opts; kwargs...)
#     end
# end

# Base.@deprecate select(opts; kwargs...) selection(opts, kwargs...)

# """
# togglebuttons: see the help for `dropdown`
# """
# togglebuttons(opts; kwargs...) =
#     Options(:ToggleButtons, opts; kwargs...)

# """
# selection_slider: see the help for `dropdown`
# If the slider has numeric (<:Real) values, and its observable is updated, it will
# update to the nearest value from the range/choices provided. To disable this
# behaviour, so that the widget state will only update if an exact match for
# observable value is found in the range/choice, use `syncnearest=false`.
# """
# selection_slider(opts; kwargs...) = begin
#     if !haskey(Dict(kwargs), :value_label)
#         #default to middle of slider
#         mid_idx = medianidx(opts)
#         push!(kwargs, (:sel_mid_idx, mid_idx))
#     end
#     Options(:SelectionSlider, opts; kwargs...)
# end

# """
# `vselection_slider(args...; kwargs...)`

# Shorthand for `selection_slider(args...; orientation="vertical", kwargs...)`
# """
# vselection_slider(args...; kwargs...) = selection_slider(args...; orientation="vertical", kwargs...)

# function nearest_val(x, val)
#     local valbest
#     local dxbest = typemax(Float64)
#     for v in x
#         dx = abs(v-val)
#         if dx < dxbest
#             dxbest = dx
#             valbest = v
#         end
#     end
#     valbest
# end

### Output Widgets

######################## Label #############################

struct Label <: Widget
    observable::Observable{String}
    widget::GtkLabelLeaf
    preserved::Vector{Any}

    function Label(observable::Observable{String}, widget, preserved)
        obj = new(observable, widget, preserved)
        gc_preserve(widget, obj)
        obj
    end
end

"""
    label(value; widget=nothing, observable=nothing)

Create a text label displaying `value` as a string; new values may
displayed by pushing to the widget. Optionally specify
  - the GtkLabel `widget` (by default, creates a new one)
  - the (Observables.jl) `observable` coupled to this label (by default, creates a new observable)
"""
function label(value;
               widget=nothing,
               observable=nothing,
               syncsig=true,
               own=nothing)
    obsin = observable
    observable, value = init_wobsval(String, observable, value)
    if own === nothing
        own = observable != obsin
    end
    if widget === nothing
        widget = GtkLabel(value)
    else
        set_gtk_property!(widget, "label", value)
    end
    preserved = []
    if syncsig
        let widget=widget
            push!(preserved, on(observable; weak=true) do val
                set_gtk_property!(widget, "label", val)
            end)
        end
    end
    if own
        ondestroy(widget, preserved)
    end
    Label(observable, widget, preserved)
end

# export Latex, Progress

# Base.@deprecate html(value; label="")  HTML(value)

# type Latex <: Widget
#     label::AbstractString
#     value::AbstractString
# end
# latex(label, value::AbstractString) = Latex(label, value)
# latex(value::AbstractString; label="") = Latex(label, value)
# latex(value; label="") = Latex(label, mimewritable("application/x-latex", value) ? stringmime("application/x-latex", value) : stringmime("text/latex", value))

# ## # assume we already have Latex
# ## writemime(io::IO, m::MIME{symbol("application/x-latex")}, l::Latex) =
# ##     write(io, l.value)

# type Progress <: Widget
#     label::AbstractString
#     value::Int
#     range::AbstractRange
#     orientation::String
#     readout::Bool
#     readout_format::String
#     continuous_update::Bool
# end

# progress(args...) = Progress(args...)
# progress(;label="", value=0, range=0:100, orientation="horizontal",
#             readout=true, readout_format="d", continuous_update=true) =
#     Progress(label, value, range, orientation, readout, readout_format, continuous_update)

# # Make a widget out of a domain
# widget(x::Observable, label="") = x
# widget(x::Widget, label="") = x
# widget(x::AbstractRange, label="") = selection_slider(x, label=label)
# widget(x::AbstractVector, label="") = togglebuttons(x, label=label)
# widget(x::Associative, label="") = togglebuttons(x, label=label)
# widget(x::Bool, label="") = checkbox(x, label=label)
# widget(x::AbstractString, label="") = textbox(x, label=label, typ=AbstractString)
# widget{T <: Number}(x::T, label="") = textbox(typ=T, value=x, label=label)

# ### Set!

# """
# `set!(w::Widget, fld::Symbol, val)`

# Set the value of a widget property and update all displayed instances of the
# widget. If `val` is a `Observable`, then updates to that observable will be reflected in
# widget instances/views.

# If `fld` is `:value`, `val` is also `push!`ed to `observable(w)`
# """
# function set!(w::Widget, fld::Symbol, val)
#     fld == :value && val != observable(w).value && push!(observable(w), val)
#     setfield!(w, fld, val)
#     update_view(w)
#     w
# end

# set!(w::Widget, fld::Symbol, valsig::Observable) = begin
#     map(val -> set!(w, fld, val), valsig) |> preserve
# end

# set!{T<:Options}(w::T, fld::Symbol, val::Union{Observable,Any}) = begin
#     fld == :options && (val = getoptions(val))
#     invoke(set!, (Widget, Symbol, typeof(val)), w, fld, val)
# end

########################## SpinButton ########################

struct SpinButton{T<:Number} <: InputWidget{T}
    observable::Observable{T}
    widget::GtkSpinButtonLeaf
    id::Culong
    preserved::Vector{Any}

    function SpinButton{T}(observable::Observable{T}, widget, id, preserved) where T
        obj = new{T}(observable, widget, id, preserved)
        gc_preserve(widget, obj)
        obj
    end
end
SpinButton(observable::Observable{T}, widget::GtkSpinButtonLeaf, id, preserved) where {T} =
    SpinButton{T}(observable, widget, id, preserved)

spinbutton(observable::Observable, widget::GtkSpinButtonLeaf, id, preserved = []) =
    SpinButton(observable, widget, id, preserved)

"""
    spinbutton(range; widget=nothing, value=nothing, observable=nothing, orientation="horizontal")

Create a spinbutton widget with the specified `range`. Optionally provide:
  - the GtkSpinButton `widget` (by default, creates a new one)
  - the starting `value` (defaults to the start of `range`)
  - the (Observables.jl) `observable` coupled to this spinbutton (by default, creates a new observable)
  - the `orientation` of the spinbutton.
"""
function spinbutton(range::AbstractRange{T};
                    widget=nothing,
                    value=nothing,
                    observable=nothing,
                    orientation="horizontal",
                    syncsig=true,
                    own=nothing) where T
    obsin = observable
    observable, value = init_wobsval(T, observable, value; default=range.start)
    if own === nothing
        own = observable != obsin
    end
    if widget === nothing
        widget = GtkSpinButton(
                          first(range), last(range), step(range))
        Gtk.G_.size_request(widget, 200, -1)
    else
        adj = Gtk.Adjustment(widget)
        Gtk.G_.lower(adj, first(range))
        Gtk.G_.upper(adj, last(range))
        Gtk.G_.step_increment(adj, step(range))
    end
    if lowercase(first(orientation)) == 'v'
        Gtk.G_.orientation(Gtk.GtkOrientable(widget),
                           Gtk.GConstants.GtkOrientation.VERTICAL)
    end
    Gtk.G_.value(widget, value)

    ## widget -> observable
    id = signal_connect(widget, "value_changed") do w
        setindex!(observable, defaultgetter(w))
    end

    ## observable -> widget
    preserved = []
    if syncsig
        push!(preserved, init_observable2widget(widget, id, observable))
    end
    if own
        ondestroy(widget, preserved)
    end

    SpinButton(observable, widget, id, preserved)
end

# Adjust the range on a spinbutton
# Is calling this `setindex!` too much of a pun?
function Base.setindex!(s::SpinButton, (range,value)::Tuple{AbstractRange,Any})
    first(range) <= value <= last(range) || error("$value is not within the span of $range")
    adj = Gtk.Adjustment(widget(s))
    Gtk.G_.lower(adj, first(range))
    Gtk.G_.upper(adj, last(range))
    Gtk.G_.step_increment(adj, step(range))
    Gtk.G_.value(widget(s), value)
end
Base.setindex!(s::SpinButton, range::AbstractRange) = setindex!(s, (range, s[]))

########################## CyclicSpinButton ########################

struct CyclicSpinButton{T<:Number} <: InputWidget{T}
    observable::Observable{T}
    widget::GtkSpinButtonLeaf
    id::Culong
    preserved::Vector{Any}

    function CyclicSpinButton{T}(observable::Observable{T}, widget, id, preserved) where T
        obj = new{T}(observable, widget, id, preserved)
        gc_preserve(widget, obj)
        obj
    end
end
CyclicSpinButton(observable::Observable{T}, widget::GtkSpinButtonLeaf, id, preserved) where {T} =
    CyclicSpinButton{T}(observable, widget, id, preserved)

cyclicspinbutton(observable::Observable, widget::GtkSpinButtonLeaf, id, preserved = []) =
    CyclicSpinButton(observable, widget, id, preserved)

"""
    cyclicspinbutton(range, carry_up; widget=nothing, value=nothing, observable=nothing, orientation="horizontal")

Create a cyclicspinbutton widget with the specified `range` that updates a `carry_up::Observable{Bool}`
only when a value outside the `range` of the cyclicspinbutton is pushed. `carry_up`
is updated with `true` when the cyclicspinbutton is updated with a value that is
higher than the maximum of the range. When cyclicspinbutton is updated with a value that is smaller
than the minimum of the range `carry_up` is updated with `false`. Optional arguments are:
  - the GtkSpinButton `widget` (by default, creates a new one)
  - the starting `value` (defaults to the start of `range`)
  - the (Observables.jl) `observable` coupled to this cyclicspinbutton (by default, creates a new observable)
  - the `orientation` of the cyclicspinbutton.
"""
function cyclicspinbutton(range::AbstractRange{T}, carry_up::Observable{Bool};
                          widget=nothing,
                          value=nothing,
                          observable=nothing,
                          orientation="horizontal",
                          syncsig=true,
                          own=nothing) where T
    obsin = observable
    observable, value = init_wobsval(T, observable, value; default=range.start)
    if own === nothing
        own = observable != obsin
    end
    if widget === nothing
        widget = GtkSpinButton(first(range) - step(range), last(range) + step(range), step(range))
        Gtk.G_.size_request(widget, 200, -1)
    else
        adj = Gtk.Adjustment(widget)
        Gtk.G_.lower(adj, first(range) - step(range))
        Gtk.G_.upper(adj, last(range) + step(range))
        Gtk.G_.step_increment(adj, step(range))
    end
    if lowercase(first(orientation)) == 'v'
        Gtk.G_.orientation(Gtk.GtkOrientable(widget),
                           Gtk.GConstants.GtkOrientation.VERTICAL)
    end
    Gtk.G_.value(widget, value)

    ## widget -> observable
    id = signal_connect(widget, "value_changed") do w
        setindex!(observable, defaultgetter(w))
    end

    ## observable -> widget
    preserved = []
    if syncsig
        push!(preserved, init_observable2widget(widget, id, observable))
    end
    if own
        ondestroy(widget, preserved)
    end

    push!(preserved, on(observable; weak=true) do val
        if val > maximum(range)
            observable.val = minimum(range)
            setindex!(carry_up, true)
        end
    end)
    push!(preserved, on(observable; weak=true) do val
        if val < minimum(range)
            observable.val = maximum(range)
            setindex!(carry_up, false)
        end
    end)
    setindex!(observable, value)

    CyclicSpinButton(observable, widget, id, preserved)
end

######################## ProgressBar #########################

struct ProgressBar{T <: Number} <: Widget
    observable::Observable{T}
    widget::GtkProgressBarLeaf
    preserved::Vector{Any}

    function ProgressBar{T}(observable::Observable{T}, widget, preserved) where T
        obj = new{T}(observable, widget, preserved)
        gc_preserve(widget, obj)
        obj
    end
end
ProgressBar(observable::Observable{T}, widget::GtkProgressBarLeaf, preserved) where {T} =
    ProgressBar{T}(observable, widget, preserved)

# convert a member of the interval into a decimal
interval2fraction(x::AbstractInterval, i) = (i - minimum(x))/IntervalSets.width(x)

"""
    progressbar(interval::AbstractInterval; widget=nothing, observable=nothing)

Create a progressbar displaying the current state in the given interval; new iterations may be
displayed by pushing to the widget. Optionally specify
  - the GtkProgressBar `widget` (by default, creates a new one)
  - the (Observables.jl) `observable` coupled to this progressbar (by default, creates a new observable)

# Examples

```julia-repl
julia> using GtkObservables

julia> using IntervalSets

julia> n = 10

julia> pb = progressbar(1..n)
Gtk.GtkProgressBarLeaf with 1: "input" = 1 Int64

julia> for i = 1:n
           # do something
           push!(pb, i)
       end

```
"""
function progressbar(interval::AbstractInterval{T};
                     widget=nothing,
                     observable=nothing,
                     syncsig=true,
                     own=nothing) where T<:Number
    value = minimum(interval)
    obsin = observable
    observable, value = init_wobsval(T, observable, value)
    if own === nothing
        own = observable != obsin
    end
    if widget === nothing
        widget = GtkProgressBar()
    else
        set_gtk_property!(widget, "fraction", interval2fraction(interval, value))
    end
    preserved = []
    if syncsig
        push!(preserved, on(observable; weak=true) do val
            set_gtk_property!(widget, "fraction", interval2fraction(interval, val))
        end)
    end
    if own
        ondestroy(widget, preserved)
    end
    ProgressBar(observable, widget, preserved)
end

progressbar(range::AbstractRange; args...) = progressbar(ClosedInterval(range), args...)


text:
struct PlantCollection{Plants}
    function PlantCollection(plants::Vector{Symbol})
        return new{ntuple(p -> plants[p], length(plants))}()
    end
end

nplants(::Type{PlantCollection{Plants}}) where Plants = length(Plants)
nplants(::PlantCollection{Plants}) where Plants = length(Plants)
plants(::Type{PlantCollection{Plants}}) where Plants = [Plants...]
plants(::PlantCollection{Plants}) where Plants = [Plants...]

const Skellefteälven = PlantCollection([:Rebnis,
                                        :Sadva,
                                        :Bergnas,
                                        :Slagnas,
                                        :Bastusel,
                                        :Grytfors,
                                        :Gallejaur,
                                        :Vargfors,
                                        :Rengard,
                                        :Batfors,
                                        :Finnfors,
                                        :Granfors,
                                        :Krangfors,
                                        :Selsfors,
                                        :Kvistforsen])


text:
using JSServe, Observables
using JSServe: @js_str, onjs, Button, TextField, Slider, linkjs, Session, App
using JSServe.DOM

app = App() do session::Session
    s1 = Slider(1:100)
    s2 = Slider(1:100)
    b = Button("hi")
    t = TextField("enter your text")
    s_value = s1.value
    linkjs(session, s1.value, s2.value)
    test = [1,2,3]
    onjs(session, s1.value, js"(v)=> console.log(v + ' ' + $(test))")
    on(t) do text
        println(text)
    end
    return DOM.div(s1, s2, b, t)
end

isdefined(Main, :server) && close(server)

server = JSServe.Server(app, "127.0.0.1", 8081)


text:
export VoidTeardown, voidTeardown

import Base: ==

"""
    VoidTeardown()

VoidTeardown object does nothing on unsubscription.
It is usefull for synchronous observables and observables which cannot be cancelled after execution.

See also: [`Teardown`](@ref), [`VoidTeardownLogic`](@ref)
"""
struct VoidTeardown <: Teardown end

as_teardown(::Type{<:VoidTeardown}) = VoidTeardownLogic()

"""
    voidTeardown

An instance of VoidTeardown singleton object.

See also: [`VoidTeardown`](@ref)
"""
const voidTeardown = VoidTeardown()


text:
using DashBootstrapComponents

popover_children = "I am a popover!";

popovers = html_div([
    dbc_button(
        "Click",
        id = "click-target",
        color = "danger",
        className = "me-1",
        n_clicks = 0,
    ),
    dbc_popover(popover_children, target = "click-target", body = true, trigger = "click"),
    dbc_button(
        "Focus",
        id = "focus-target",
        color = "danger",
        className = "me-1",
        n_clicks = 0,
    ),
    dbc_popover(popover_children, target = "focus-target", body = true, trigger = "focus"),
    dbc_button(
        "Hover",
        id = "hover-target",
        color = "danger",
        className = "me-1",
        n_clicks = 0,
    ),
    dbc_popover(popover_children, target = "hover-target", body = true, trigger = "hover"),
    dbc_button("Legacy", id = "legacy-target", color = "danger", n_clicks = 0),
    dbc_popover(
        popover_children,
        target = "legacy-target",
        body = true,
        trigger = "legacy",
    ),
]);

max_issues_repo_name: sthagen/dash-bootstrap-components

max_forks_repo_name: sthagen/dash-bootstrap-components


text:
function check_dims(model)
    M, N = size(model)
    for (expected_len, field) in [
            (N, :lb), (N, :ub), (M, :b),
            (N, :c), (M, :mets), (N, :rxns)
        ]
        dat = getfield(model, field)
        dat_len = length(dat)
        expected_len != dat_len &&
            error("'$(field)' len = $(dat_len) missmatch model size = $(size(model))")
    end
end


text:
module ZonalFlow

using OrdinaryDiffEq
using StochasticDiffEq
using DiffEqNoiseProcess
using DiffEqCallbacks
using RecursiveArrayTools
using FFTW
using LinearAlgebra
using Random
using Distributions
using NPZ

include("structures.jl")
include("ic.jl")
include("coeffs.jl")
include("solve.jl")
include("equations.jl")
# include("noise.jl")
include("tools.jl")
include("analysis.jl")
include("writers.jl")
# include("deprecated.jl")
# include("future.jl")

export  Domain,
        Coefficients,
        PointJet,
        Kolmogorov,
        Stochastic,
        BetaPlane

export  NL,
        GQL,
        GCE2,
        CE2

export  integrate

end


text:
function create_geostationary_granule(
    target_lon::Real,
    target_lat::Real,
    central_longitude::Real,
    N_step=35,
    N_pixel=1016,
    x_space=5400, # footprint size E-W in m
    y_space=2700 # footprint size N-S in m
)

    N = N_step * N_pixel

    # lon/lat projection object
    wgs84 = Projection("+proj=longlat +datum=WGS84 +no_defs")
    # geostationary projection
    geo_proj = Projection("+proj=geos +lon_0=$(central_longitude) +h=35785831.0 +sweep=y")

    # Produce a rectangular grid given the parameters
    c_x = 0.0
    c_y = 0.0
    try
        c_x, c_y = Proj4.transform(wgs84, geo_proj, [target_lon, target_lat])
    catch
        return nothing
    end

    # Move up to the "top left" scene of the grid
    topleft_x = c_x + (N_step - 1) / 2 * x_space
    topleft_y = c_y + (N_pixel - 1) / 2 * y_space

    x_coords = Float64[]
    y_coords = Float64[]
    dts = DateTime[]

    for xstep in 0:N_step-1
        for ystep in 0:N_pixel-1

            this_x = topleft_x - x_space * xstep
            this_y = topleft_y - y_space * ystep

            push!(x_coords, this_x)
            push!(y_coords, this_y)

        end
    end

    lons = Float64[]
    lats = Float64[]

    for i in 1:N

        this_lon = NaN
        this_lat = NaN

        try
            this_lon, this_lat =
                Proj4.transform(geo_proj, wgs84, [x_coords[i], y_coords[i]])

        catch

        end

        push!(lons, this_lon)
        push!(lats, this_lat)

    end

    locarray = Geolocation[]

    # Produce the location array
    # These are the full granule!
    for i in 1:N
        this_loc = Geolocation(lons[i] ,lats[i])
        push!(locarray, this_loc)
    end

    return geo_proj, locarray

end

function calculate_geocarb_uncertainty(continuum::Real)

    #a = 833661.0
    #b = 4109.0

    # THIS IS RELATIVE UNCERTAINTY!
    # To get absolute uncertainty for 757nm you must multiply
    # by the reflected continuum level radiance!
    #return 1.0 / sqrt(a * albedo * cos(deg2rad(sza)) - b)

    a = 0.0004
    b = 0.0016

    return sqrt(a + continuum * b)

end

function GeostationaryIntensiveSampling(
    radius::Real,
    target_lon::Real,
    target_lat::Real,
    central_longitude::Real,
    start_time::DateTime,
    end_time::DateTime;
    N_step=35,
    N_pixel=1016,
    x_space=5400, # footprint size E-W in m
    y_space=2700, # footprint size N-S in m
    stare_time=Dates.Second(9) + Dates.Millisecond(600) # How long does one frame take?
)

    N = N_step * N_pixel
    instrument = "Geostationary $(central_longitude)"

    # This would make conversions from compound periods possible
    stare_time_period = Dates.Millisecond(Dates.toms(stare_time))

    geo_proj, locarray = create_geostationary_granule(
        target_lon,
        target_lat,
        central_longitude,
        N_step,
        N_pixel,
        x_space,
        y_space
    )

    lons = (p -> p.lon).(locarray)
    lats = (p -> p.lat).(locarray)

    lon_bound_min = minimum(lons)
    lon_bound_max = maximum(lons)
    lat_bound_min = minimum(lats)
    lat_bound_max = maximum(lats)

    vnp_sd = create_VNP_SD_from_locbounds(
        lon_bound_min,
        lat_bound_min,
        lon_bound_max,
        lat_bound_max
    )

    scenearray = Scene[]

    # repeat the loop over all locations as long as the measurement
    # time stays within the stated limit..

    current_frame_time = start_time
    current_granule = 1
    current_frame = 1

    while current_frame_time <= end_time

        #println(current_frame, "/", current_granule)

        for i in 1:N_pixel

            # Grab the location corresponding to pixel/step
            this_loc = locarray[i + (current_frame - 1) * N_pixel]

            this_loctime = GeolocationTime(
                this_loc,
                current_frame_time
            )

            # #############################
            # Calculate viewing zenith here
            # #############################
            #
            # Position of scene in ECEF
            r_location = geodetic_to_ecef(this_loc.lon, this_loc.lat, 0.0)
            # Position of satellite in ECEF
            r_satellite = geodetic_to_ecef(central_longitude, 0.0, 0.0)
            # Normalized location
            r_norm = normalize(r_location)
            r_loc_to_sat_norm = normalize(r_satellite - r_location)
            _tmp = dot(r_loc_to_sat_norm, r_norm)
            if (_tmp > 1) & (_tmp < 1 + 1e-6)
                _tmp = 1.0
            end

            this_vza = rad2deg(acos(_tmp))

            this_scene = create_SIF_scene(
                instrument,
                "N/A",
                this_loctime,
                this_vza,
                calculate_geocarb_uncertainty,
                vnp_sd
            )

            push!(scenearray, this_scene)

        end

        if current_frame < N_step
            current_frame += 1
        else
            current_frame = 1
            current_granule += 1
        end

        current_frame_time += stare_time_period
    end

    N_scene = length(scenearray)
    # Once the full scenes have been done, we need to subset
    # to the radius given by the user. The reason why we can
    # do this only AFTER all the scenes have been calculated,
    # is that we must have scenes corresponding to some real
    # scanning operation, and can only filter afterwards.

    radius_mask = Int[]
    for i in 1:N_scene
        if check_location_within_radius(target_lon, target_lat,
                                        radius, scenearray[i].loctime)

            push!(radius_mask, i)

        end
    end

    # Match the location array with the locations found in the
    # scenes. Drop any locations that don't appear in scenes.
    new_locarray = unique((p -> p.loctime.loc).(scenearray))

    info = "Geostationary intensive sampling at $(target_lon), $(target_lat)"

    return GeostationaryIntensiveSampling(
        info,
        [instrument],
        locarray,
        scenearray[radius_mask]
    )

end


text:
libPrefix() = @linux? "lib" : @osx? "lib" : ""

libPostfix() = @linux? ".so" : @osx? ".dylib" : ""

@generated libName{T<:Backend}(::Type{T}) =
	if is(T, CPU)
		:("afcpu")
	elseif is(T, OpenCL)
		:("afopencl")
	else
		:("afcuda")
	end

type AFLib{T<:Backend}
	ptr::Ptr{Void}
	err::Nullable{Exception}
	AFLib() =
		try
			ptr = Libdl.dlopen(string(libPrefix(), libName(T), libPostfix()))
			me = new(ptr, Nullable{Exception}())
			me
		catch err
			me = new(C_NULL, Nullable(err))
			me
		end
end

function getLibPtr{T<:Backend}(lib::AFLib{T})
	if isnull(lib.err)
		lib.ptr
	else
		throw(get(lib.err))
	end
end

type AFLibs
	CPU::AFLib{CPU}
	CUDA::AFLib{CUDA}
	OpenCL::AFLib{OpenCL}

	AFLibs() = new(AFLib{CPU}(), AFLib{CUDA}(), AFLib{OpenCL}())
end

global afLibs = AFLibs()

@generated getLibPtr{T<:Backend}(::Type{T}) =
	if is(T, CPU)
		:( getLibPtr(afLibs.CPU) )
	elseif is(T, OpenCL)
		:( getLibPtr(afLibs.OpenCL) )
	else
		:( getLibPtr(afLibs.CUDA) )
	end

max_stars_repo_name: unbornchikken/julia-ml-proto

max_issues_repo_name: unbornchikken/julia-ml-proto

max_forks_repo_name: unbornchikken/julia-ml-proto


text:
# Use baremodule to shave off a few KB from the serialized `.ji` file
baremodule Zlib_jll
using Base
using Base: UUID
import JLLWrappers

JLLWrappers.@generate_main_file_header("Zlib")
JLLWrappers.@generate_main_file("Zlib", UUID("83775a58-1f1d-513f-b197-d71354ab007a"))
end  # module Zlib_jll


text:
include("../imports.jl")

@testset ExtendedTestSet "lrfind" begin
    learner = testlearner()
    @test_nowarn result = lrfind(learner)

end


text:
# This file is a part of ArraysOfArrays.jl, licensed under the MIT License (MIT).

using ArraysOfArrays
using Test

using ElasticArrays
using UnsafeArrays

@testset "array_of_similar_arrays" begin
    function rand_flat_array(Val_N::Val{N}) where {N}
        sz_max = (2,3,2,4,5)
        sz = ntuple(i -> sz_max[i], Val_N)
        x = rand(sz...)
    end

    function rand_nested_similar_arrays(Val_M::Val{M}, Val_N::Val{N}) where {M,N}
        sz_max = (2,3,2,4,5)
        sz_inner = ntuple(i -> sz_max[i], Val_M)
        sz_outer = ntuple(i -> sz_max[i + M], Val_N)

        A = Array{Array{Float64, M}, N}(undef, sz_outer...)
        for i in eachindex(A)
            A[i] = rand(sz_inner...)
        end
        A
    end

    @inline function test_from_flat(::Type{TT}, ::Type{RT}, Val_L::Val{L}) where {
        TT<:ArrayOfSimilarArrays,
        RT<:ArrayOfSimilarArrays,
        L
    }
        @testset "$TT from Array{Float64,$L}" begin
            A = rand_flat_array(Val_L)
            @test typeof(@inferred TT(A)) == RT
            @test typeof(@inferred convert(TT, A)) == RT
        end
    end

    @inline function test_from_nested(::Type{TT}, ::Type{RT}, Val_M::Val{M}, Val_N::Val{N}) where {
        TT<:ArrayOfSimilarArrays,
        RT<:ArrayOfSimilarArrays,
        M, N
    }
        @testset "$TT from Array{Array{Float64,$M},$N}" begin
            A = rand_nested_similar_arrays(Val_M, Val_N)

            A2_ctor = @inferred TT(A)
            U = eltype(flatview(A2_ctor))

            A_U = Array{Array{U,M},N}(A)

            @test typeof(A2_ctor) == RT
            @test A2_ctor == A_U

            A2_conv = @inferred convert(TT, A)
            @test typeof(A2_conv) == RT
            @test A2_conv == A2_ctor

            U = eltype(flatview(A2_ctor))
            A3 = @inferred Array(A2_ctor)
            @test typeof(A3) == Array{Array{U,M},N}
            @test A3 == A_U
        end
    end

    @testset "construct/convert from flat array" begin
        test_from_flat(ArrayOfSimilarArrays{Float64,2,3}, ArrayOfSimilarArrays{Float64,2,3,5,Array{Float64,5}}, Val(5))
        test_from_flat(ArrayOfSimilarArrays{Float64,2}, ArrayOfSimilarArrays{Float64,2,3,5,Array{Float64,5}}, Val(5))
        test_from_flat(ArrayOfSimilarVectors{Float64}, ArrayOfSimilarVectors{Float64,2,3,Array{Float64,3}}, Val(3))
        test_from_flat(VectorOfSimilarArrays{Float64}, VectorOfSimilarArrays{Float64,2,3,Array{Float64,3}}, Val(3))
        test_from_flat(VectorOfSimilarVectors{Float64}, VectorOfSimilarVectors{Float64,Array{Float64,2}}, Val(2))
        test_from_flat(VectorOfSimilarVectors, VectorOfSimilarVectors{Float64,Array{Float64,2}}, Val(2))

        test_from_flat(ArrayOfSimilarArrays{Float32,2,3}, ArrayOfSimilarArrays{Float32,2,3,5,Array{Float32,5}}, Val(5))
        test_from_flat(ArrayOfSimilarArrays{Float32,2}, ArrayOfSimilarArrays{Float32,2,3,5,Array{Float32,5}}, Val(5))
        test_from_flat(ArrayOfSimilarVectors{Float32}, ArrayOfSimilarVectors{Float32,2,3,Array{Float32,3}}, Val(3))
        test_from_flat(VectorOfSimilarArrays{Float32}, VectorOfSimilarArrays{Float32,2,3,Array{Float32,3}}, Val(3))
        test_from_flat(VectorOfSimilarVectors{Float32}, VectorOfSimilarVectors{Float32,Array{Float32,2}}, Val(2))
        test_from_flat(VectorOfSimilarVectors{Float32}, VectorOfSimilarVectors{Float32,Array{Float32,2}}, Val(2))
    end

    @testset "construct/convert from nested arrays" begin
        test_from_nested(ArrayOfSimilarArrays, ArrayOfSimilarArrays{Float64,2,3,5,Array{Float64,5}}, Val(2), Val(3))
        test_from_nested(ArrayOfSimilarArrays{Float64,2,3}, ArrayOfSimilarArrays{Float64,2,3,5,Array{Float64,5}}, Val(2), Val(3))
        test_from_nested(ArrayOfSimilarArrays{Float64}, ArrayOfSimilarArrays{Float64,2,3,5,Array{Float64,5}}, Val(2), Val(3))
        test_from_nested(ArrayOfSimilarArrays{Float32}, ArrayOfSimilarArrays{Float32,2,3,5,Array{Float32,5}}, Val(2), Val(3))

        test_from_nested(ArrayOfSimilarArrays, VectorOfSimilarArrays{Float64,4,5,Array{Float64,5}}, Val(4), Val(1))
        test_from_nested(ArrayOfSimilarArrays, ArrayOfSimilarVectors{Float64,4,5,Array{Float64,5}}, Val(1), Val(4))
        test_from_nested(ArrayOfSimilarArrays, VectorOfSimilarVectors{Float64,Array{Float64,2}}, Val(1), Val(1))

        test_from_nested(VectorOfSimilarArrays{Float64,4}, VectorOfSimilarArrays{Float64,4,5,Array{Float64,5}}, Val(4), Val(1))
        test_from_nested(VectorOfSimilarArrays{Float64}, VectorOfSimilarArrays{Float64,4,5,Array{Float64,5}}, Val(4), Val(1))
        test_from_nested(VectorOfSimilarArrays{Float32}, VectorOfSimilarArrays{Float32,4,5,Array{Float32,5}}, Val(4), Val(1))
        test_from_nested(VectorOfSimilarArrays, VectorOfSimilarArrays{Float64,4,5,Array{Float64,5}}, Val(4), Val(1))

        test_from_nested(ArrayOfSimilarVectors{Float64,4}, ArrayOfSimilarVectors{Float64,4,5,Array{Float64,5}}, Val(1), Val(4))
        test_from_nested(ArrayOfSimilarVectors{Float64}, ArrayOfSimilarVectors{Float64,4,5,Array{Float64,5}}, Val(1), Val(4))
        test_from_nested(ArrayOfSimilarVectors{Float32}, ArrayOfSimilarVectors{Float32,4,5,Array{Float32,5}}, Val(1), Val(4))
        test_from_nested(ArrayOfSimilarVectors, ArrayOfSimilarVectors{Float64,4,5,Array{Float64,5}}, Val(1), Val(4))

        test_from_nested(VectorOfSimilarVectors{Float64}, VectorOfSimilarVectors{Float64,Array{Float64,2}}, Val(1), Val(1))
        test_from_nested(VectorOfSimilarVectors{Float32}, VectorOfSimilarVectors{Float32,Array{Float32,2}}, Val(1), Val(1))
        test_from_nested(VectorOfSimilarVectors, VectorOfSimilarVectors{Float64,Array{Float64,2}}, Val(1), Val(1))
    end

    @testset "similar and copyto!" begin
        A = ArrayOfSimilarArrays{Float64,1}(rand_flat_array(Val(1)))
        @test (@inferred copyto!((@inferred similar(A)), A)) == A

        A = ArrayOfSimilarArrays{Float64,2}(rand_flat_array(Val(5)))
        @test (@inferred copyto!((@inferred similar(A)), A)) == A
    end

    @testset "flatview" begin
        A = rand_nested_similar_arrays(Val(3), Val(2))
        B = ArrayOfSimilarArrays(A)
        @inferred(flatview(B))[:] == collect(flatview(A))
    end

    @testset "deepgetindex" begin
        A = rand_nested_similar_arrays(Val(3), Val(2))
        B = ArrayOfSimilarArrays(A)

        @test deepgetindex(A, 3, 4, 2, 1, 2) == @inferred deepgetindex(B, 3, 4, 2, 1, 2)
        @test deepgetindex(A, 2:3, 4, 2, 1, 2) == @inferred deepgetindex(B, 2:3, 4, 2, 1, 2)
        @test deepgetindex(A, 2:3, 2:4, 2, 1, 2) == @inferred deepgetindex(B, 2:3, 2:4, 2, 1, 2)
        @test deepgetindex(A, 2, 4, :, 1, 2) == @inferred deepgetindex(B, 2, 4, :, 1, 2)
        @test deepgetindex(A, 2, 4, :, 1, 1:2) == @inferred deepgetindex(B, 2, 4, :, 1, 1:2)
        @test deepgetindex(A, 2:3, 4, :, 1, 2) == @inferred deepgetindex(B, 2:3, 4, :, 1, 2)
        @test deepgetindex(A, 2:3, 4, :, 1, 1:2) == @inferred deepgetindex(B, 2:3, 4, :, 1, 1:2)
    end

    @testset "deepsetindex!" begin
        function testdata()
            A = rand_nested_similar_arrays(Val(3), Val(2))
            B = ArrayOfSimilarArrays(A)
            A, B
        end

        A, B = testdata()
        @test deepsetindex!(A, 42, 3, 4, 2, 1, 2) == @inferred deepsetindex!(B, 42, 3, 4, 2, 1, 2)
        @test deepgetindex(B, 3, 4, 2, 1, 2) == 42

        A, B = testdata()
        X1 = rand(2)
        @test deepsetindex!(A, X1, 2:3, 4, 2, 1, 2) == @inferred deepsetindex!(B, X1, 2:3, 4, 2, 1, 2)
        @test deepgetindex(B, 2:3, 4, 2, 1, 2) == X1

        A, B = testdata()
        X2 = rand(2,2)
        @test deepsetindex!(A, X2, 2, 4, :, 1, 1:2) == @inferred deepsetindex!(B, X2, 2, 4, :, 1, 1:2)
        @test deepgetindex(B, 2, 4, :, 1, 1:2) == X2

        A, B = testdata()
        X3 = [rand(2,2), rand(2,2)]
        @test deepsetindex!(A, X3, 2:3, 4, :, 1, 1:2) == @inferred deepsetindex!(B, X3, 2:3, 4, :, 1, 1:2)
        @test deepgetindex(B, 2:3, 4, :, 1, 1:2) == X3
    end

    @testset "deepview" begin
        A = rand_nested_similar_arrays(Val(3), Val(2))
        B = ArrayOfSimilarArrays(A)

        @test deepview(A, 3, 4, 2, 1, 2) == @inferred deepview(B, 3, 4, 2, 1, 2)
        @test deepgetindex(A, 2:3, 4, 2, 1, 2) == @inferred deepview(B, 2:3, 4, 2, 1, 2)
        @test deepgetindex(A, 2:3, 2:4, 2, 1, 2) == @inferred deepview(B, 2:3, 2:4, 2, 1, 2)
        @test deepview(A, 2, 4, :, 1, 2) == @inferred deepview(B, 2, 4, :, 1, 2)
        @test deepview(A, 2, 4, :, 1, 1:2) == @inferred deepview(B, 2, 4, :, 1, 1:2)
        @test deepview(A, 2:3, 4, :, 1, 2) == @inferred deepview(B, 2:3, 4, :, 1, 2)
        @test deepview(A, 2:3, 4, :, 1, 1:2) == @inferred deepview(B, 2:3, 4, :, 1, 1:2)
    end

    @testset "examples" begin
        A_flat = rand(2,3,4,5,6)
        A_nested = nestedview(A_flat, 2)
        @test A_nested isa AbstractArray{<:AbstractArray{T,2},3} where T
        @test flatview(A_nested) === A_flat

        # -------------------------------------------------------------------

        A_nested = nestedview(ElasticArray{Float64}(undef, 2, 3, 0), 2)

        for i in 1:4
            push!(A_nested, rand(2, 3))
        end
        @test size(flatview(A_nested)) == (2, 3, 4)

        resize!(A_nested, 6)
        @test size(flatview(A_nested)) == (2, 3, 6)

    end
end


text:
VERSION >= v"0.4.0-dev+6521" && __precompile__()

module Element
using Compat

export Atom, Molecule, dft, dft!, lingrid, loggrid, lebgrid, atomicradgrid, atomicgrid,
	AtomicGrid, AtomicRadialGrid, AngularGrid, makebasis!, makegrid!, eachatom, NAO,
	MultipoleFunc, MultipoleFuncSpl, RadialFunc, MultipoleBasis, integrate, ∫, grad, ∇,
	mat, expandmultipole, Point3, Point3f, Point2, dist, randp, rotmat, randrotmat,
	rotate!, setposition, simple_logger, bondslice, print_stats

# Load libxc from BinDeps
const depfile = joinpath(dirname(@__FILE__), "..", "deps", "deps.jl")
if isfile(depfile)
    include(depfile)
else
    error("libxc not properly installed. Please run Pkg.build(\"Element\")")
end

include("base.jl")
include("multistep.jl")
include("fzero.jl")
include("findsteps.jl")
include("point.jl")
include("func.jl")
include("spline.jl")
include("sphharm.jl")
include("sphharmtable.jl")
include("stats.jl")
# include("raggedarray.jl")
include("lebedev.jl")
include("coord.jl")  		# point, func
include("grid.jl")  		# point
include("atomicgrid.jl")
include("radialfunc.jl")  	# point, func
include("multipole.jl")  	# point, func, grid
include("coulomb.jl")
include("basis.jl")  		# point, func
include("eig.jl")
include("radschrod.jl")
include("radpoisson.jl")
include("xc.jl")
include("mixer.jl")
include("scf.jl")
include("atomicdata.jl")
include("atom.jl")
include("atomicdft.jl")
include("nao.jl")
include("naobasis.jl")
include("partition.jl")
include("multigrid.jl")
include("molecule.jl")
include("hamiltonian.jl")
include("poissonmultipole.jl")
include("dft.jl")
include("rotation.jl")
# include("ml.jl")

function __init__()
	filename = joinpath(Pkg.dir("Element"), "deps/lebedev.jls")
	if isfile(filename)
		global lebedev = open(filename) do io deserialize(io) end
	end
end

end  # module Element

max_stars_repo_name: johncsnyder/Element


text:
include("sampling.jl")
function bootstrap!(
    items::Vector{Item},
    examinees::Vector{Examinee},
    responses::Vector{Response};
    method = "mmle", #"polyagamma"
    replications = 100,
    type = "parametric",
    sample_fraction = 0.9,
    design = "incomplete", #"complete"
    rescale_latent = false,
    kwargs...
)

    starting_pars = get_parameters_vals(items)
    I = size(starting_pars, 1)
    n_pars = size(starting_pars, 2)

    starting_latents = get_latents_vals(examinees)
    N = size(starting_latents, 2)
    n_latents = size(starting_latents, 1)
    chain = Vector{Vector{Vector{Float64}}}(undef, replications)
    n_sample = Int64[]
    chain = Distributed.@sync Distributed.@distributed (vcat) for r = 1 : replications
        println("Replication: ", r)
        if type == "nonparametric"
            n_responses_sampled_per_item = zeros(I)
            while minimum(n_responses_sampled_per_item) < 1
                n_sample = non_parametric_sample(N, sample_fraction)
                #loop until all items have at least a response
                examinees_sampled = examinees[n_sample]
                responses_sampled = vcat(map( e -> get_responses_by_examinee_id(e.id, responses), examinees_sampled)...)
                n_responses_sampled_per_item = map( i -> size(get_responses_by_item_id(i.id, responses_sampled), 1), items)
            end
        elseif type == "parametric" && method == "mmle"
            #discrete ability distribution
            dist = examinees[1].latent.prior
            W = dist.p
            X = dist.support
            K = size(W, 1)
            #takes only the first Latent
            (bins, nothing) = cutR(
                starting_latents[1, :];
                start = minimum(X),
                stop = maximum(X),
                n_bins = K,
                return_breaks = false,
                return_mid_points = true,
            )
            n_responses_sampled_per_item = zeros(I)
            while minimum(n_responses_sampled_per_item) < 1
                n_sample = parametric_sample(N, sample_fraction, bins, dist)
                #loop until all items have at least a response
                examinees_sampled = examinees[n_sample]
                responses_sampled = vcat(map( e -> get_responses_by_examinee_id(e.id, responses), examinees_sampled)...)
                n_responses_sampled_per_item = map( i -> size(get_responses_by_item_id(i.id, responses_sampled), 1), items)
            end
        else
            error("Bootstrap type can be \"parametric\" (only mmle) or \"nonparametric\".")
        end
        println("Number of examinees sample in replication ",r," : ", size(n_sample, 1))
        #must change examinees idx and examinee_idx ow get_response_matrix does not work well
        examinees_r = Examinee[]
        responses_r = Response[]
        for n = 1 : size(n_sample, 1)
            n_main = n_sample[n]
            examinee_main = examinees[n_main]
            examinee = Examinee(n, examinee_main.id, examinee_main.latent)
            examinee.latent.assessed =  false
            push!(examinees_r, examinee)
            responses_e = get_responses_by_examinee_id(examinee_main.id, responses)
            for r in responses_e
                push!(responses_r, Response(r.item_idx, n, r.item_id, r.examinee_id, r.val, r.start_time, r.end_time))
            end
        end
        items_r = map(i -> i, items)
        n_not_sampled = setdiff(collect(1:N), n_sample)
        if method == "mmle"
            joint_estimate_mmle!(items_r, examinees_r, responses_r; rescale_latent = rescale_latent, kwargs...)
        elseif method == "polyagamma"
            joint_estimate_pg!(items_r, examinees_r, responses_r; kwargs...)
        else
            error("Estimation method can be \"mmle\" or \"polyagamma\".")
        end
        [map(i_r -> get_parameters_vals(i_r), items_r)]
    end
    map(i -> items[i].parameters.chain = map(c_r -> c_r[i], chain), 1 : I)
    return items::Vector{Item}, examinees::Vector{Examinee}
end