text:
### Input widgets

"""
    init_wsigval([T], signal, value; default=nothing) -> signal, value

Return a suitable initial state for `signal` and `value` for a
widget. Any but one of these argument can be `nothing`. A new `signal`
will be created if the input `signal` is `nothing`. Passing in a
pre-existing `signal` will return the same signal, either setting the
signal 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 `signal` is a
`Reactive.Signal`, then `T` must agree with `eltype(signal)`.
"""
init_wsigval(::Nothing, ::Nothing; default=nothing) = _init_wsigval(nothing, default)
init_wsigval(::Nothing, value; default=nothing) = _init_wsigval(typeof(value), nothing, value)
init_wsigval(signal, value; default=nothing) = _init_wsigval(eltype(signal), signal, value)
init_wsigval(::Type{T}, ::Nothing, ::Nothing; default=nothing) where {T} =
    _init_wsigval(T, nothing, default)
init_wsigval(::Type{T}, signal, value; default=nothing) where {T} =
    _init_wsigval(T, signal, value)

_init_wsigval(::Nothing, value) = _init_wsigval(typeof(value), nothing, value)
_init_wsigval(::Type{T}, ::Nothing, ::Nothing) where {T} = error("must supply an initial value")
_init_wsigval(::Type{T}, ::Nothing, value) where {T} = Signal(T, value), value
_init_wsigval(::Type{T}, signal::Signal{T}, ::Nothing) where {T} =
    _init_wsigval(T, signal, value(signal))
function _init_wsigval(::Type{T}, signal::Signal{T}, value) where T
    push!(signal, value)
    signal, value
end

"""
    init_signal2widget(widget::GtkWidget, id, signal) -> updatesignal
    init_signal2widget(getter, setter, widget::GtkWidget, id, signal) -> updatesignal

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

If `updatesignal` is garbage-collected, the widget will no longer
update. Most likely you should either `preserve` or store
`updatesignal`.
"""
function init_signal2widget(getter::Function,
                            setter!::Function,
                            widget::GtkWidget,
                            id, signal)
    map(signal) do val
        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
init_signal2widget(widget::GtkWidget, id, signal) =
    init_signal2widget(defaultgetter, defaultsetter!, widget, id, signal)

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
        map(close, preserved)
        empty!(preserved)
        # but it's too dangerous to close signal itself
    end
    nothing
end

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

struct Slider{T<:Number} <: InputWidget{T}
    signal::Signal{T}
    widget::GtkScaleLeaf
    id::Culong
    preserved::Vector

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

# differs from median(r) in that it always returns an element of the range
medianidx(r) = (1+length(r))>>1
medianelement(r::AbstractRange) = r[medianidx(r)]

slider(signal::Signal, widget::GtkScaleLeaf, id, preserved = []) =
    Slider(signal, widget, id, preserved)

"""
    slider(range; widget=nothing, value=nothing, signal=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 (Reactive.jl) `signal` coupled to this slider (by default, creates a new signal)
  - the `orientation` of the slider.
"""
function slider(range::AbstractRange{T};
                widget=nothing,
                value=nothing,
                signal=nothing,
                orientation="horizontal",
                syncsig=true,
                own=nothing) where T
    signalin = signal
    signal, value = init_wsigval(T, signal, value; default=medianelement(range))
    if own == nothing
        own = signal != signalin
    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 -> signal
    id = signal_connect(widget, :value_changed) do w
        push!(signal, defaultgetter(w))
    end

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

    Slider(signal, widget, id, preserved)
end

# Adjust the range on a slider
# Is calling this `push!` too much of a pun?
function Base.push!(s::Slider, range::AbstractRange, value=value(s))
    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

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

struct Checkbox <: InputWidget{Bool}
    signal::Signal{Bool}
    widget::GtkCheckButtonLeaf
    id::Culong
    preserved::Vector

    function (::Type{Checkbox})(signal::Signal{Bool}, widget, id, preserved)
        obj = new(signal, widget, id, preserved)
        gc_preserve(widget, obj)
        obj
    end
end

checkbox(signal::Signal, widget::GtkCheckButtonLeaf, id, preserved=[]) =
    Checkbox(signal, widget, id, preserved)

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

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

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

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

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

struct ToggleButton <: InputWidget{Bool}
    signal::Signal{Bool}
    widget::GtkToggleButtonLeaf
    id::Culong
    preserved::Vector

    function (::Type{ToggleButton})(signal::Signal{Bool}, widget, id, preserved)
        obj = new(signal, widget, id, preserved)
        gc_preserve(widget, obj)
        obj
    end
end

togglebutton(signal::Signal, widget::GtkToggleButtonLeaf, id, preserved=[]) =
    ToggleButton(signal, widget, id, preserved)

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

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

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

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

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

struct Button <: InputWidget{Nothing}
    signal::Signal{Nothing}
    widget::Union{GtkButtonLeaf,GtkToolButtonLeaf}
    id::Culong

    function (::Type{Button})(signal::Signal{Nothing}, widget, id)
        obj = new(signal, widget, id)
        gc_preserve(widget, obj)
        obj
    end
end

button(signal::Signal, widget::Union{GtkButtonLeaf,GtkToolButtonLeaf}, id) =
    Button(signal, widget, id)

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

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

    id = signal_connect(widget, :clicked) do w
        push!(signal, nothing)
    end

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

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

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

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

textbox(signal::Signal, widget::GtkButtonLeaf, id, preserved = []) =
    Textbox(signal, widget, id, preserved)

"""
    textbox(value=""; widget=nothing, signal=nothing, range=nothing, gtksignal=:activate)
    textbox(T::Type; widget=nothing, signal=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 (Reactive.jl) `signal` coupled
to this text box. Finally, you can specify which Gtk signal (e.g.
`activate`, `changed`) you'd like the widget to update with.
"""
function textbox(::Type{T};
                 widget=nothing,
                 value=nothing,
                 range=nothing,
                 signal=nothing,
                 syncsig=true,
                 own=nothing,
                 gtksignal=:activate) where T
    if T <: AbstractString && range != nothing
        throw(ArgumentError("You cannot set a range on a string textbox"))
    end
    signalin = signal
    signal, value = init_wsigval(T, signal, value; default="")
    if own == nothing
        own = signal != signalin
    end
    if widget == nothing
        widget = GtkEntry()
    end
    set_gtk_property!(widget, :text, value)

    id = signal_connect(widget, gtksignal) do w
        push!(signal, entrygetter(w, signal, 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_signal2widget(w->entrygetter(w, signal, range),
                                            range == nothing ? entrysetter! : checked_entrysetter!,
                                            widget, id, signal))
    end
    own && ondestroy(widget, preserved)

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

entrygetter(w, signal::Signal{T}, ::Nothing) where {T<:AbstractString} =
    get_gtk_property(w, :text, String)
function entrygetter(w, signal::Signal{T}, range) where T
    val = tryparse(T, get_gtk_property(w, :text, String))
    if val == nothing
        nval = value(signal)
        # 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
    r[clamp(i, 1, length(r))]
end

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

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

struct Textarea <: InputWidget{String}
    signal::Signal{String}
    widget::GtkTextView
    id::Culong
    preserved::Vector

    function (::Type{Textarea})(signal::Signal{String}, widget, id, preserved)
        obj = new(signal, widget, id, preserved)
        gc_preserve(widget, obj)
        obj
    end
end

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

Creates an extended text-entry area. Optionally provide a GtkTextView `widget`
and/or the (Reactive.jl) `signal` associated with this widget. The
`signal` updates when you type.
"""
function textarea(value::String="";
                  widget=nothing,
                  signal=nothing,
                  syncsig=true,
                  own=nothing)
    signalin = signal
    signal, value = init_wsigval(signal, value)
    if own == nothing
        own = signal != signalin
    end
    if widget == nothing
        widget = GtkTextView()
    end
    buf = Gtk.G_.buffer(widget)
    set_gtk_property!(buf, :text, value)

    id = signal_connect(buf, :changed) do w
        push!(signal, get_gtk_property(w, :text, String))
    end

    preserved = []
    if syncsig
        # GtkTextBuffer is not a GtkWdiget, so we have to do this manually
        push!(preserved, map(signal) 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(signal, widget, id, preserved)
end

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

struct Dropdown <: InputWidget{String}
    signal::Signal{String}
    mappedsignal::Signal
    widget::GtkComboBoxTextLeaf
    id::Culong
    preserved::Vector

    function (::Type{Dropdown})(signal::Signal{String}, mappedsignal::Signal, widget, id, preserved)
        obj = new(signal, mappedsignal, widget, id, preserved)
        gc_preserve(widget, obj)
        obj
    end
end

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

Create a "dropdown" widget. `choices` can be a vector (or other iterable) of
options. Optionally specify
  - the GtkComboBoxText `widget` (by default, creates a new one)
  - the starting `value`
  - the (Reactive.jl) `signal` coupled to this slider (by default, creates a new signal)
  - whether the widget should allow text entry

# Examples

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

To link a callback to the dropdown, use

    f = dropdown(("turn red"=>colorize_red, "turn green"=>colorize_green))
    map(g->g(image), f.mappedsignal)
"""
function dropdown(; choices=nothing,
                  widget=nothing,
                  value=juststring(first(choices)),
                  signal=nothing,
                  label="",
                  with_entry=true,
                  icons=nothing,
                  tooltips=nothing,
                  own=nothing)
    signalin = signal
    signal, value = init_wsigval(String, signal, value)
    if own == nothing
        own = signal != signalin
    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!(widget, :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
        push!(signal, getactive(w))
    end

    preserved = []
    push!(preserved, init_signal2widget(getactive, setactive!, widget, id, signal))
    if !allstrings
        choicedict = Dict(choices...)
        mappedsignal = map(val->choicedict[val], signal; typ=Any)
    else
        mappedsignal = Signal(nothing)
    end
    if own
        ondestroy(widget, preserved)
    end

    Dropdown(signal, mappedsignal, widget, id, preserved)
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)
#         #signal needs to be of an array of values, not just a single value
#         signal = Signal(collect(values(options))[1:1])
#         Options(:SelectMultiple, options; signal=signal, 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 signal 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
# signal 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
    signal::Signal{String}
    widget::GtkLabel
    preserved::Vector{Any}

    function (::Type{Label})(signal::Signal{String}, widget, preserved)
        obj = new(signal, widget, preserved)
        gc_preserve(widget, obj)
        obj
    end
end

"""
    label(value; widget=nothing, signal=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 (Reactive.jl) `signal` coupled to this label (by default, creates a new signal)
"""
function label(value;
               widget=nothing,
               signal=nothing,
               syncsig=true,
               own=nothing)
    signalin = signal
    signal, value = init_wsigval(String, signal, value)
    if own == nothing
        own = signal != signalin
    end
    if widget == nothing
        widget = GtkLabel(value)
    else
        set_gtk_property!(widget, :label, value)
    end
    preserved = []
    if syncsig
        push!(preserved, map(signal) do val
            set_gtk_property!(widget, :label, val)
        end)
    end
    if own
        ondestroy(widget, preserved)
    end
    Label(signal, 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::Signal, 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 `Signal`, then updates to that signal will be reflected in
# widget instances/views.

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

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

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

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

struct SpinButton{T<:Number} <: InputWidget{T}
    signal::Signal{T}
    widget::GtkSpinButtonLeaf
    id::Culong
    preserved::Vector

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

spinbutton(signal::Signal, widget::GtkSpinButtonLeaf, id, preserved = []) =
    SpinButton(signal, widget, id, preserved)

"""
    spinbutton(range; widget=nothing, value=nothing, signal=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 (Reactive.jl) `signal` coupled to this spinbutton (by default, creates a new signal)
  - the `orientation` of the spinbutton.
"""
function spinbutton(range::AbstractRange{T};
                    widget=nothing,
                    value=nothing,
                    signal=nothing,
                    orientation="horizontal",
                    syncsig=true,
                    own=nothing) where T
    signalin = signal
    signal, value = init_wsigval(T, signal, value; default=range.start)
    if own == nothing
        own = signal != signalin
    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 -> signal
    id = signal_connect(widget, :value_changed) do w
        push!(signal, defaultgetter(w))
    end

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

    SpinButton(signal, widget, id, preserved)
end

# Adjust the range on a spinbutton
# Is calling this `push!` too much of a pun?
function Base.push!(s::SpinButton, range::AbstractRange, value=value(s))
    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

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

struct CyclicSpinButton{T<:Number} <: InputWidget{T}
    signal::Signal{T}
    widget::GtkSpinButtonLeaf
    id::Culong
    preserved::Vector

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

cyclicspinbutton(signal::Signal, widget::GtkSpinButtonLeaf, id, preserved = []) =
    CyclicSpinButton(signal, widget, id, preserved)

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

Create a cyclicspinbutton widget with the specified `range` that updates a `carry_up::Signal{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 (Reactive.jl) `signal` coupled to this cyclicspinbutton (by default, creates a new signal)
  - the `orientation` of the cyclicspinbutton.
"""
function cyclicspinbutton(range::AbstractRange{T}, carry_up::Signal{Bool};
                       widget=nothing,
                       value=nothing,
                       signal=nothing,
                       orientation="horizontal",
                       syncsig=true,
                       own=nothing) where T
    signalin = signal
    signal, value = init_wsigval(T, signal, value; default=range.start)
    if own == nothing
        own = signal != signalin
    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 -> signal
    id = signal_connect(widget, :value_changed) do w
        push!(signal, defaultgetter(w))
    end

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

    up = filter(x -> x > last(range), value, signal)
    foreach(up; init=nothing) do _
        push!(signal, first(range))
        push!(carry_up, true)
    end
    down = filter(x -> x < first(range), value, signal)
    foreach(down; init=nothing) do _
        push!(signal, last(range))
        push!(carry_up, false)
    end
    push!(signal, value)

    CyclicSpinButton(signal, widget, id, preserved)
end

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

struct ProgressBar{T <: Number} <: Widget
    signal::Signal{T}
    widget::GtkProgressBarLeaf
    preserved::Vector{Any}

    function ProgressBar{T}(signal::Signal{T}, widget, preserved) where T
        obj = new{T}(signal, widget, preserved)
        gc_preserve(widget, obj)
        obj
    end
end
ProgressBar(signal::Signal{T}, widget::GtkProgressBarLeaf, preserved) where {T} =
    ProgressBar{T}(signal, 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, signal=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 (Reactive.jl) `signal` coupled to this progressbar (by default, creates a new signal)

# Examples

```julia-repl
julia> using GtkReactive

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,
               signal=nothing,
               syncsig=true,
               own=nothing) where T<:Number
    value = minimum(interval)
    signalin = signal
    signal, value = init_wsigval(T, signal, value)
    if own == nothing
        own = signal != signalin
    end
    if widget == nothing
        widget = GtkProgressBar()
    else
        set_gtk_property!(widget, :fraction, interval2fraction(interval, value))
    end
    preserved = []
    if syncsig
        push!(preserved, map(signal) do val
            set_gtk_property!(widget, :fraction, interval2fraction(interval, val))
        end)
    end
    if own
        ondestroy(widget, preserved)
    end
    ProgressBar(signal, widget, preserved)
end

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


text:
abstract type LossFunction end

struct HuberLoss <: LossFunction
    δ::Float64
    c_ptr::Ptr{Cvoid}
end

function HuberLoss(δ::Float64)
    c_ptr = ceres_create_huber_loss_c(δ)
    HuberLoss(δ, c_ptr)
end

struct EmptyLoss <: LossFunction end

ext: jl


text:
using Test
using PolynomialBasis
using Roots
using IntervalArithmetic
using Revise
using ImplicitDomainQuadrature

IDQ = ImplicitDomainQuadrature

function circle_distance_function(coords, center, radius)
    difference = (coords .- center).^2
    distance = radius .- mapslices(sum,difference,dims=1)'
    return distance
end

numqp = 4
polyorder = 2
poly = InterpolatingPolynomial(1,2,polyorder)

center = [3.,0.5]
radius = 1.5
coords = [0.0  0.0  0.0  1.5  1.5  1.5  3.0  3.0  3.0
          0.0  0.5  1.0  0.0  0.5  1.0  0.0  0.5  1.0]
coeffs = circle_distance_function(coords,center,radius)
update!(poly,coeffs)

interpgrad = IDQ.interpolating_gradient(poly)

grad(x) = gradient(poly,x)

box = IntervalBox(-1..1,2)

# quad1d = ReferenceQuadratureRule(numqp)
# pquad = area_quadrature(poly,+1,box,quad1d)

max_stars_repo_name: ArjunNarayanan/ImplicitDomainQuadrature

max_issues_repo_name: ArjunNarayanan/ImplicitDomainQuadrature

max_forks_repo_name: ArjunNarayanan/ImplicitDomainQuadrature


text:
# Superselection sectors (quantum numbers):
# for defining graded vector spaces and invariant subspaces of tensor products
#==============================================================================#
"""
    abstract type Sector end

Abstract type for representing the label sets of graded vector spaces, which should
correspond to (unitary) fusion categories.

Every new `G<:Sector` should implement the following methods:
*   `one(::Type{G})` -> unit element of `G`
*   `conj(a::G)` -> a̅: conjugate or dual label of a
*   `⊗(a::G, b::G)` -> iterable with fusion outputs of `a ⊗ b`
*   `Nsymbol(a::G, b::G, c::G)` -> number of times `c` appears in `a ⊗ b`
*   `fusiontype(::Type{G})` -> `Abelian`, `SimpleNonAbelian` or `DegenerateNonAbelian`
*   `braidingtype(::Type{G})` -> `Bosonic`, `Fermionic`, `Anyonic`, ...
and, if `fusiontype(G) <: NonAbelian`,
*   `Fsymbol(a::G, b::G, c::G, d::G, e::G, f::G)` -> F-symbol: scalar (in case of `SimpleNonAbelian`) or matrix (in case of `DegenerateNonAbelian`)
*   ... can all other information (quantum dimension, cups and caps) be extracted from `F`?
and if `braidingtype(G) == Fermionic`
*   `fermionparity(a::G)` -> `Bool` representing the fermion parity of sector `a`
and optionally, if if `fusiontype(G) == DegenerateNonAbelian`
*   `vertex_ind2label(i::Int, a::G, b::G, c::G)` -> a custom label for the `i`th copy of `c` appearing in `a ⊗ b`
"""
abstract type Sector end

# Define a sector for ungraded vector spaces
struct Trivial <: Sector
end
Base.show(io::IO, ::Trivial) = print(io, "I")

"""
    function one(::Sector) -> Sector
    function one(::Type{<:Sector}) -> Sector

Returns the unit element within this type of sector
"""
Base.one(a::Sector) = one(typeof(a))
Base.one(::Type{Trivial}) = Trivial()

"""
    function dual(a::Sector) -> Sector

Returns the conjugate label `conj(a)`
"""
dual(a::Sector) = conj(a)
dual(::Trivial) = Trivial()

# Fusion: the most important aspect of Sector
#---------------------------------------------
"""
    function ⊗(a::G, b::G) where {G<:Sector}

Returns an iterable of elements of `c::G` that appear in the fusion product `a ⊗ b`.
Note that every element `c` should appear at most once, fusion degeneracies (if
`fusiontype(G) == DegenerateNonAbelian`) should be accessed via `Nsymbol(a,b,c)`.
"""
⊗(::Trivial, ::Trivial) = (Trivial(),)

"""
    function Nsymbol(a::G, b::G, c::G) where {G<:Sector} -> Integer

Returns an `Integer` representing the number of times `c` appears in the fusion
product `a ⊗ b`. Could be a `Bool` if `fusiontype(G) = Abelian` or `SimpleNonAbelian`.
"""
Nsymbol(::Trivial, ::Trivial, ::Trivial) = true

# trait to describe the fusion of superselection sectors
abstract type Fusion end
struct Abelian <: Fusion
end
abstract type NonAbelian <: Fusion end
struct SimpleNonAbelian <: NonAbelian # non-abelian fusion but multiplicity free
end
struct DegenerateNonAbelian <: NonAbelian # non-abelian fusion with multiplicities
end

"""
    function fusiontype(G::Type{<:Sector}) -> Type{<:Fusion}

Returns the type of fusion behavior of sectors of type G, which can be either
*   `Abelian`: single fusion output when fusing two sectors;
*   `SimpleNonAbelian`: multiple outputs, but every output occurs at most one, also known as multiplicity free (e.g. irreps of SU(2));
*   `DegenerateNonAbelian`: multiple outputs that can occur more than once (e.g. irreps of SU(3)).
"""
fusiontype(::Type{Trivial}) = Abelian
fusiontype(a::Sector) = fusiontype(typeof(a))

function ⊗(a::G, b::G, c::G, rest::Vararg{G}) where {G<:Sector}
    if fusiontype(G) == Abelian
        return a ⊗ first(⊗(b, c, rest...))
    else
        s = Set{G}()
        for d in ⊗(b, c, rest...)
            for e in a ⊗ d
                push!(s, e)
            end
        end
        return s
    end
end

"""
    function Fsymbol(a::G, b::G, c::G, d::G, e::G, f::G) where {G<:Sector}

Returns the F-symbol F^{a,b,c}_d that associates the two different fusion orders
of sectors `a`, `b` and `c` into an ouput sector `d`, using either an intermediate
sector `a ⊗ b → e` or `b ⊗ c → f`:
```
a-<-μ-<-e-<-ν-<-d                                     a-<-λ-<-d
    ∨       ∨       -> Fsymbol(a,b,c,d,e,f)[μ,ν,κ,λ]      ∨
    b       c                                         b-<-κ
                                                          ∨
                                                          c
```
If `fusiontype(G)` is `Abelian` or `SimpleNonAbelian`, the F-symbol is a number.
Otherwise it is a rank 4 array of size `(Nsymbol(a,b,e), Nsymbol(e,c,d), Nsymbol(b,c,f), Nsymbol(a,f,d))`.
"""
function Fsymbol end
Fsymbol(::Trivial, ::Trivial, ::Trivial, ::Trivial, ::Trivial, ::Trivial) = 1

"""
    function Rsymbol(a::G, b::G, c::G) where {G<:Sector}

Returns the R-symbol R^{c}_{a,b} that maps between `a ⊗ b → c` and `b ⊗ a → c` as in
```
a -<-μ-<- c                                 b -<-ν-<- c
     ∨          -> Rsymbol(a,b,c)[μ,ν]           ∧
     b                                           a
```
If `fusiontype(G)` is `Abelian` or `SimpleNonAbelian`, the R-symbol is a number.
Otherwise it is a square matrix with row and column size `Nsymbol(a,b,c) == Nsymbol(b,a,c)`.
"""
function Rsymbol end
Rsymbol(::Trivial, ::Trivial, ::Trivial) = 1

# If a G::Sector with `fusion(G) == DegenerateNonAbelian` fusion wants to have custom vertex
# labels, a specialized method for `vertindex2label` should be added
"""
    function vertex_ind2label(i::Int, a::G, b::G, c::G) where {G<:Sector}

Convert the index i of the fusion vertex (a,b)->c into a label. For `fusiontype(G)==Abelian`
or `fusiontype(G)==NonAbelian`, where every fusion output occurs only once and `i===1`, the
default is to suppress vertex labels by setting them equal to `nothing`. For
`fusiontype(G)==DegenerateNonAbelian`, the default is to just use `i`, unless a specialized
method is provided.
"""
vertex_ind2label(i::Int, s1::G, s2::G, sout::G) where {G<:Sector}= _ind2label(fusiontype(G), i::Int, s1::G, s2::G, sout::G)
_ind2label(::Type{Abelian}, i, s1, s2, sout) = nothing
_ind2label(::Type{SimpleNonAbelian}, i, s1, s2, sout) = nothing
_ind2label(::Type{DegenerateNonAbelian}, i, s1, s2, sout) = i

"""
    function vertex_labeltype(G::Type{<:Sector}) -> Type

Returns the type of labels for the fusion vertices of sectors of type `G`.
"""
Base.@pure vertex_labeltype(G::Type{<:Sector}) = typeof(vertex_ind2label(1, one(G), one(G), one(G)))

# combine fusion properties of tensor products of sectors
Base.:&(::Type{F},::Type{F}) where {F<:Fusion} = F
Base.:&(F1::Type{<:Fusion},F2::Type{<:Fusion}) = F2 & F1

Base.:&(::Type{SimpleNonAbelian},::Type{Abelian}) = SimpleNonAbelian
Base.:&(::Type{DegenerateNonAbelian},::Type{Abelian}) = DegenerateNonAbelian
Base.:&(::Type{DegenerateNonAbelian},::Type{SimpleNonAbelian}) = DegenerateNonAbelian

# properties that can be determined in terms of the F symbol
# TODO: find mechanism for returning these numbers with custom type T<:AbstractFloat
"""
    function dim(a::Sector)

Returns the (quantum) dimension of the sector `a`.
"""
dim(a::Sector) = _dim(a, fusiontype(typeof(a)))
_dim(a::Sector, ::Type{Abelian}) = 1
_dim(a::Sector, ::Type{SimpleNonAbelian}) = abs(1/Fsymbol(a,conj(a),a,a,one(a),one(a)))
_dim(a::Sector, ::Type{DegenerateNonAbelian}) = abs(1/Fsymbol(a,conj(a),a,a,one(a),one(a))[1])

"""
    function frobeniusschur(a::Sector)

Returns the Frobenius-Schur indicator of a sector `a`.
"""
frobeniusschur(a::Sector) = _frobeniusschur(a, fusiontype(a))
_frobeniusschur(a::Sector, ::Union{Type{Abelian},Type{SimpleNonAbelian}}) = sign(Fsymbol(a,conj(a),a,a,one(a),one(a)))
_frobeniusschur(a::Sector, ::Type{DegenerateNonAbelian}) = sign(Fsymbol(a,conj(a),a,a,one(a),one(a))[1])

"""
    function Bsymbol(a::G, b::G, c::G) where {G<:Sector}

Returns the value of B^{a,b}_c which appears in transforming a splitting vertex
into a fusion vertex using the transformation
```
a -<-μ-<- c                                 a -<-ν-<- c
     ∨          -> Bsymbol(a,b,c)[μ,ν]           ∧
     b                                         dual(b)
```
If `fusiontype(G)` is `Abelian` or `SimpleNonAbelian`, the B-symbol is a number,
and more specifically a pure phase with `abs == 1`. Otherwise it is a unitary matrix
with row and column size `Nsymbol(a,b,c) == Nsymbol(c,dual(b),a)`.
"""
Bsymbol(a::G, b::G, c::G) where {G<:Sector} = _Bsymbol(a,b,c, fusiontype(G))
_Bsymbol(a,b,c, ::Type{Abelian}) = sign(Fsymbol(a, b, dual(b), a, c, one(a)))
_Bsymbol(a,b,c, ::Type{SimpleNonAbelian}) = sign(sqrt(dim(a)*dim(b)/dim(c))*Fsymbol(a, b, dual(b), a, c, one(a))) # sign enforces this to be a pure phase (abs=1)
_Bsymbol(a,b,c, ::Type{DegenerateNonAbelian}) = sqrt(dim(a)*dim(b)/dim(c))*reshape(Fsymbol(a,b,dual(b),a,c,one(a)), (Nsymbol(a,b,c), Nsymbol(c,dual(b),a)))

# Not necessary
Asymbol(a::G, b::G, c::G) where {G<:Sector} = _Asymbol(a,b,c, fusiontype(G))
_Asymbol(a,b,c, ::Type{Abelian}) = conj(frobeniusschur(a)*Fsymbol(dual(a),a,b,b,one(a),c))
_Asymbol(a,b,c, ::Type{SimpleNonAbelian}) = sqrt(dim(a)*dim(b)/dim(c))*conj(frobeniusschur(a)*Fsymbol(dual(a),a,b,b,one(a),c))
_Asymbol(a,b,c, ::Type{DegenerateNonAbelian}) = sqrt(dim(a)*dim(b)/dim(c))*reshape(conj(frobeniusschur(a)*Fsymbol(dual(a),a,b,b,one(a),c)), (Nsymbol(a,b,c), Nsymbol(dual(a),c,b)))

# Braiding:
#-------------------------------------------------
# trait to describe type to denote how the elementary spaces in a tensor product space
# interact under permutations or actions of the braid group
abstract type Braiding end # generic braiding
abstract type SymmetricBraiding <: Braiding end # symmetric braiding => actions of permutation group are well defined
struct Bosonic <: SymmetricBraiding end # trivial under permutations
struct Fermionic <: SymmetricBraiding end
struct Anyonic <: Braiding end

Base.:&(::Type{B},::Type{B}) where {B<:Braiding} = B
Base.:&(B1::Type{<:Braiding},B2::Type{<:Braiding}) = B2 & B1

Base.:&(::Type{Bosonic},::Type{Fermionic}) = Fermionic
Base.:&(::Type{Bosonic},::Type{Anyonic}) = Anyonic
Base.:&(::Type{Fermionic},::Type{Anyonic}) = Anyonic

"""
    function braidingtype(G::Type{<:Sector}) -> Type{<:Braiding}

Returns the type of braiding behavior of sectors of type G, which can be either
*   `Bosonic`: trivial exchange
*   `Fermionic`: fermionic exchange depending on `fermionparity`
*   `Anyonic`: requires general R_(a,b)^c phase or matrix (depending on `SimpleNonAbelian` or `DegenerateNonAbelian` fusion)

Note that `Bosonic` and `Fermionic` are subtypes of `SymmetricBraiding`, which means that
braids are in fact equivalent to crossings (i.e. braiding twice is an identity:
`Rsymbol(b,a,c)*Rsymbol(a,b,c) = I`) and permutations are uniquely defined.
"""
braidingtype(::Type{Trivial}) = Bosonic
braidingtype(a::Sector) = braidingtype(typeof(a))

# SectorSet:
#-------------------------------------------------------------------------------
# wrapper type to represent a subset of sectors, e.g. as output of a fusion of
# two sectors. It is used as iterator but should be a set, i.e. unique elements
struct SectorSet{G<:Sector,S}
    set::S
end
SectorSet{T}(set::S) where {T<:Sector,S} = SectorSet{T,S}(set)

Base.iteratoreltype(::Type{<:SectorSet}) = Base.HasEltype()
Base.eltype(::SectorSet{G}) where {G<:Sector} = G
Base.iteratorsize(::Type{SectorSet{G,S}}) where {G<:Sector,S} = Base.iteratorsize(S)
Base.length(s::SectorSet) = length(s.set)
Base.size(s::SectorSet) = size(s.set)

Base.start(s::SectorSet) = start(s.set)
function Base.next(s::SectorSet{G}, state) where {G<:Sector}
    v, nextstate = next(s.set, state)
    return G(v), nextstate
end
Base.done(s::SectorSet, state) = done(s.set, state)

# possible sectors
include("irreps.jl") # irreps of symmetry groups, with bosonic braiding
include("fermions.jl") # irreps of symmetry groups, with defined fermionparity and fermionic braiding
include("product.jl") # direct product of different sectors


text:
@testset "VectorFold" begin
    vf_dict = Dict([
        make_vector_fold([1,2], 10, 5, :immutable) => Dict(
            :pattern => [1,2],
            :gap => 10,
            :folds => 5,
            :length => 10,
            :unfold => [1,2,11,12,21,22,31,32,41,42],
            :reverse => reverse([1,2,11,12,21,22,31,32,41,42]),
        ),
        make_vector_fold([1,2], 10, 5, :mutable) => Dict(
            :pattern => [1,2],
            :gap => 10,
            :folds => 5,
            :length => 10,
            :unfold => [1,2,11,12,21,22,31,32,41,42],
            :reverse => reverse([1,2,11,12,21,22,31,32,41,42]),
        ),
    ])

    for (vf, results) in vf_dict
        @test pattern(vf) == results[:pattern]
        @test gap(vf) == results[:gap]
        @test folds(vf) == results[:folds]
        @test length(vf) == results[:length]
        @test unfold(vf) == results[:unfold]
        @test ndims(vf) == 1
        @test rand(vf) ∈ vf
        @test collect(vf) == [i for i in vf] == unfold(vf)
        @test collect(Iterators.reverse(vf)) == reverse(collect(vf)) == results[:reverse]
    end
    @test isempty(make_vector_fold(Vector(),1,1,:immutable))
    @test isempty(make_vector_fold(Vector(),1,1))

    v1 = make_vector_fold([42,3,45,6],13,4)
    w1 = unfold(v1)
    v11 = fold(w1)

    @test unfold(v11) == w1

    v2 = make_vector_fold([34,34,43,43],10,3)
    w2 = unfold(v2)
    v22 = fold(w2)

    @test unfold(v22) == w2

    # isf = IntervalsFold(Interval((0.0, true), (0.0, true)), 1.0, 5)
    # @test is_points(isf)
    # @test unfold(make_vector_fold(isf)) == [i for i in 0.0:4.0]
end


text:
export zoo_nnf, zoo_nnf_file, NnfFormat

struct NnfFormat <: FileFormat end

##############################################
# Read NNFs (file format used by the c2d and D4 compilers)
##############################################

zoo_nnf_file(name) =
    artifact"circuit_model_zoo" * zoo_version * "/nnfs/$name"

"""
    zoo_nnf(name)

Loads NNF file with given name from model zoo. See https://github.com/UCLA-StarAI/Circuit-Model-Zoo.
"""
zoo_nnf(name) =
    read(zoo_nnf_file(name), LogicCircuit, NnfFormat())

const nnf_grammar = raw"""
    start: header (_NL node)+ _NL?

    header : "nnf" _WS INT _WS INT _WS INT

    node : "L" _WS SIGNED_INT -> literal_node
         | "A" _WS INT child_nodes -> and_node
         | "O" _WS INT _WS INT child_nodes -> or_node

        child_nodes : (_WS INT)*

    %import common.INT
    %import common.SIGNED_INT
    %import common.WS_INLINE -> _WS
    %import common.NEWLINE -> _NL
    """

const nnf_parser = Lark(nnf_grammar)

mutable struct NnfParse <: JuiceTransformer
    i::Int
    nodes::Vector{PlainLogicCircuit}
    NnfParse() = new(0, PlainLogicCircuit[])
end

@rule start(t::NnfParse, x) = begin
    v, e, n = x[1]
    circuit = t.nodes[end]
    @assert num_nodes(circuit) == v
    @assert num_edges(circuit) == e
    @assert num_variables(circuit) == n
    circuit
end

@rule header(t::NnfParse, x) = begin
    v, e, n = Base.parse.(Int, x)
    t.nodes = Vector{PlainLogicCircuit}(undef,v)
    v, e, n
end

@inline_rule literal_node(t::NnfParse, x) =
    t.nodes[t.i+=1] = PlainLiteralNode(Base.parse(Lit,x))

@rule and_node(t::NnfParse, x) = begin
    @assert length(x[2]) == Base.parse(Lit,x[1])
    t.nodes[t.i+=1] = if length(x[2]) == 0
        PlainConstantNode(true)
    else
        Plain⋀Node(x[2])
    end
end

@rule or_node(t::NnfParse, x) = begin
    @assert length(x[3]) == Base.parse(Lit,x[2])
    t.nodes[t.i+=1] = if length(x[3]) == 0
        PlainConstantNode(false)
    else
        Plain⋁Node(x[3])
    end
end

@rule child_nodes(t::NnfParse, x) = begin
    child_i = Base.parse.(Int,x) .+ 1
    t.nodes[child_i]
end

function Base.parse(::Type{PlainLogicCircuit}, str, ::NnfFormat)
    ast = Lerche.parse(nnf_parser, str)
    Lerche.transform(NnfParse(), ast)
end

Base.read(io::IO, ::Type{PlainLogicCircuit}, ::NnfFormat) =
    parse(PlainLogicCircuit, read(io, String), NnfFormat())

##############################################
# Write Nnfs
##############################################

function Base.write(io::IO, circuit::LogicCircuit, ::NnfFormat)

    labeling = label_nodes(circuit)
    map!(x -> x-1, values(labeling)) # nodes are 0-based indexed

    println(io, "nnf $(num_nodes(circuit)) $(num_edges(circuit)) $(num_variables(circuit))")
    foreach(circuit) do n
        if isliteralgate(n)
            println(io, "L $(literal(n))")
        elseif isconstantgate(n)
            if constant(n) == true
                println(io, "A 0")
            else
                println(io, "O 0 0")
            end
        else
            if is⋀gate(n)
                print(io, "A $(num_children(n))")
            else
                @assert is⋁gate(n)
                print(io, "O 0 $(num_children(n))")
            end
            for child in children(n)
                print(io, " $(labeling[child])")
            end
            println(io)
        end
    end
    nothing
end

max_issues_repo_name: Juice-jl/LogicCircuits

max_forks_repo_name: Juice-jl/LogicCircuits


text:
# ---
# title: 807. Max Increase to Keep City Skyline
# id: problem807
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories:
# link: <https://leetcode.com/problems/max-increase-to-keep-city-skyline/description/>
# hidden: true
# ---
#
# In a 2 dimensional array `grid`, each value `grid[i][j]` represents the height
# of a building located there. We are allowed to increase the height of any
# number of buildings, by any amount (the amounts can be different for different
# buildings). Height 0 is considered to be a building as well.
#
# At the end, the "skyline" when viewed from all four directions of the grid,
# i.e. top, bottom, left, and right, must be the same as the skyline of the
# original grid. A city's skyline is the outer contour of the rectangles formed
# by all the buildings when viewed from a distance. See the following example.
#
# What is the maximum total sum that the height of the buildings can be
# increased?
#
#
#
#     **Example:**
#     Input: grid = [[3,0,8,4],[2,4,5,7],[9,2,6,3],[0,3,1,0]]
#     Output: 35
#     Explanation:
#     The grid is:
#     [ [3, 0, 8, 4],
#       [2, 4, 5, 7],
#       [9, 2, 6, 3],
#       [0, 3, 1, 0] ]
#
#     The skyline viewed from top or bottom is: [9, 4, 8, 7]
#     The skyline viewed from left or right is: [8, 7, 9, 3]
#
#     The grid after increasing the height of buildings without affecting skylines is:
#
#     gridNew = [ [8, 4, 8, 7],
#                 [7, 4, 7, 7],
#                 [9, 4, 8, 7],
#                 [3, 3, 3, 3] ]
#
#
#
# **Notes:**
#
#   * `1 < grid.length = grid[0].length <= 50`.
#   * All heights `grid[i][j]` are in the range `[0, 100]`.
#   * All buildings in `grid[i][j]` occupy the entire grid cell: that is, they are a `1 x 1 x grid[i][j]` rectangular prism.
#
#
## @lc code=start
using LeetCode

## add your code here:
## @lc code=end


text:
using Router

route("/", "tweets#TweetsController.index")

max_stars_repo_name: essenciary/genie-demo-tweet-stats

max_issues_repo_name: essenciary/genie-demo-tweet-stats

max_forks_repo_name: essenciary/genie-demo-tweet-stats


text:
module OpenGLBackend

using ModernGL
using CSyntax
using CSyntax.CEnum
using ..LibCImGui
using ..CImGui: GetIO, GetTexDataAsRGBA32, SetTexID

include("impl.jl")
export ImGui_ImplOpenGL3_Init, ImGui_ImplOpenGL3_Shutdown
export ImGui_ImplOpenGL3_NewFrame
export ImGui_ImplOpenGL3_RenderDrawData
export ImGui_ImplOpenGL3_CreateFontsTexture, ImGui_ImplOpenGL3_DestroyFontsTexture
export ImGui_ImplOpenGL3_CreateDeviceObjects, ImGui_ImplOpenGL3_DestroyDeviceObjects
export ImGui_ImplOpenGL3_CreateImageTexture, ImGui_ImplOpenGL3_UpdateImageTexture, ImGui_ImplOpenGL3_DestroyImageTexture

const g_GlslVersion = Ref(130)
const g_FontTextures = GLuint[]
const g_ShaderHandle = Ref(GLuint(0))
const g_VertHandle = Ref(GLuint(0))
const g_FragHandle = Ref(GLuint(0))
const g_AttribLocationTex = Ref(GLint(0))
const g_AttribLocationProjMtx = Ref(GLint(0))
const g_AttribLocationVtxPos = Ref(GLint(0))
const g_AttribLocationVtxUV = Ref(GLint(0))
const g_AttribLocationVtxColor = Ref(GLint(0))
const g_VboHandle = Ref(GLuint(0))
const g_ElementsHandle = Ref(GLuint(0))
const g_ImageTexture = Dict{Int,GLuint}()

end # module


text:
################################################################################
# lazydict.jl
#
# A lazy persistent dictionary type that uses the PDict types.
#
# @author Noah C. Benson
#
# MIT License
# Copyright (c) 2020-2021 Noah C. Benson

# This function is used for processing sequences of arguments when constructing
# a lazy dictionary: you want to typejoin across pairs/tuples, and you want to
# figure out Pair{K,V} for which all the given values are either of type V or
# of Delay{V}.
function _lazypairs_typejoin(args)
    n = length(args)
    ks = Vector{Type}(undef, n)
    vs = Vector{Type}(undef, n)
    top = 0
    for (k,v) in args
        top += 1
        ks[top] = typeof(k)
        vT = typeof(v)
        if vT <: Delay
            vT = vT.parameters[1]
        end
        vs[top] = vT
    end
    return (typejoin(ks...), typejoin(vs...))
end

# Like with PDicts, we construct LazyDicts in an unfortunaetly complex way in
# order to reduce code duplication. The three kinds of persistent sets are very
# similar, just using different equality tests, so we can keep most of the code
# the same.
macro _ldict_code(name::Symbol, eq, h, dtype)
    return quote
        struct $name{K,V} <: AbstractPDict{K,V}
            dict::$dtype{K,Delay{V}}
            function $name{K,V}() where {K,V}
                return new{K,V}($dtype{K,Delay{V}}())
            end
            function $name{K,V}(itr) where {K,V}
                return reduce(push, itr, init=new{K,V}($dtype{K,Delay{V}}()))
            end
            function $name{K,V}(d::$name{K,V}) where {K,V}
                return d
            end
            function $name{K,V}(ps::Union{Pair,Tuple}...) where {K,V}
                return reduce(push, ps, init=new{K,V}($dtype{K,Delay{V}}()))
            end
            function $name{K,V}(dict::$dtype{K,Delay{V}}) where {K,V}
                return new{K,V}(dict)
            end
        end
        # Document the equal/hash types.
        equalfn(::Type{T}) where {T <: $name} = $eq
        hashfn(::Type{T}) where {T <: $name} = $h
        equalfn(::$name) = $eq
        hashfn(::$name) = $h
        # Generic construction.
        $name() = $name{Any,Any}()
        $name(itr) = begin
            (K,V) = _lazypairs_typejoin(itr)
            return $name{K,V}(itr)
        end
        $name(d::AbstractDict{K,V}) where {K,V} = $name{K,V}(d)
        $name(::Tuple{}) = $name{Any,Any}()
        $name(p::Pair{K,V}) where {K,V} = $name{K,V}(p)
        $name(ps::Union{Pair,Tuple}...) = begin
            (K,V) = _lazypairs_typejoin(ps)
            return $name{K,V}(ps...)
        end
        # Base methods.
        Base.empty(s::$name{K,V}, ::Type{J}=K, ::Type{U}=V) where {K,V,J,U} = $name{J,U}()
        Base.length(s::$name) = length(getfield(s, :dict))
        Base.iterate(u::$name{K,V}) where {K,V} = begin
            (length(u) == 0) && return nothing
            it = iterate(getfield(u, :dict))
            (it === nothing) && return nothing
            ((k,v),state) = it
            return (k => v[], state)
        end
        Base.iterate(u::$name{K,V}, state) where {K,V} = begin
            it = iterate(getfield(u, :dict), state)
            (it === nothing) && return nothing
            ((k,v),state) = it
            return (k => v[], state)
        end
        Base.get(u::$name{K,V}, k, df) where {K,V} = begin
            uu = get(getfield(u, :dict), k, nothing)
            (uu === nothing) && return df
            uu::Delay{V}
            return uu[]
        end
        Base.haskey(u::$name{K,V}, k) where {K,V} = begin
            uu = get(getfield(u, :dict), k, nothing)
            return (uu !== nothing)
        end
        Base.keys(u::$name{K,V}, k) where {K,V} = keys(getfield(u, :dict))
        Base.in(kv::Pair, u::$name{K,V}, eqfn::F) where {K,V,F<:Function} = begin
            dict = getfield(u, :dict)
            v = get(dict, kv.first, nothing)
            (v === nothing) && return false
            return eqfn(kv.second, v[])
        end
        Base.in(x::Pair, u::$name{K,V}) where {K,V} = in(x, u, $eq)
        # And the Air methods.
        Air.push(u::$name{K,V}, x::Pair) where {K,V} = begin
            (k,v) = x
            dict = getfield(u, :dict)
            currval = get(dict, k, nothing)
            if currval !== nothing
                (currval === x) && return u
                (Base.isready(currval) && currval[] === x) && return u
            end
            isa(v, Delay{V}) || (v = Delay{V}(v))
            return $name{K,V}(setindex(dict, v, k))
        end
        Air.push(u::$name{K,V}, x::Pair{J,Delay{V}}) where {K,J,V} = begin
            (k,v) = x
            dict = getfield(u, :dict)
            currval = get(dict, k, nothing)
            if currval !== nothing
                (currval === x) && return u
                (Base.isready(currval) && currval[] === x) && return u
            end
            return $name{K,V}(setindex(dict, v, k))
        end
        Air.push(u::$name{K,V}, x::Pair{J,V}) where {K,J,V} = begin
            (k,v) = x
            dict = getfield(u, :dict)
            currval = get(dict, k, nothing)
            if currval !== nothing && Base.isready(currval) && currval[] === x
                return u
            else
                return $name{K,V}(setindex(dict, Delay{V}(v), k))
            end
        end
        Air.delete(u::$name{K,V}, x::J) where {K,V,J} = begin
            dict = getfield(u, :dict)
            newd = delete(dict, x)
            (newd === dict) && return u
            return $name{K,V}(newd)
        end
        Base.isready(u::$name{K,V}, k) where {K,V} = begin
            dict = getfield(u, :dict)
            v = get(dict, k, nothing)
            (v === nothing) && throw(KeyError(k))
            return !isa(v._val, _DelayPending)
        end
    end |> esc
end
# Generate the types.
@_ldict_code LazyDict isequal hash PDict
@_ldict_code LazyIdDict (===) objectid PIdDict
# Document the types.
@doc """
    LazyDict{K,V}

A dictionay type equivalent to `PDict{K,V}` in every way except that for any
value in the dict that is a `Delay` object, the dictionary hides the delay and
always returns the delay's value. This allows any value that has not yet been
requested to be lazily unevaluated.

See also: [`PDict`](@ref), `Base.Dict`, [`LazyIdDict`](@ref),
[`Delay`](@ref), [`@delay`](@ref).

# Examples

```@meta
DocTestSetup = quote
    using Air
end
```

```jldoctest; filter=r"LazyDict{Any, ?Any}()"
julia> LazyDict()
LazyDict{Any,Any}()
```

```jldoctest; filter=r"LazyDict{Symbol, ?Real} with 3 entries:"
julia> LazyDict(:a => 1, :b => 2, :c => 12.8)
LazyDict{Symbol,Real} with 3 entries:
  :c => 12.8
  :a => 1
  :b => 2
```

```jldoctest; filter=r"LazyDict{Symbol, ?Float64} with 3 entries:"
julia> d = LazyDict{Symbol,Float64}(:a => 1, :b => 2, :c => 12.8)
LazyDict{Symbol,Float64} with 3 entries:
  :c => 12.8
  :a => 1.0
  :b => 2.0

julia> d2 = push(d, :d => Delay{Real}(() -> (println("Running..."); 0.5))); haskey(d2, :d)
true

julia> d2[:d]
Running...
0.5

julia> d2[:d]
0.5
```
""" LazyDict
@doc """
    LazyIdDict{K,V}

A dictionay type equivalent to `PIdDict{K,V}` in every way except that for any
value in the dict that is a `Delay` object, the dictionary hides the delay and
always returns the delay's value. This allows any value that has not yet been
requested to be lazily unevaluated.

See also: [`PIdDict`](@ref), `Base.IdDict`, [`LazyDict`](@ref),
[`Delay`](@ref), [`@delay`](@ref).

# Examples

```@meta
DocTestSetup = quote
    using Air
end
```

```jldoctest; filter=r"LazyIdDict{Any, ?Any}()"
julia> LazyIdDict()
LazyIdDict{Any,Any}()
```

```jldoctest; filter=r"LazyIdDict{Symbol, ?Real} with 3 entries:"
julia> LazyIdDict(:a => 1, :b => 2, :c => 12.8)
LazyIdDict{Symbol,Real} with 3 entries:
  :b => 2
  :a => 1
  :c => 12.8
```

```jldoctest; filter=r"LazyIdDict{Symbol, ?Float64} with 3 entries:"
julia> d = LazyIdDict{Symbol,Float64}(:a => 1, :b => 2, :c => 12.8)
LazyIdDict{Symbol,Float64} with 3 entries:
  :b => 2.0
  :a => 1.0
  :c => 12.8

julia> d2 = push(d, :d => Delay{Real}(() -> (println("Running..."); 0.5)));

julia> haskey(d2, :d)
true

julia> d2[:d]
Running...
0.5

julia> d2[:d]
0.5
```
""" LazyIdDict

# Export the relevant symbols.
export LazyDict, LazyIdDict


text:
include("common.jl")
@testset "Discontinuity" begin
    # total order
    @testset "total order" begin
        a = Discontinuity(1, 3)

        @test a > 0
        @test a < 2
        @test a == 1

        b = Discontinuity(2.0, 2)

        @test !(a > b)
        @test a < b
        @test a != b

        c = Discontinuity(1.0, 2)

        @test a > c
        @test !(a < c)
        @test a != c
    end

    # simple DDE example
    @testset "DDE" begin
        integrator = init(prob_dde_2delays, MethodOfSteps(BS3()))

        # initial discontinuities
        @testset "initial" begin
            @test isempty(integrator.opts.d_discontinuities_cache)
            @test length(integrator.opts.d_discontinuities) == 2 &&
                issubset([Discontinuity(1/5, 1), Discontinuity(1/3, 1)],
                         integrator.opts.d_discontinuities.valtree)
        end

        # tracked discontinuities
        @testset "tracked" begin
            @test integrator.tracked_discontinuities == [Discontinuity(0., 0)]

            solve!(integrator)

            discs = [Discontinuity(t, order) for (t, order) in
                     ((0., 0), (1/5, 1), (1/3, 1), (2/5, 2), (8/15, 2), (3/5, 3),
                      (2/3, 2), (11/15, 3), (13/15, 3))]

            for (tracked, disc) in zip(integrator.tracked_discontinuities, discs)
                @test tracked.t ≈ disc.t && tracked.order == disc.order
            end
        end
    end

    # additional discontinuities
    @testset "DDE with discontinuities" begin
        integrator = init(prob_dde_2delays, MethodOfSteps(BS3());
                          d_discontinuities = [Discontinuity(0.3, 4), Discontinuity(0.6, 5)])

        @test integrator.opts.d_discontinuities_cache == [Discontinuity(0.3, 4)]
        @test length(integrator.opts.d_discontinuities) == 3 &&
            issubset([Discontinuity(1/5, 1), Discontinuity(1/3, 1), Discontinuity(0.3, 4)],
                         integrator.opts.d_discontinuities.valtree)
        end
end


text:
# Helper routines

function reverseeven!(x::AbstractVector)
    n = length(x)
    if iseven(n)
        @inbounds @simd for k=2:2:n÷2
            x[k],x[n+2-k] = x[n+2-k],x[k]
        end
    else
        @inbounds @simd for k=2:2:n÷2
            x[k],x[n+1-k] = x[n+1-k],x[k]
        end
    end
    x
end

function negateeven!(x::AbstractVector)
    @inbounds @simd for k = 2:2:length(x)
        x[k] *= -1
    end
    x
end

### In-place O(n) interlacing

function highestleader(n::Int)
    i = 1
    while 3i < n i *= 3 end
    i
end

function nextindex(i::Int,n::Int)
    i <<= 1
    while i > n
        i -= n + 1
    end
    i
end

function cycle_rotate!(v::AbstractVector, leader::Int, it::Int, twom::Int)
    i = nextindex(leader, twom)
    while i != leader
        idx1, idx2 = it + i - 1, it + leader - 1
        @inbounds v[idx1], v[idx2] = v[idx2], v[idx1]
        i = nextindex(i, twom)
    end
    v
end

function right_cyclic_shift!(v::AbstractVector, it::Int, m::Int, n::Int)
    itpm = it + m
    itpmm1 = itpm - 1
    itpmpnm1 = itpmm1 + n
    reverse!(v, itpm, itpmpnm1)
    reverse!(v, itpm, itpmm1 + m)
    reverse!(v, itpm + m, itpmpnm1)
    v
end

"""
This function implements the algorithm described in:

    P. Jain, "A simple in-place algorithm for in-shuffle," arXiv:0805.1598, 2008.
"""
function interlace!(v::AbstractVector,offset::Int)
    N = length(v)
    if N < 2 + offset
        return v
    end

    it = 1 + offset
    m = 0
    n = 1

    while m < n
        twom = N + 1 - it
        h = highestleader(twom)
        m = h > 1 ? h÷2 : 1
        n = twom÷2

        right_cyclic_shift!(v,it,m,n)

        leader = 1
        while leader < 2m
            cycle_rotate!(v, leader, it, 2m)
            leader *= 3
        end

        it += 2m
    end
    v
end

abstract type AbstractInterlaceBasis{T} <: Basis{T} end
copy(A::AbstractInterlaceBasis) = interlacebasis(A, map(copy, A.args))

"""
    PiecewiseInterlace(args...)

is an analogue of `Basis` that takes the union of the first axis,
and the second axis is a blocked interlace of args.
If there is overlap, it uses the first in order.
"""
struct PiecewiseInterlace{T, Args} <: AbstractInterlaceBasis{T}
    args::Args
end

PiecewiseInterlace{T}(args...) where T = PiecewiseInterlace{T,typeof(args)}(args)
PiecewiseInterlace(args...) = PiecewiseInterlace{mapreduce(eltype,promote_type,args)}(args...)
PiecewiseInterlace{T}(args::AbstractVector) where T = PiecewiseInterlace{T,typeof(args)}(args)
PiecewiseInterlace(args::AbstractVector) = PiecewiseInterlace{eltype(eltype(args))}(args)

interlacebasis(::PiecewiseInterlace, args...) = PiecewiseInterlace(args...)

axes(A::PiecewiseInterlace) = (union(axes.(A.args,1)...), LazyBandedMatrices._block_vcat_axes(unitblocks.(axes.(A.args,2))...))

==(A::PiecewiseInterlace, B::PiecewiseInterlace) = all(A.args .== B.args)

function QuasiArrays._getindex(::Type{IND}, A::PiecewiseInterlace{T}, (x,j)::IND) where {IND,T}
    Jj = findblockindex(axes(A,2), j)
    @boundscheck x in axes(A,1) || throw(BoundsError(A, (x,j)))
    J = Int(block(Jj))
    i = blockindex(Jj)
    x in axes(A.args[i],1) && return A.args[i][x, J]
    zero(T)
end

function \(A::AbstractInterlaceBasis, B::AbstractInterlaceBasis)
    axes(A,1) == axes(B,1) || throw(DimensionMismatch())
    T = promote_type(eltype(A),eltype(B))
    A == B && return Eye{T}((axes(A,2),))
    BlockBroadcastArray{T}(Diagonal, unitblocks.((\).(A.args, B.args))...)
end

@simplify function *(D::Derivative, S::AbstractInterlaceBasis)
    axes(D,2) == axes(S,1) || throw(DimensionMismatch())
    args = arguments.(*, Derivative.(axes.(S.args,1)) .* S.args)
    all(length.(args) .== 2) || error("Not implemented")
    interlacebasis(S, map(first, args)...) * BlockBroadcastArray{promote_type(eltype(D),eltype(S))}(Diagonal, unitblocks.(last.(args))...)
end

struct PiecewiseFactorization{T,FF,Ax} <: Factorization{T}
    factorizations::FF
    axes::Ax
end

PiecewiseFactorization{T}(fac, ax) where T = PiecewiseFactorization{T,typeof(fac),typeof(ax)}(fac, ax)

function \(F::PiecewiseFactorization{T}, v::AbstractQuasiVector) where {T}
    BlockBroadcastArray{T}(vcat, unitblocks.((\).(F.factorizations, getindex.(Ref(v), F.axes)))...)
end

function factorize(V::SubQuasiArray{T,2,<:AbstractInterlaceBasis,<:Tuple{Inclusion,BlockSlice{BlockRange1{OneTo{Int}}}}}) where T
    P = parent(V)
    _,jr = parentindices(V)
    N = Int(last(jr.block))
    PiecewiseFactorization{T}(factorize.(view.(P.args, :, Ref(Base.OneTo(N)))), axes.(P.args,1))
end

function factorize(V::SubQuasiArray{<:Any,2,<:AbstractInterlaceBasis,<:Tuple{Inclusion,AbstractVector{Int}}})
    P = parent(V)
    _,jr = parentindices(V)
    J = findblock(axes(P,2),maximum(jr))
    ProjectionFactorization(factorize(P[:,Block.(OneTo(Int(J)))]), jr)
end


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

JLLWrappers.@generate_main_file_header("H3")
JLLWrappers.@generate_main_file("H3", UUID("60c8821e-da38-5dbd-93c7-983f1e6d69be"))
end  # module H3_jll


text:
using BlockArrays
if VERSION >= v"0.5-"
    using Base.Test
else
    using BaseTestNext
    const Test = BaseTestNext
end

include("test_blockindices.jl")

@enum blockenums u=1 γ=2 ξ=3

let
    BA_1 = BlockArray(Vector{Float64}, [1,2,3])
    a_1 = rand(2)
    BA_1[Block(2)] = a_1
    @test BA_1[γ] == a_1
    @test BA_1[Block(2)] == a_1
    @test BA_1[2] == a_1[1]
    @test_throws DimensionMismatch BA_1[Block(3)] = rand(4)
    @test_throws BlockBoundsError blockcheckbounds(BA_1, 4)
    @test_throws BlockBoundsError BA_1[Block(4)]

    BA_2 = BlockArray(Matrix{Float64}, [1,2], [3,4])
    a_2 = rand(1,4)
    BA_2[Block(1,2)] = a_2
    @test BA_2[Block(1,2)] == a_2
    BA_2[Block(1,2)] = zeros(1,4)
    BA_2[u, γ] = a_2
    @test BA_2[u, γ] == a_2

    @test BA_2[1,5] == a_2[2]
    @test_throws DimensionMismatch BA_2[Block(1,2)] = rand(1,5)
end

let
    for BlockType in (BlockArray, PseudoBlockArray)
        a_1 = rand(6)
        BA_1 = BlockType(a_1, [1,2,3])
        @test full(BA_1) == a_1
        @test nblocks(BA_1) == (3,)
        @test nblocks(BA_1,1) == 3
        @test eltype(similar(BA_1, Float32)) == Float32
        q = rand(1)
        BA_1[Block(1)] = q
        @test BA_1[Block(1)] == q
        @test BA_1[u] == q
        if BlockType == PseudoBlockArray
            q2 = zeros(q)
            getblock!(q2, BA_1, 1)
            @test q2 == q
            @test_throws DimensionMismatch getblock!(zeros(2), BA_1, 1)
            fill!(q2, 0)
            getblock!(q2, BA_1, u)
            @test q2 == q
        end
        fill!(BA_1, 1.0)
        @test BA_1 == ones(size(BA_1))
        ran = rand(size(BA_1))
        copy!(BA_1, ran)
        @test BA_1 == ran

        a_1_sparse = sprand(6, 0.9)
        BA_1_sparse = BlockType(a_1_sparse, [1,2,3])
        @test full(BA_1_sparse) == a_1_sparse
        BA_1_sparse[4] = 3.0
        @test BA_1_sparse[4] == 3.0

        a_2 = rand(3, 7)
        BA_2 = BlockType(a_2, [1,2], [3,4])
        @test full(BA_2) == a_2
        @test nblocks(BA_2) == (2,2)
        @test nblocks(BA_2, 1) == 2
        @test nblocks(BA_2, 2, 1) == (2, 2)
        @test eltype(similar(BA_2, Float32)) == Float32
        q = rand(1,4)
        BA_2[Block(1,2)] = q
        @test_throws DimensionMismatch BA_2[Block(1,2)] = rand(1,5)
        @test BA_2[Block(1,2)] == q
        @test BA_2[u,γ] == q
        if BlockType == PseudoBlockArray
            q2 = zeros(q)
            getblock!(q2, BA_2, 1, 2)
            @test q2 == q
            @test_throws DimensionMismatch getblock!(zeros(1,5), BA_2, 1, 2)
            fill!(q2, 0)
            getblock!(q2, BA_2, u, γ)
            @test q2 == q
        end
        fill!(BA_2, 1.0)
        @test BA_2 == ones(size(BA_2))
        ran = rand(size(BA_2))
        copy!(BA_2, ran)
        @test BA_2 == ran

        a_2_sparse = sprand(3, 7, 0.9)
        BA_2_sparse = BlockType(a_2_sparse, [1,2], [3,4])
        @test full(BA_2_sparse) == a_2_sparse
        BA_2_sparse[1,2] = 3.0
        @test BA_2_sparse[1,2] == 3.0

        a_3 = rand(3, 7,4)
        BA_3 = BlockType(a_3, [1,2], [3,4], [1,2,1])
        @test full(BA_3) == a_3
        @test nblocks(BA_3) == (2,2,3)
        @test nblocks(BA_3, 1) == 2
        @test nblocks(BA_3, 3, 1) == (3, 2)
        @test nblocks(BA_3, 3) == 3
        @test eltype(similar(BA_3, Float32)) == Float32
        q = rand(1,4,2)
        BA_3[Block(1,2,2)] = q
        @test BA_3[Block(1,2,2)] == q
        @test BA_3[u, γ, γ] == q
        if BlockType == PseudoBlockArray
            q3 = zeros(q)
            getblock!(q3, BA_3, 1, 2, 2)
            @test q3 == q
            @test_throws DimensionMismatch getblock!(zeros(1,3,2), BA_3, 1, 2,2)
            fill!(q3, 0)
            getblock!(q3, BA_3, u, γ, γ)
            @test q3 == q
        end
        fill!(BA_3, 1.0)
        @test BA_3 == ones(size(BA_3))
        ran = rand(size(BA_3))
        copy!(BA_3, ran)
        @test BA_3 == ran
    end
end

let
    A = BlockArray(rand(4, 5), [1,3], [2,3]);
    buf = IOBuffer()
    Base.showerror(buf, BlockBoundsError(A, (3,2)))
    @test takebuf_string(buf) == "BlockBoundsError: attempt to access 2×2-blocked 4×5 BlockArrays.BlockArray{Float64,2,Array{Float64,2}} at block index [3,2]"
end

replstrmime(x) = stringmime("text/plain", x)
@test replstrmime(BlockArray(collect(reshape(1:16, 4, 4)), [1,3], [2,2])) == "2×2-blocked 4×4 BlockArrays.BlockArray{Int64,2,Array{Int64,2}}:\n 1  5  │   9  13\n ──────┼────────\n 2  6  │  10  14\n 3  7  │  11  15\n 4  8  │  12  16"
let
    for BlockType in (BlockArray, PseudoBlockArray)
        for T in (Float32, Float64, Int32, Int64)
            for (BA_1, BA_2) in ((BlockType(rand(T, 6), [1,2,3]), BlockType(rand(T, 6), [1,2,3])),
                                 (BlockType(rand(T, 3, 7), [1,2], [3,4]), BlockType(rand(T, 3, 7), [1,2], [3,4])))
                for f in Base.flatten((BlockArrays.UNARY_FUNCS, map(i -> i[1], BlockArrays.REDUCTION_FUNCS)))
                    @eval func = $f
                    print(BlockType, " ", T, " ", f, " ")
                    if !method_exists(func, Tuple{T})
                        println("Skipped no method!")
                        continue
                    end
                    local res
                    try
                        res = func(BA_1)
                    catch e
                        (isa(e, DomainError) || isa(e, Base.Math.AmosException)) || rethrow(e)
                        println("Skipped exception!")
                        continue
                    end
                    @test res ≈ func(full(BA_1))
                    println("OK!")
                end

                for f in Base.flatten((BlockArrays.BINARY_FUNCS, BlockArrays.BOOLEAN_BINARY_FUNCS))
                    @eval func = $f
                    print(BlockType, " ", T, " ", f, " ")
                    A = rand(T)
                    if !method_exists(func, Tuple{T, T})
                        println("Skipped no method!")
                        continue
                    end
                    local res, res_scal_1, res_scal_2
                    try
                        res = func(BA_1, BA_2)
                        #res_scal_1 = func(BA_1, A)
                        #res_scal_2 = func(A, BA_2)
                    catch e
                        (isa(e, DomainError) || isa(e, Base.Math.AmosException)) || rethrow(e)
                        println("Skipped!")
                        continue
                    end
                    @test isequal(res, func(full(BA_1), full(BA_2)))
                    #@test isequal(res_scal_1, func(full(BA_1), A))
                    #@test isequal(res_scal_2, func(A, full(BA_2)))
                    println("OK!")
                end
            end
        end
    end
end


text:
export tfimMPO

"""
  Get MPO of transverse field Ising model Hamiltonian with field strength h
"""
function tfimMPO(sites, h::Float64)
  # Input operator terms which define a Hamiltonian
  N = length(sites)
  ampo = OpSum()
  for j in 1:(N - 1)
    ampo += -1, "Z", j, "Z", j + 1
  end
  for j in 1:N
    ampo += h, "X", j
  end
  # Convert these terms to an MPO tensor network
  return MPO(ampo, sites)
end

export get_matrices
function get_matrices(N, h)
  sites = siteinds("S=1/2", N)
  psi0 = randomMPS(sites; linkdims=20)

  # define parameters for DMRG sweeps
  sweeps = Sweeps(50)
  setmaxdim!(sweeps, 10, 20, 100, 100, 200)
  setcutoff!(sweeps, 1E-10)

  H = tfimMPO(sites, h)
  energy, psi = dmrg(H, psi0, sweeps)
  @show energy

  return map(psi.data) do t
      T = reshape(t.tensor.storage.data, size(t))
      if ndims(T) == 2
        return T
      else
        return permutedims(T, (3, 1, 2))
      end
  end
end

export tfim_renyi_entropy
function tfim_renyi_entropy(N, h)
  As = get_matrices(N, h)
  return renyi_entropy(As; nsamples=10^6)/N
end

export exact_tfim_renyi_entropy
function exact_tfim_renyi_entropy(N, h)
  As = get_matrices(N, h)
  return exact_renyi_entropy(As)/N
end

data_dir(xs...) = joinpath(pkgdir(RenyiEntropyOfMagic, "data", xs...))

export prepare_states
function prepare_states(N, hs)
    isdir(data_dir()) || mkpath(data_dir())
    As = pmap(hs) do h
        get_matrices(N, h)
    end
    serialize(data_dir("$N-sites.mps"), hs=>As)
end

function read_matrices(N)
    deserialize(data_dir("$N-sites.mps"))
end

export scan_tfim_renyi_entropy
function scan_tfim_renyi_entropy(N; nsamples=10^6)
    hs, As = read_matrices(N)
    return pmap(zip(hs, As)) do (h, A)
        @info "running" h
        return renyi_entropy(A; nsamples)/N
    end
end


text:
#=
Copyright (c) 2015, Intel Corporation

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of Intel Corporation nor the names of its contributors
      may be used to endorse or promote products derived from this software
      without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=#

module StructureAnalysisMaximalStruct

    import Sparso
    using Sparso: Sym, Symexpr, TypedExprNode
    using ..SymbolicAnalysis
    using ..SymbolicAnalysis: SA_SUCC

    function symbolize(e :: Symexpr, tp :: Type, unique = false)
        if isa(e, Sym)
            if tp <: Vector
                s = MiddleSymbol((e, :NUM_1))
            elseif tp <: SparseMatrixCSC
                s = MiddleSymbol((e, e))
            elseif tp <:Int || tp <:Float64 || tp <:Complex || tp <:Bool
                s = MiddleSymbol((:NUM_1, :NUM_1))
            else
                dump(tp)
                assert(0)
            end
            return s
        else
            assert(0)
        end
    end

    function preprocess(property_proxies, symbol_info)
        predefined = Dict{Sym, AbstractSymbol}()
        for (s, v) in property_proxies
            if !isa(s, Symbol)
                continue
            end
            if isa(v.constant_valued, MiddleSymbol)
                predefined[s] = symbolize(s, symbol_info[s])
            end

            if isa(v.maximal_structured, MiddleSymbol)
                predefined[s] = v.maximal_structured
            end
        end
        predefined
    end

    function postprocess(res, property_proxies, symbol_info)
        for (s, v) in res
            assert(isa(s, Sym))
            if isa(v, MiddleSymbol)
                property_proxies[s].maximal_structured = v
            end
        end
    end

    function add_sub_action(e)
        if all(a -> a.svalue == BOTTOM_SYMBOL, e.args)
            e.svalue = BOTTOM_SYMBOL
        else
            for arg in e.args
                if isa(arg.svalue, MiddleSymbol)
                    e.svalue = arg.svalue
                    break
                end
            end
        end
    end

    # A * B -> (A, B)
    function A_mul_B_action(e)
        if isa(e.args[1].svalue, MiddleSymbol) && isa(e.args[2].svalue, MiddleSymbol)
            e.svalue = MiddleSymbol((e.args[1].svalue.value[1], e.args[2].svalue.value[2]))
        end
    end

    # A * B * C
    function mul3_action(e)
        if isa(e.args[1].svalue, MiddleSymbol) && isa(e.args[3].svalue, MiddleSymbol)
            e.svalue = MiddleSymbol((e.args[1].svalue.value[1], e.args[3].svalue.value[2]))
            if !isa(e.args[2].svalue, MiddleSymbol)
                e.args[2].svalue = e.svalue
            end
        end
    end

    ## v * M -> size of v
    function v_mul_M_action(e)
        assert(0)
        e.svalue = e.args[1].svalue
    end

    # M * v -> (M, M) or (M, v) ?
    function M_mul_v_action(e)
        #dump(e)
        #assert(0)
        if isa(e.args[1].svalue, MiddleSymbol)
            e.svalue = MiddleSymbol((e.args[1].svalue.value[1], :NUM_1))
        end
    end

    function elem_mul_action(e)
        if isa(e.args[1].svalue, MiddleSymbol)
            e.svalue = e.args[1].svalue
        elseif isa(e.args[2].svalue, MiddleSymbol)
            e.svalue = e.args[2].svalue
        end
    end

    function speye_action(e)
        e.svalue = MiddleSymbol((Symbol(e.args[1].raw_expr), Symbol(e.args[1].raw_expr)))
    end

    function transpose_action(e)
        if isa(e.args[1].svalue, MiddleSymbol)
            e.svalue = MiddleSymbol((e.args[1].svalue.value[2], e.args[1].svalue.value[1]))
        end
    end

    function pass_a1_action(e)
        e.svalue = e.args[1].svalue
    end

    function pass_a2_action(e)
        e.svalue = e.args[2].svalue
    end

    function assign_action(e)
        e.args[1].svalue = e.args[2].svalue
    end

    const transfer_rules = (
        ((:(=), Any, SparseMatrixCSC), assign_action),
        ((:(=), Factorization, Any), assign_action),
        ((:(=), Any, Vector), assign_action),

        ((:call, GlobalRef(Main, :*), SparseMatrixCSC, SparseMatrixCSC), A_mul_B_action),
        ((:call, GlobalRef(Main, :spmatmul_witheps), SparseMatrixCSC, SparseMatrixCSC, Any), A_mul_B_action),
        ((:call, GlobalRef(Main, :*), SparseMatrixCSC, SparseMatrixCSC, SparseMatrixCSC), mul3_action),
        ((:call, GlobalRef(Main, :*), SparseMatrixCSC, Vector), M_mul_v_action),
        ((:call, GlobalRef(Main, :*), Vector, SparseMatrixCSC), v_mul_M_action),
        ((:call, GlobalRef(Main, :+), SparseMatrixCSC, SparseMatrixCSC), add_sub_action),
        ((:call, GlobalRef(Main, :+), SparseMatrixCSC, SparseMatrixCSC, SparseMatrixCSC), add_sub_action),
        ((:call, GlobalRef(Main, :-), Any, SparseMatrixCSC), add_sub_action),
        ((:call, GlobalRef(Main, :ctranspose), SparseMatrixCSC), transpose_action),
        ((:call, GlobalRef(Main, :cholfact_int32), SparseMatrixCSC), pass_a1_action),
        ((:call, GlobalRef(Main, :speye), Int), speye_action),
        ((:call, GlobalRef(Main, :speye_int32), Int), speye_action),

        ((:call, GlobalRef(Main, :*), SparseMatrixCSC, Union{Float64, Int64, Int32}), pass_a1_action),
        ((:call, GlobalRef(Main, :/), SparseMatrixCSC, Union{Float64, Int64, Int32}), pass_a1_action),
        ((:call, GlobalRef(Main, :*), Union{Float64, Int64, Int32}, SparseMatrixCSC), pass_a2_action),

        ((:call, GlobalRef(Main, :.*), Vector, Vector), elem_mul_action),
        ((:call, GlobalRef(Main, :+), Vector, Vector), add_sub_action),
        ((:call, GlobalRef(Main, :-), Vector, Vector), add_sub_action),
        ((:call, GlobalRef(Main, :-), Vector, Vector), add_sub_action),

        #((:call, TopNode(:setfield!), SparseMatrixCSC, QuoteNode(:nzval), Any), pass_a1_action_exit),
        ((:call, TypedExprNode(Function, :call, TopNode(:apply_type), GlobalRef(Main, :SparseMatrixCSC), GlobalRef(Main, :Float64), GlobalRef(Main, :Int32)), Any), pass_a1_action),
    )

    const pass_info = ("MaximalStruct", transfer_rules, preprocess, postprocess, symbolize)
end

max_stars_repo_name: IntelLabs/Sparso

max_issues_repo_name: IntelLabs/Sparso

max_forks_repo_name: IntelLabs/Sparso


text:
#=
Copyright 2021 BlackRock, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=#

abstract type TermCache end

TERM_COND_FLAG = Int64
CONVERGE_TERM_COND_FLAG = 1
FIRST_VARS_TERM_COND_FLAG = 2

"""
    get_term_cond_cache(params::AdmmParams, settings::Settings)

Return the TermCache corresponding to the `term_cond_type` specification in the ADMM Settings `settings`
"""
function get_term_cond_cache(params::AdmmParams, settings::Settings)
    if settings.term_cond_type == CONVERGE_TERM_COND_FLAG
        return ConvergeTermCache(params, settings)
    elseif settings.term_cond_type == FIRST_VARS_TERM_COND_FLAG
        return FirstVarsTermCache(params, settings)
    end
end

"""
    ConvergeTermCache

Cache state to helps determine whether or not to terminate

The fields represent:
- `x_last`: Previous x
- `z_last`: Previous z
- `w_last`: Previous w
- `y_last`: Previous y
"""
mutable struct ConvergeTermCache
    x_last::Vector{Float64}
    z_last::Vector{Float64}
    w_last::Vector{Float64}
    y_last::Vector{Float64}

    function ConvergeTermCache(num_vars::Int64, num_constrs::Int64)
        return new(zeros(num_vars), zeros(num_constrs), zeros(num_vars), zeros(num_constrs))
    end
end

"""
    ConvergeTermCache(params::AdmmParams, settings::Settings)

Construct a ConvergeTermCache given AdmmParams `params` and ADMM Settings `settings`
"""
function ConvergeTermCache(params::AdmmParams, settings::Settings)
    return ConvergeTermCache(get_num_vars(params), get_num_constrs(params))
end

"""
    check_term_conds!(tc::ConvergeTermCache, vars::Vars, params::AdmmParams, settings::Settings, iter::Int)

Check termination conditions
"""
function check_term_conds!(tc::ConvergeTermCache, vars::Vars, params::AdmmParams,
                           settings::Settings, iter::Int)

    # only check for convergence every so often
    iter_condition = iter % settings.term_cond_freq == 0
    if iter_condition
        converged = ((maximum(abs.(vars.x - vars.xt)) ≤ settings.ϵ) &&
                     (maximum(abs.(tc.x_last - vars.x)) ≤ settings.ϵ) &&
                     (maximum(abs.(tc.w_last - vars.w)) ≤ settings.ϵ))
        if get_num_constrs(vars) > 0
            converged &= ((maximum(abs.(tc.z_last - vars.z)) ≤ settings.ϵ) &&
                          (maximum(abs.(vars.z - vars.zt)) ≤ settings.ϵ) &&
                          (maximum(abs.(tc.y_last - vars.y)) ≤ settings.ϵ))
        end

        if converged
            return true
        end
    end

    # Store variables for next step.
    tc.x_last = vars.x
    tc.z_last = vars.z
    tc.w_last = vars.w
    tc.y_last = vars.y

    return false
end

"""
    FirstVarsTermCache

Cache state to helps determine whether or not to terminate

The fields represent:
- `obj_best`: The best encountered objective value
- `res_best`: The best encountered residual value
- `x_best`: The best encountered `x` iterate
- `z_best`: The best encountered `z` iterate
- `w_best`: The best encountered `w` (dual) iterate
- `y_best`: The best encountered `y` (dual) iterate
- `n1`: `n` - `m`
- `lb`: Lower bound
- `ub`: Upper bound
- `A1`: first `n1` columns of `A`
- `A2`: remaining columns of `A`
- `not_improved_count`: The number of iterations without improvement seeing improvement
- `not_improved_count_req`: The max allowed iterations without improvement

"""
mutable struct FirstVarsTermCache
    obj_best::Float64
    res_best::Float64
    x_best::Vector{Float64}
    z_best::Vector{Float64}
    w_best::Vector{Float64}
    y_best::Vector{Float64}
    n1::Int64
    lb::Vector{Float64}
    ub::Vector{Float64}
    A1::SparseMatrixCSC
    A2::SparseMatrixCSC
    not_improved_count::Int64
    not_improved_count_req::Int64

    function FirstVarsTermCache(params::AdmmParams, settings::Settings)
        n = get_num_vars(params)
        m = get_num_constrs(params)

        obj_best = Inf
        res_best = Inf
        x_best = zeros(n)
        z_best = zeros(m)
        w_best = zeros(n)
        y_best = zeros(m)
        n1 = n - m
        lb = [params.g[i, 1][1].lb for i in (n1 + 1):n]
        ub = [params.g[i, 1][end].ub for i in (n1 + 1):n]
        A1 = params.A[:, 1:n1]
        A2 = params.A[:, (n1 + 1):end]
        not_improved_count = 0
        not_improved_count_req = ceil(Int64,
                                      settings.non_improvement_iters /
                                      settings.term_cond_freq)
        return new(obj_best, res_best, x_best, z_best, w_best, y_best, n1, lb, ub, A1, A2,
                   not_improved_count, not_improved_count_req)
    end
end

"""
    check_term_conds!(tc::FirstVarsTermCache, vars::Vars, params::AdmmParams, settings::Settings, iter::Int)

Check termination conditions
"""
function check_term_conds!(tc::FirstVarsTermCache, vars::Vars, params::AdmmParams,
                           settings::Settings, iter::Int)

    # only check for convergence every so often
    iter_condition = iter % settings.term_cond_freq == 0
    if iter_condition
        obj, res = get_obj_and_res_from_first_vars(tc, vars, params)

        # in order to have "improved", the objective value must decrease, the
        # residuals must be sufficiently small
        improved = ((obj < tc.obj_best - settings.obj_tol) && (res < settings.res_tol))
        if improved
            tc.not_improved_count = 0
        else
            tc.not_improved_count += 1
        end

        # if found a new best objective/residual pair, save the iterate
        if (obj < tc.obj_best) && (res < settings.res_tol)
            tc.x_best = copy(vars.x)
            tc.z_best = copy(vars.z)
            tc.w_best = copy(vars.w)
            tc.y_best = copy(vars.y)
            tc.obj_best = obj
            tc.res_best = res
        end

        # if there hasn't been sufficient improvement over the best for a
        # certain number of iterations, set the vars to contain the best iterate
        converged = (tc.not_improved_count > tc.not_improved_count_req) &&
                    (tc.obj_best < Inf)
        if converged
            vars.x = copy(tc.x_best)
            vars.z = copy(tc.z_best)
            vars.w = copy(tc.w_best)
            vars.y = copy(tc.y_best)
        end
    else
        converged = false
    end

    return converged
end

"""
    get_obj_and_res_from_first_vars(tc::FirstVarsTermCache, vars::Vars, params::AdmmParams)

Get objective and residual from the FirstVarsTermCache `tc`
"""
function get_obj_and_res_from_first_vars(tc::FirstVarsTermCache, vars::Vars,
                                         params::AdmmParams)
    x = guess_point_from_first_vars(vars, params)
    n = get_num_vars(params)

    fx = 0.5 * x' * params.P * x + params.q' * x  # quad plus lin
    gx = 0.0  # sep
    residual = 0.0
    for i in 1:get_num_vars(params)
        if isfinite(params.g[i, 1](x[i]))
            gx += params.g[i, 1](x[i])
        end
        if i > tc.n1
            if x[i] < tc.lb[i - tc.n1]
                residual += tc.lb[i - tc.n1] - x[i]
            elseif x[i] > tc.ub[i - tc.n1]
                residual += x[i] - tc.ub[i - tc.n1]
            end
        end
    end

    objective = fx + gx
    return objective, residual
end

function guess_point_from_first_vars(vars::Vars, params::AdmmParams)
    n = get_num_vars(vars)
    m = get_num_constrs(vars)
    n1 = n - m
    x1 = vars.x[1:n1]
    A1 = params.A[:, 1:n1]
    A2 = params.A[:, (n1 + 1):end]
    x2 = A2 \ (params.b - A1 * x1)
    x = [x1; x2]
    return x
end


text:
# measurements.jl
#
# Defines estimators and provides measurements

function Measure()

    spin_prop = copy(spin_left)

    for i = 1:M #propagate half the list only (to the middle)
       if operator_list[i,1] == -2
           spin_prop[operator_list[i,2]] = xor(spin_prop[operator_list[i,2]],1) #spinflip
       end
    end #for

    mag = 0
    for j=1:nSpin
        mag += 2*spin_prop[j] - 1
    end

	return mag*mag

end #M2


text:
let _build = false
    for p in ("AutoGrad","ArgParse","Compat","JLD","Knet")
        if Pkg.installed(p) == nothing
            Pkg.add(p)
            _build = true
        end
    end
    if _build
        Pkg.build("Knet")
    end
end

max_stars_repo_name: kirnap/KU-dependencyParser

max_issues_repo_name: kirnap/KU-dependencyParser

max_forks_repo_name: kirnap/KU-dependencyParser


text:
using Test
using WorldBankData
using FilePathsBase
using DocumentFormat

@test DocumentFormat.isformatted(p".")


text:
using BinaryProvider
using Pkg

tarball_url = "https://github.com/kdheepak/FIGletFonts/archive/v0.5.0.tar.gz"
hash = "39f46c840ba035ba3b52aebf46123e6eda7393a7a18c5e6e02fb68c8cb50a33d"
prefix = Prefix(@__DIR__)

!isdefined(Pkg, :Artifacts) && !isinstalled(tarball_url, hash, prefix=prefix) && install(tarball_url, hash, prefix=prefix)

max_stars_repo_name: wookay/TestFIGlet

max_issues_repo_name: wookay/TestFIGlet

max_forks_repo_name: wookay/TestFIGlet


text:
module MatrixDepotTemplate

using LinearAlgebra
using MatrixDepot
using MatrixDepot: include_generator, FunctionName, Group, publish_user_generators

"""
random Orthogonal matrix
========================
*Input options:*
+ n: the dimension of the matrix
"""
randorth(n) = Matrix(qr(randn(n,n)).Q)

"""
random symmetric matrix
=======================
*Input options:*
+ n: the dimension of the matrix
"""
function randsym(n)
    A = zeros(n, n)
    for j = 1:n
    for i = j:n
        A[i,j] = randn()
        if i != j; A[j,i] = A[i,j] end
    end
    end
    return A
end

function __init__()
    include_generator(FunctionName, "randsym", randsym)
    include_generator(FunctionName, "randorth", randorth)
    include_generator(Group, :symmetric, randsym)
    include_generator(Group, :random, randsym)
    publish_user_generators()
end

end


text:
"""
和线有关的功能
"""

struct Segment
    pt1 :: Point2D
    pt2 :: Point2D
    length :: Float64
    Segment(pt1::Point2D, pt2::Point2D) = begin
        len = sqrt((pt1.x - pt2.x)^2 + (pt1.y - pt2.y)^2)
        new(pt1, pt2, len)
    end
end


text:
using CautiousEngine
using Test

@testset "CautiousEngine.jl" begin
    # Write your tests here.
    include("gptests.jl")
    include("dfatests.jl")
    include("serializationtests.jl")
    include("systemtests.jl")
    include("regiontests.jl")
end


text:
mutable struct Workspace

    Nx :: Int64
    Ny :: Int64
    Nz :: Int64

    T :: Array
    U :: Array
    V :: Array
    W :: Array

    sT :: Array
    sU :: Array
    sV :: Array

    ptr :: Dict
    dim_dict :: Dict

    warning_cnt :: Integer

    function Workspace(;
        Nx :: Int64,
        Ny :: Int64,
        Nz :: Int64,
        warning_cnt = 20,
    )

        _T = []
        _U = []
        _V = []
        _W = []

        _sT = []
        _sU = []
        _sV = []

        ptr = Dict(
            :T => 1,
            :U => 1,
            :V => 1,
            :W => 1,
            :sT => 1,
            :sU => 1,
            :sV => 1,
        )

        dim_dict = Dict(
            :T =>  [Nz, Nx, Ny],
            :U =>  [Nz, Nx, Ny],
            :V =>  [Nz, Nx, Ny+1],
            :W =>  [Nz+1, Nx, Ny],
            :sT => [1, Nx, Ny  ],
            :sU => [1, Nx, Ny  ],
            :sV => [1, Nx, Ny+1],
        )

        return new(
            Nx, Ny, Nz,
            _T, _U, _V, _W,
            _sT, _sU, _sV,
            ptr,
            dim_dict,
            warning_cnt,
        )

    end

end

function getSpace!(
    wksp :: Workspace,
    grid :: Symbol,
    flat :: Bool = false;
    o :: Union{Float64, Nothing} = nothing  # overwritten value
)
    i = wksp.ptr[grid]
    list = getfield(wksp, grid)

    if i > length(list)
        #println("Running out of workspace of " * string(grid) * ", create new...")
        push!(list, genEmptyGrid(wksp, Float64, grid))
        if length(list) > wksp.warning_cnt
            println(format("Warning: Now we have {:d} workspace arrays.", length(list)))
        end
    end

    wksp.ptr[grid] += 1

    arr = ( flat ) ? view(list[i], :) : list[i]

    if o != nothing
        arr .= o
    end

    return arr
end

function reset!(
    wksp :: Workspace,
    grid :: Symbol=:ALL,
)
    if grid == :ALL
        for k in keys(wksp.ptr)
            wksp.ptr[k] = 1
        end
    else
        wksp.ptr[grid] = 1
    end

end

function genEmptyGrid(
    wksp  :: Workspace,
    dtype :: DataType,
    grid  :: Symbol,
)
    Nx, Ny, Nz = wksp.Nx, wksp.Ny, wksp.Nz
    dim = wksp.dim_dict[grid]

    return zeros(dtype, dim...)

end

#=
function releaseMemory!(
    wksp :: Workspace,
)

    if grid == :ALL
        for k in keys(wksp.ptr)
            wksp.ptr[k] = 1
        end
    else
        wksp.ptr[grid] = 1
    end

end
=#

max_stars_repo_name: meteorologytoday/IOM

max_issues_repo_name: meteorologytoday/IOM

max_forks_repo_name: meteorologytoday/IOM