text:
require("Codecs")
require("Compose")
require("DataFrames")
require("DataArrays")
require("DataStructures")
require("Datetime")
require("Distributions")
require("Iterators")
require("JSON")
require("Loess")

module Gadfly

using Codecs
using Color
using Compose
using DataFrames
using DataArrays
using DataStructures
using Datetime
using JSON

import Iterators
import Iterators: distinct, drop
import Compose.draw, Compose.hstack, Compose.vstack
import Base: copy, push!, start, next, done, has, show, getindex, cat,
             writemime, isfinite, display

export Plot, Layer, Theme, Scale, Coord, Geom, Guide, Stat, render, plot,
       layer, @plot, spy, set_default_plot_size, set_default_plot_format,
       prepare_display

# Re-export some essentials from Compose
export D3, SVG, PNG, PS, PDF, draw, inch, mm, cm, px, pt, color, vstack, hstack

# Backwards compatibility with julia-0.2 names
if !isdefined(:rad2deg)
    const rad2deg = radians2degrees
    const deg2rad = degrees2radians
end

if !isdefined(:setfield!)
    const setfield! = setfield
end

typealias ColorOrNothing Union(ColorValue, Nothing)

element_aesthetics(::Any) = []
default_scales(::Any) = []
default_statistic(::Any) = Stat.identity()

abstract Element
abstract ScaleElement       <: Element
abstract CoordinateElement  <: Element
abstract GeometryElement    <: Element
abstract GuideElement       <: Element
abstract StatisticElement   <: Element

include("misc.jl")
include("format.jl")
include("ticks.jl")
include("color.jl")
include("varset.jl")
include("theme.jl")
include("aesthetics.jl")
include("data.jl")

# The layer and plot functions can also take functions that are evaluated with
# no arguments and are expected to produce an element.
typealias ElementOrFunction{T <: Element} Union(Element, Base.Callable, Theme)

# Prepare the display backend (ijuila, in particular) to show plots rendered on
# the d3 backend.

const gadfly_js = readall(joinpath(dirname(Base.source_path()), "gadfly.js"))

function prepare_display(d::Display)
    Compose.prepare_display(d)
    display(d, "text/html", """""")
end

function prepare_display()
    prepare_display(Base.Multimedia.displays[end])
end

try
    display("text/html", """""")
catch
end

# Set prefereed canvas size when rendering a plot with an explicit call to
# `draw`.
default_plot_width = 12cm
default_plot_height = 8cm

function set_default_plot_size(width::Compose.MeasureOrNumber,
                               height::Compose.MeasureOrNumber)
    global default_plot_width
    global default_plot_height
    default_plot_width = Compose.x_measure(width)
    default_plot_height = Compose.y_measure(height)
end

default_plot_format = :html

function set_default_plot_format(fmt::Symbol)
    if !(fmt in [:html, :png, :svg, :pdf, :ps])
        error("$(fmt) is not a supported plot format")
    end
    global default_plot_format
    default_plot_format = fmt
end

# A plot has zero or more layers. Layers have a particular geometry and their
# own data, which is inherited from the plot if not given.
type Layer <: Element
    data_source::Union(AbstractDataFrame, Nothing)
    mapping::Dict
    statistic::StatisticElement
    geom::GeometryElement
    theme::Union(Nothing, Theme)

    function Layer()
        new(nothing, Dict(), Stat.nil(), Geom.nil(), nothing)
    end
end

function layer(data_source::AbstractDataFrame, elements::ElementOrFunction...;
               mapping...)
    lyr = Layer()
    lyr.data_source = data_source
    lyr.mapping = clean_mapping(mapping)
    for element in elements
        add_plot_element(lyr, element)
    end
    lyr
end

function layer(elements::ElementOrFunction...; mapping...)
    lyr = Layer()
    lyr.mapping = clean_mapping(mapping)
    for element in elements
        add_plot_element(lyr, element)
    end
    lyr
end

function add_plot_element{T<:Element}(lyr::Layer, arg::T)
    error("Layers can't be used with elements of type $(typeof(arg))")
end

function add_plot_element(lyr::Layer, arg::Base.Callable)
    add_plot_element(lyr, arg())
end

function add_plot_element(lyr::Layer, arg::GeometryElement)
    lyr.geom = arg
end

function add_plot_element(lyr::Layer, arg::StatisticElement)
    lyr.statistic = arg
end

function add_plot_element(lyr::Layer, arg::Theme)
    lyr.theme = arg
end

# A full plot specification.
type Plot
    layers::Vector{Layer}
    data_source::Union(Nothing, AbstractDataFrame)
    data::Data
    scales::Vector{ScaleElement}
    statistics::Vector{StatisticElement}
    coord::CoordinateElement
    guides::Vector{GuideElement}
    theme::Theme
    mapping::Dict

    function Plot()
        new(Layer[], nothing, Data(), ScaleElement[], StatisticElement[],
            Coord.cartesian(), GuideElement[], default_theme)
    end
end

function add_plot_element(p::Plot, data::AbstractDataFrame, arg::Function)
    add_plot_element(p, data, arg())
end

function add_plot_element(p::Plot, data::AbstractDataFrame, arg::GeometryElement)
    if !isempty(p.layers) && isa(p.layers[end].geom, Geom.Nil)
        p.layers[end].geom = arg
    else
        layer = Layer()
        layer.geom = arg
        push!(p.layers, layer)
    end
end

function add_plot_element(p::Plot, data::AbstractDataFrame, arg::ScaleElement)
    push!(p.scales, arg)
end

function add_plot_element(p::Plot, data::AbstractDataFrame, arg::StatisticElement)
    if isempty(p.layers)
        push!(p.layers, Layer())
    end

    p.layers[end].statistic = arg
end

function add_plot_element(p::Plot, data::AbstractDataFrame, arg::CoordinateElement)
    p.coord = arg
end

function add_plot_element(p::Plot, data::AbstractDataFrame, arg::GuideElement)
    push!(p.guides, arg)
end

function add_plot_element(p::Plot, data::AbstractDataFrame, arg::Layer)
    push!(p.layers, arg)
end

function add_plot_element{T <: Element}(p::Plot, data::AbstractDataFrame, f::Type{T})
    add_plot_element(p, data, f())
end

function add_plot_element(p::Plot, ::AbstractDataFrame, theme::Theme)
    p.theme = theme
end

# Evaluate a plot mapping, and update the Data structure appropriately.
#
# Args:
#   data: Data object to be updated.
#   data_source: data frame in which context of which the mapping is evaluated.
#   k: key
#   v: value
#
# Modifies:
#   data
#
function set_mapped_data!(data::Data, data_source::AbstractDataFrame, k::Symbol, v)
    setfield!(data, k, eval_plot_mapping(data_source, v))

    if isa(v, String) || isa(v, Symbol)
        data.titles[k] = string(v)
    else
        data.titles[k] = string(k)
    end
end

# Handle aesthetics aliases and warn about unrecognized aesthetics.
#
# Returns:
#   A new mapping with aliases evaluated and unrecognized aesthetics removed.
#
function clean_mapping(mapping)
    cleaned = Dict{Symbol, AestheticValue}()
    for (key, val) in mapping
        if haskey(aesthetic_aliases, key)
            key = aesthetic_aliases[key]
        elseif !in(key, Aesthetics.names)
            warn("$(string(key)) is not a recognized aesthetic. Ignoring.")
            continue
        end

        if !(typeof(val) <: AestheticValue)
            error(
            """Aesthetic $(key) is mapped to a value of type $(typeof(val)).
               It must be mapped to a string, symbol, array, or expression.""")
        end

        cleaned[key] = val
    end
    cleaned
end

# Evaluate a mapping.
eval_plot_mapping(data::AbstractDataFrame, arg::Symbol) = data[arg]
eval_plot_mapping(data::AbstractDataFrame, arg::String) = eval_plot_mapping(data, symbol(arg))
eval_plot_mapping(data::AbstractDataFrame, arg::Integer) = data[arg]
eval_plot_mapping(data::AbstractDataFrame, arg::Expr) = with(data, arg)
eval_plot_mapping(data::AbstractDataFrame, arg::AbstractArray) = arg

# Acceptable types of values that can be bound to aesthetics.
typealias AestheticValue Union(Nothing, Symbol, String, Integer, Expr,
                               AbstractArray)

# Create a new plot.
#
# Grammar of graphics style plotting consists of specifying a dataset, one or
# more plot elements (scales, coordinates, geometries, etc), and binding of
# aesthetics to columns or expressions of the dataset.
#
# For example, a simple scatter plot would look something like:
#
#     plot(my_data, Geom.point, x="time", y="price")
#
# Where "time" and "price" are the names of columns in my_data.
#
# Args:
#   data_source: Data to be bound to aesthetics.
#   mapping: Aesthetics symbols (e.g. :x, :y, :color) mapped to
#            names of columns in the data frame or other expressions.
#   elements: Geometries, statistics, etc.

function plot(data_source::AbstractDataFrame, elements::ElementOrFunction...; mapping...)
    p = Plot()
    p.mapping = clean_mapping(mapping)
    p.data_source = data_source
    for (k, v) in p.mapping
        set_mapped_data!(p.data, data_source, k, v)
    end

    for element in elements
        add_plot_element(p, data_source, element)
    end

    p
end

function plot(elements::ElementOrFunction...; mapping...)
    plot(DataFrame(), elements...; mapping...)
end

# The old fashioned (pre named arguments) version of plot.
#
# This version takes an explicit mapping dictionary, mapping aesthetics symbols
# to expressions or columns in the data frame.
#
# Args:
#   data_source: Data to be bound to aesthetics.
#   mapping: Dictionary of aesthetics symbols (e.g. :x, :y, :color) to
#            names of columns in the data frame or other expressions.
#   elements: Geometries, statistics, etc.
#
# Returns:
#   A Plot object.
#
function plot(data_source::AbstractDataFrame, mapping::Dict, elements::ElementOrFunction...)
    p = Plot()
    for element in elements
        add_plot_element(p, data_source, element)
    end

    for (var, value) in mapping
        set_mapped_data!(p.data, data_source, var, value)
    end
    p.mapping = mapping
    p.data_source = data_source

    p
end

include("poetry.jl")

# Turn a graph specification into a graphic.
#
# This is where magic happens (sausage is made). Processing all the parts of the
# plot is actually pretty simple. It's made complicated by trying to handle
# defaults. With that aside, plots are made in the following steps.
#
#    I. Apply scales to transform raw data to the form expected by the aesthetic.
#   II. Apply statistics to the scaled data. Statistics are essentially functions
#       that map one or more aesthetics to one or more aesthetics.
#  III. Apply coordinates. Currently all this does is figure out the coordinate
#       system used by the plot panel canvas.
#   IV. Render geometries. This gives us one or more compose forms suitable to be
#       composed with the plot's panel.
#    V. Render guides. Guides are conceptually very similar to geometries but with
#       the ability to be placed outside of the plot panel.
#
#  Finally there is a very important call to layout_guides which puts everything
#  together.
#
# Args:
#   plot: a plot to render.
#
# Returns:
#   A compose Canvas containing the graphic.
#
function render(plot::Plot)
    if isempty(plot.layers)
        layer = Layer()
        layer.geom = Geom.point()
        push!(plot.layers, layer)
    end

    # Process layers, filling inheriting mappings or data from the Plot where
    # they are missing.
    datas = Array(Data, length(plot.layers))
    for (i, layer) in enumerate(plot.layers)
        if layer.data_source === nothing && isempty(layer.mapping)
            datas[i] = plot.data
        else
            datas[i] = Data()

            if layer.data_source === nothing
                layer.data_source = plot.data_source
            end

            if isempty(layer.mapping)
                layer.mapping = plot.mapping
            end

            for (k, v) in layer.mapping
                setfield!(datas[i], k, eval_plot_mapping(layer.data_source, v))
            end
        end
    end

    # Add default statistics for geometries.
    layer_stats = Array(StatisticElement, length(plot.layers))
    for (i, layer) in enumerate(plot.layers)
        layer_stats[i] = typeof(layer.statistic) == Stat.nil ?
            default_statistic(layer.geom) : layer.statistic
    end

    used_aesthetics = Set{Symbol}()
    for layer in plot.layers
        union!(used_aesthetics, element_aesthetics(layer.geom))
    end

    for stat in layer_stats
        union!(used_aesthetics, element_aesthetics(stat))
    end

    defined_unused_aesthetics = setdiff(set(keys(plot.mapping)), used_aesthetics)
    if !isempty(defined_unused_aesthetics)
        warn("The following aesthetics are mapped, but not used by any geometry:\n    ",
             join([string(a) for a in defined_unused_aesthetics], ", "))
    end

    scaled_aesthetics = Set{Symbol}()
    for scale in plot.scales
        union!(scaled_aesthetics, element_aesthetics(scale))
    end

    # Only one scale can be applied to an aesthetic (without getting some weird
    # and incorrect results), so we organize scales into a dict.
    scales = Dict{Symbol, ScaleElement}()
    for scale in plot.scales
        for var in element_aesthetics(scale)
            scales[var] = scale
        end
    end

    unscaled_aesthetics = setdiff(used_aesthetics, scaled_aesthetics)

    # Add default scales for statistics.
    for stat in layer_stats
        for scale in default_scales(stat)
            # Use the statistics default scale only when it covers some
            # aesthetic that is not already scaled.
            scale_aes = set(element_aesthetics(scale))
            if !isempty(intersect(scale_aes, unscaled_aesthetics))
                for var in scale_aes
                    scales[var] = scale
                end
                setdiff!(unscaled_aesthetics, scale_aes)
            end
        end
    end

    # Assign scales to mapped aesthetics first.
    for var in unscaled_aesthetics
        if !haskey(plot.mapping, var)
            continue
        end

        t = classify_data(getfield(plot.data, var))

        if haskey(default_aes_scales[t], var)
            scale = default_aes_scales[t][var]
            scale_aes = set(element_aesthetics(scale))
            for var in scale_aes
                scales[var] = scale
            end
        end
    end

    for var in unscaled_aesthetics
        if haskey(plot.mapping, var) || haskey(scales, var)
            continue
        end

        t = :categorical
        for data in datas
            val = getfield(data, var)
            if val != nothing
                t = classify_data(val)
                break
            end
        end

        if haskey(default_aes_scales[t], var)
            scale = default_aes_scales[t][var]
            scale_aes = set(element_aesthetics(scale))
            for var in scale_aes
                scales[var] = scale
            end
        end
    end

    # Avoid clobbering user-defined guides with default guides (e.g.
    # in the case of labels.)
    guides = copy(plot.guides)
    explicit_guide_types = Set()
    for guide in guides
        push!(explicit_guide_types, typeof(guide))
    end

    statistics = Set{StatisticElement}()
    for statistic in plot.statistics
        push!(statistics, statistic)
    end

    # Default guides and statistics
    facet_plot = true
    for layer in plot.layers
        if typeof(layer.geom) != Geom.subplot_grid
            facet_plot = false
            break
        end
    end

    if !facet_plot
        if !in(Guide.PanelBackground, explicit_guide_types)
            push!(guides, Guide.background())
        end

        if !in(Guide.ZoomSlider, explicit_guide_types)
            push!(guides, Guide.zoomslider())
        end

        if !in(Guide.XTicks, explicit_guide_types)
            push!(guides, Guide.xticks())
        end

        if !in(Guide.YTicks, explicit_guide_types)
            push!(guides, Guide.yticks())
        end
    end

    for guide in guides
        push!(statistics, default_statistic(guide))
    end

    function mapped_and_used(vs)
        any([haskey(plot.mapping, v) && in(v, used_aesthetics) for v in vs])
    end

    function choose_name(vs, fallback)
        for v in vs
            if haskey(plot.data.titles, v)
                return plot.data.titles[v]
            end
        end
        fallback
    end

    if mapped_and_used(x_axis_label_aesthetics) &&
        !in(Guide.XLabel, explicit_guide_types)
        label = choose_name(x_axis_label_aesthetics, "x")
        if facet_plot && haskey(plot.data.titles, :xgroup)
            label = string(label, " by ", plot.data.titles[:xgroup])
        end

        push!(guides, Guide.xlabel(label))
    end

    if mapped_and_used(y_axis_label_aesthetics) &&
       !in(Guide.YLabel, explicit_guide_types)
        label = choose_name(y_axis_label_aesthetics, "y")
        if facet_plot && haskey(plot.data.titles, :ygroup)
            label = string(label, " by ", plot.data.titles[:ygroup])
        end

        push!(guides, Guide.ylabel(label))
    end

    # I. Scales
    layer_aess = Scale.apply_scales(Iterators.distinct(values(scales)), datas...)

    # set default labels
    for (i, layer) in enumerate(plot.layers)
        if layer_aess[i].color_key_title == nothing &&
           haskey(layer.mapping, :color) &&
           !isa(layer.mapping[:color], AbstractArray)
           layer_aess[i].color_key_title = string(layer.mapping[:color])
       end
    end

    if layer_aess[1].color_key_title == nothing &&
       haskey(plot.mapping, :color) && !isa(plot.mapping[:color], AbstractArray)
        layer_aess[1].color_key_title = string(plot.mapping[:color])
    end

    # IIa. Layer-wise statistics
    for (layer_stat, aes) in zip(layer_stats, layer_aess)
        Stat.apply_statistics(StatisticElement[layer_stat], scales, plot.coord, aes)
    end

    # IIb. Plot-wise Statistics
    plot_aes = cat(layer_aess...)
    statistics = collect(statistics)
    Stat.apply_statistics(statistics, scales, plot.coord, plot_aes)

    # Add some default guides determined by defined aesthetics
    if !all([aes.color === nothing for aes in [plot_aes, layer_aess...]]) &&
       !in(Guide.ColorKey, explicit_guide_types)
        push!(guides, Guide.colorkey())
    end

    canvas = render_prepared(plot, plot_aes, layer_aess, layer_stats, scales,
                             statistics, guides)

    pad_inner(canvas, 5mm)
end

# Render a plot given a precomputed Aesthetics object for each layer.
#
# Additionally, without all the work to choose reasonable defaults performed by
# `render`. This is a separate function from `render` to facilitate rendering
# subplots.
#
# Args:
#   plot: Plot to be rendered.
#   aess: A vector of precomputed Aesthetics objects of the same length
#       as plot.layers.
#   layer_stats: A vector of statistic elements of the same length as
#       plot.layers.
#   scales: Dictionary mapping an aesthetics symbol to the scale applied to it.
#   statistics: Statistic elements applied plot-wise.
#   guides: Guide elements indexed by type. (Only one type of each guide may
#       be in the same plot.)
#   preserve_plot_canvas_size: Don't squish the plot to fit the guides.
#       Guides will be drawn outside the canvas
#
# Returns:
#   A Compose canvas containing the rendered plot.
#
function render_prepared(plot::Plot,
                         plot_aes::Aesthetics,
                         layer_aess::Vector{Aesthetics},
                         layer_stats::Vector{StatisticElement},
                         scales::Dict{Symbol, ScaleElement},
                         statistics::Vector{StatisticElement},
                         guides::Vector{GuideElement};
                         preserve_plot_canvas_size=false)

    # III. Coordinates
    plot_canvas = Coord.apply_coordinate(plot.coord, plot_aes, layer_aess...)

    # IV. Geometries
    themes = Theme[layer.theme === nothing ? plot.theme : layer.theme
                   for layer in plot.layers]
    plot_canvas = compose(plot_canvas,
                          [render(layer.geom, theme, aes)
                           for (layer, aes, theme) in zip(plot.layers, layer_aess, themes)]...)

    # V. Guides
    guide_canvases = {}
    for guide in guides
        guide_canvas = render(guide, plot.theme, plot_aes)
        if guide_canvas != nothing
            append!(guide_canvases, guide_canvas)
        end
    end

    canvas = Guide.layout_guides(plot_canvas, plot.theme, guide_canvases...,
                                 preserve_plot_canvas_size=preserve_plot_canvas_size)

    class = "plotroot"
    if haskey(scales, :x) && isa(scales[:x], Scale.ContinuousScale)
        class = string(class, " xscalable")
    end
    if haskey(scales, :y) && isa(scales[:y], Scale.ContinuousScale)
        class = string(class, " yscalable")
    end

    compose(canvas, svgclass(class))
end

# A convenience version of Compose.draw that let's you skip the call to render.
function draw(backend::Compose.Backend, p::Plot)
    draw(backend, render(p))
end

# Convenience stacking functions
vstack(ps::Plot...) = vstack([render(p) for p in ps]...)
vstack(ps::Vector{Plot}) = vstack([render(p) for p in ps]...)

hstack(ps::Plot...) = hstack([render(p) for p in ps]...)
hstack(ps::Vector{Plot}) = hstack([render(p) for p in ps]...)

# writemime functions for all supported compose backends.

function writemime(io::IO, m::MIME"text/html", p::Plot)
    buf = IOBuffer()
    d3 = D3(buf, default_plot_width, default_plot_height, false)
    draw(d3, p)
    writemime(io, m, d3)
end

function writemime(io::IO, m::MIME"image/svg+xml", p::Plot)
    buf = IOBuffer()
    svg = SVG(buf, default_plot_width, default_plot_height, false)
    draw(svg, p)
    writemime(io, m, svg)
end

try
    getfield(Compose, :Cairo) # throws if Cairo isn't being used
    function writemime(io::IO, ::MIME"image/png", p::Plot)
        draw(PNG(io, default_plot_width, default_plot_height), p)
    end
end

try
    getfield(Compose, :Cairo) # throws if Cairo isn't being used
    function writemime(io::IO, ::MIME"application/postscript", p::Plot)
        draw(PS(io, default_plot_width, default_plot_height), p)
    end
end

# TODO: the serializeable branch has to be merged before this will work.
#function writemime(io::IO, ::MIME"application/json", p::Plot)
    #JSON.print(io, serialize(p, with_data=true))
#end

function writemime(io::IO, ::MIME"text/plain", p::Plot)
    write(io, "Plot(...)")
end

function default_mime()
    if default_plot_format == :png
        "image/png"
    elseif default_plot_format == :svg
        "image/svg+xml"
    elseif default_plot_format == :html
        "text/html"
    elseif default_plot_format == :ps
        "application/postscript"
    elseif default_plot_format == :pdf
        "application/pdf"
    else
        ""
    end
end

if isdefined(Base, :REPL)
    import Base.Multimedia: @try_display, xdisplayable
    const REPLDisplay = Base.REPL.REPLDisplay

    function display(p::Plot)
        displays = Base.Multimedia.displays
        for i = length(displays):-1:1
            m = default_mime()

            # TODO: What we really want is the try_display macro, but that's not
            # defined in 0.2 and causes an error even though this branch
            # isn't executed in 0.2.
            if xdisplayable(displays[i], m, p)
                try
                    return display(displays[i], m, p)
                catch e
                    isa(e, MethodError) && e.f in (display, redisplay, writemime) ||
                        rethrow()
                end
            end

            if xdisplayable(displays[i], p)
                try
                    return display(displays[i], p)
                catch e
                    isa(e, MethodError) && e.f in (display, redisplay, writemime) ||
                        rethrow()
                end
            end
        end
        invoke(display,(Any,),p)
    end
else
    # julia 0.2 fallback
    const REPLDisplay = TextDisplay

    function display(d::TextDisplay, p::Plot)
        m = default_mime()
        display(d, m, p)
    end
end

function open_file(filename)
    if OS_NAME == :Darwin
        run(`open $(filename)`)
    elseif OS_NAME == :Linux || OS_NAME == :FreeBSD
        run(`xdg-open $(filename)`)
    elseif OS_NAME == :Windows
        run(`$(ENV["COMSPEC"]) /c start $(filename)`)
    else
        warn("Showing plots is not supported on OS $(string(OS_NAME))")
    end
end

# Fallback display method. When there isn't a better option, we write to a
# temporary file and try to open it.
function display(d::REPLDisplay, ::MIME"image/png", p::Plot)
    filename = string(tempname(), ".png")
    output = open(filename, "w")
    draw(PNG(output, default_plot_width, default_plot_height), p)
    close(output)
    open_file(filename)
end

function display(d::REPLDisplay, ::MIME"image/svg+xml", p::Plot)
    filename = string(tempname(), ".svg")
    output = open(filename, "w")
    draw(SVG(output, default_plot_width, default_plot_height), p)
    close(output)
    open_file(filename)
end

function display(d::REPLDisplay, ::MIME"text/html", p::Plot)
    filename = string(tempname(), ".html")
    output = open(filename, "w")

    plot_output = IOBuffer()
    draw(D3(plot_output, default_plot_width, default_plot_height, false), p)
    plot_js = takebuf_string(plot_output)

    write(output,
        """
        <!DOCTYPE html>

            <title>Gadfly Plot</title>

        """)
    close(output)
    open_file(filename)
end

function display(d::REPLDisplay, ::MIME"application/postscript", p::Plot)
    filename = string(tempname(), ".ps")
    output = open(filename, "w")
    draw(PS(output, default_plot_width, default_plot_height), p)
    close(output)
    open_file(filename)
end

function display(d::REPLDisplay, ::MIME"application/pdf", p::Plot)
    filename = string(tempname(), ".pdf")
    output = open(filename, "w")
    draw(PDF(output, default_plot_width, default_plot_height), p)
    close(output)
    open_file(filename)
end

include("scale.jl")
include("coord.jl")
include("geometry.jl")
include("guide.jl")
include("statistics.jl")

# All aesthetics must have a scale. If none is given, we use a default.
# The default depends on whether the input is discrete or continuous (i.e.,
# PooledDataVector or DataVector, respectively).
const default_aes_scales = {
        :functional => {:func => Scale.func()},
        :numerical => {:x           => Scale.x_continuous(),
                       :xmin        => Scale.x_continuous(),
                       :xmax        => Scale.x_continuous(),
                       :xintercept  => Scale.x_continuous(),
                       :y           => Scale.y_continuous(),
                       :ymin        => Scale.y_continuous(),
                       :ymax        => Scale.y_continuous(),
                       :yintercept  => Scale.y_continuous(),
                       :middle      => Scale.y_continuous(),
                       :upper_fence => Scale.y_continuous(),
                       :lower_fence => Scale.y_continuous(),
                       :upper_hinge => Scale.y_continuous(),
                       :lower_hinge => Scale.y_continuous(),
                       :xgroup      => Scale.xgroup(),
                       :ygroup      => Scale.ygroup(),
                       :color       => Scale.continuous_color(),
                       :label       => Scale.label(),
                       :size        => Scale.size_continuous()},
        :categorical => {:x          => Scale.x_discrete(),
                         :xmin       => Scale.x_discrete(),
                         :xmax       => Scale.x_discrete(),
                         :xintercept => Scale.x_discrete(),
                         :y          => Scale.y_discrete(),
                         :ymin       => Scale.y_discrete(),
                         :ymax       => Scale.y_discrete(),
                         :yintercept => Scale.y_discrete(),
                         :xgroup     => Scale.xgroup(),
                         :ygroup     => Scale.ygroup(),
                         :color      => Scale.discrete_color(),
                         :label      => Scale.label()}}

# Determine whether the input is categorical or numerical

typealias CategoricalType Union(String, Bool, Symbol)

function classify_data{N, T <: CategoricalType}(data::AbstractArray{T, N})
    :categorical
end

function classify_data{N, T <: Base.Callable}(data::AbstractArray{T, N})
    :functional
end

function classify_data(data::AbstractArray{Any})
    for val in data
        if isa(val, CategoricalType)
            return :categorical
        end
    end
    :numerical
end

function classify_data(data::AbstractArray)
    :numerical
end

# Axis labels are taken whatever is mapped to these aesthetics, in order of
# preference.
const x_axis_label_aesthetics = [:x, :xmin, :xmax]
const y_axis_label_aesthetics = [:y, :ymin, :ymax]

end # module Gadfly


text:
using Base.Test
using QuantumOptics

@testset "stochastic_schroedinger" begin

b_spin = SpinBasis(1//2)
sz = sigmaz(b_spin)
sm = sigmam(b_spin)
sp = sigmap(b_spin)
zero_op = 0*sz
γ = 0.1
noise_op = 0.5γ*sz

H = γ*(sp + sm)
Hs = [noise_op]

ψ0 = spindown(b_spin)
ρ0 = dm(ψ0)

dt = 1/30.0
T = [0:0.1:1;]
T_short = [0:dt:dt;]

# Test equivalence of stochastic schroedinger phase noise and master dephasing
Ntraj = 100
ρ_avg = [0*ρ0 for i=1:length(T)]
for i=1:Ntraj
    t, ψt = stochastic.schroedinger(T, ψ0, H, Hs; dt=1e-3)
    ρ_avg += dm.(ψt)./Ntraj
end
tout, ρt = timeevolution.master(T, ρ0, H, [sz]; rates=[0.25γ^2])

for i=1:length(tout)
    @test tracedistance(ρ_avg[i], ρt[i]) < dt
end

# Function definitions for schroedinger_dynamic
function fdeterm(t, psi)
    H
end
function fstoch_1(t, psi)
    [zero_op]
end
function fstoch_2(t, psi)
    [zero_op, zero_op, zero_op]
end
function fstoch_3(t, psi)
    noise_op, noise_op
end

# Non-dynamic Schrödinger
tout, ψt4 = stochastic.schroedinger(T, ψ0, H, [zero_op, zero_op]; dt=dt)
tout, ψt3 = stochastic.schroedinger(T, ψ0, H, zero_op; dt=dt)
# Dynamic Schrödinger
tout, ψt1 = stochastic.schroedinger_dynamic(T, ψ0, fdeterm, fstoch_1; dt=dt)
tout, ψt2 = stochastic.schroedinger_dynamic(T, ψ0, fdeterm, fstoch_2; dt=dt, noise_processes=3)

# Test equivalence to Schrödinger equation with zero noise
# Test sharp equality for same algorithms
@test ψt1 == ψt3
@test ψt2 == ψt4

tout, ψt_determ = timeevolution.schroedinger_dynamic(T, ψ0, fdeterm)
# Test approximate equality for different algorithms
for i=1:length(tout)
    @test norm(ψt1[i] - ψt2[i]) < dt
    @test norm(ψt1[i] - ψt_determ[i]) < dt
end

# Test remaining function calls for short times to test whether they work in principle
tout, ψt = stochastic.schroedinger(T_short, ψ0, H, noise_op; dt=dt)
tout, ψt = stochastic.schroedinger_dynamic(T_short, ψ0, fdeterm, fstoch_3; dt=dt)

end # testset

ext: jl


text:
"""
Half pipe of aluminum, with an open through crack.

The pipe corner is excited by a concentrated force in the form of a
Hann-windowed sinusoidal pulse.

The crack configuration is not exactly the same one as in the book.
It is not described unambiguously in the book.

Reference:
Ostachowicz W, Kudela P, Krawczuk M, Zak A. Guided Waves in Structures for SHM: The Time - domain Spectral Element Method. A John Wiley & Sons, Ltd., 2011.
"""
module cracked_half_pipe_examples

using LinearAlgebra
using SparseArrays
using Arpack
using FinEtools
using FinEtoolsDeforLinear
using FinEtoolsFlexStructures
using FinEtoolsFlexStructures.FESetShellT3Module: FESetShellT3
using FinEtoolsFlexStructures.AssemblyModule
using FinEtoolsFlexStructures.FEMMShellT3FFModule
using FinEtoolsFlexStructures.RotUtilModule: initial_Rfield, update_rotation_field!
using SymRCM
using VisualStructures: default_layout_3d, plot_nodes, plot_midline, render, plot_space_box, plot_midsurface, space_aspectratio, save_to_json
using PlotlyJS
using Gnuplot;  @gp "clear"
using FinEtools.MeshExportModule.VTKWrite: vtkwritecollection
using ThreadedSparseCSR
using UnicodePlots
using InteractiveUtils
using BenchmarkTools
using FinEtools.MeshExportModule.VTKWrite: vtkwritecollection, vtkwrite

const E = 72.7*phun("GPa");
const nu = 0.33;
const rho = 2700.0*phun("kg/m^3")
const d1 = pi*phun("rad")
const d2 = 6.0/180*pi*phun("rad")
const d5 = (180-40.0)/180*pi*phun("rad") - d2/2
const d6 = d1 - d5 - d2
const nd5 = 19
const nd2 = 2
const nd6 = 2
const nd3 = 25
const nd4 = 25
const halfcirc = 500*phun("mm")
const radius = halfcirc / pi
const d3 = 1000*phun("mm")
const d4 = d3/2
const thickness = 10*phun("mm")
const ksi = 0.0
const omegad = 1000*phun("Hz")
const carrier_frequency = 75*phun("kilo*Hz")
const modulation_frequency = carrier_frequency/4
const totalforce = 1*phun("N")
const forcepatchradius = 3*phun("mm")
const forcedensity = totalforce/(pi*forcepatchradius^2)
const tend = 0.5*phun("milli*s")
const visualize = true
const visualizeclear = true
const visualizevtk = !true
const distributedforce = !true

function _pwr(K, M, maxit = 30, rtol = 1/10000)
    invM = fill(0.0, size(M, 1))
    invM .= 1.0 ./ (vec(diag(M)))
    v = rand(size(M, 1))
    w = fill(0.0, size(M, 1))
    everyn = Int(round(maxit / 50)) + 1
    lambda = lambdap = 0.0
    for i in 1:maxit
        ThreadedSparseCSR.bmul!(w, K, v)
        wn = norm(w)
        w .*= (1.0/wn)
        v .= invM .* w
        vn = norm(v)
        v .*= (1.0/vn)
        if i % everyn  == 0
            lambda = sqrt((v' * (K * v)) / (v' * M * v))
            # @show i, abs(lambda - lambdap) / lambda
            if abs(lambda - lambdap) / lambda  < rtol
                break
            end
            lambdap = lambda
        end
    end
    return lambda
end

function _cd_loop!(M, K, ksi, U0, V0, tend, dt, force!, peek)
    # Central difference integration loop, for mass-proportional Rayleigh damping.
    U = similar(U0)
    V = similar(U0)
    A = similar(U0)
    F = similar(U0)
    E = similar(U0)
    C = similar(U0)
    C .= (ksi*2*omegad) .* vec(diag(M))
    invMC = similar(U0)
    invMC .= 1.0 ./ (vec(diag(M)) .+ (dt/2) .* C)
    nsteps = Int64(round(tend/dt))
    if nsteps*dt < tend
        dt = tend / (nsteps+1)
    end
    dt2_2 = ((dt^2)/2)
    dt_2 = (dt/2)

    t = 0.0
    @. U = U0; @. V = V0; # Initial Conditions
    A .= invMC .* force!(F, t); # Compute initial acceleration
    peek(0, U, V, t)
    for step in 1:nsteps
        t = t + dt
        @. U += dt*V + dt2_2*A;   # Displacement update
        force!(F, t);  # External forces are computed
        ThreadedSparseCSR.bmul!(E, K, U)  # Evaluate elastic restoring forces
        @. F -= E + C * (V + dt_2 * A)  # Calculate total force
        @. V += dt_2 * A; # Update the velocity, part one: add old acceleration
        @. A = invMC * F # Compute the acceleration
        @. V += dt_2 * A; # Update the velocity, part two: add new acceleration
        peek(step, U, V, t)
    end
end

function _execute(nref = 2, color = "red")
    tolerance = min(d2/nd2, d3/nd3, d4/nd4, d5/nd5)/nref/10

    xs = sort(unique(vcat(
        linearspace(0.0, d5, nd5),
        linearspace(d5, d5+d2, nd2),
        linearspace(d5+d2, d1, nd6)
        )))
    ys = collect(linearspace(0.0, d4, nd4))

    fens1, fes1 = T3blockx(xs, ys); # Mesh
    for r in 1:nref
        fens1, fes1 = T3refine(fens1, fes1)
    end

    ys = collect(linearspace(d4, d3, nd3))

    fens2, fes2 = T3blockx(xs, ys); # Mesh
    for r in 1:nref
        fens2, fes2 = T3refine(fens2, fes2)
    end

    fens, fes1, fes2 = mergemeshes(fens1, fes1,  fens2, fes2, 0.0)
    fes = cat(fes1, fes2)

    offset = min(d2 / nd2 / nref / 10, 4*tolerance)
    l1 = selectnode(fens; box = [0 d5+offset d4 d4], inflate = tolerance)
    l2 = selectnode(fens; box = [d5+d2-offset d1 d4 d4], inflate = tolerance)
    la = selectnode(fens; box = [-Inf Inf d4 d4], inflate = tolerance)

    candidates = vcat(l1, l2)
    fens, fes = mergenodes(fens, fes, tolerance, candidates)
    bfes = meshboundary(fes)
    @info "Mesh $(count(fens)) nodes, $(count(fes)) elements"

    fens.xyz = xyz3(fens)

    for i in 1:count(fens)
        a, y = fens.xyz[i, 1:2]
        fens.xyz[i, :] .= cos(pi-a)*radius, y, sin(pi-a)*radius
    end

    vtkwrite("cracked_half_pipe.vtu", fens, fes)
    vtkwrite("cracked_half_pipe-boundary.vtu", fens, bfes)

    # Renumber the nodes
    femm = FEMMBase(IntegDomain(fes, TriRule(1)))
    C = connectionmatrix(femm, count(fens))
    perm = symrcm(C)

    mater = MatDeforElastIso(DeforModelRed3D, rho, E, nu, 0.0)

    sfes = FESetShellT3()
    accepttodelegate(fes, sfes)
    femm = FEMMShellT3FFModule.make(IntegDomain(fes, TriRule(1), thickness), mater)
    # Set up
    femm.drilling_stiffness_scale = 1.0

    # Construct the requisite fields, geometry and displacement
    # Initialize configuration variables
    geom0 = NodalField(fens.xyz)
    u0 = NodalField(zeros(size(fens.xyz,1), 3))
    Rfield0 = initial_Rfield(fens)
    dchi = NodalField(zeros(size(fens.xyz,1), 6))

    # No EBC's
    applyebc!(dchi)
    numberdofs!(dchi, perm);
    # numberdofs!(dchi);

    # Assemble the system matrix
    FEMMShellT3FFModule.associategeometry!(femm, geom0)
    SM = FinEtoolsFlexStructures.AssemblyModule
    K = FEMMShellT3FFModule.stiffness(femm, SM.SysmatAssemblerSparseCSRSymm(0.0), geom0, u0, Rfield0, dchi);
    M = FEMMShellT3FFModule.mass(femm, SysmatAssemblerSparseDiag(), geom0, dchi);

    omega_max = _pwr(K, M, Int(round(count(fens) / 1000)))
    omega_max = max(omega_max, 20*2*pi*carrier_frequency)
    @show dt = Float64(0.99 * 2/omega_max)

    U0 = gathersysvec(dchi)
    V0 = deepcopy(U0)

    mpoint = selectnode(fens; nearestto=[radius 0 0.0])[1]
    cpoint = selectnode(fens; nearestto=[radius d3 0])[1]

    mpointdof1 = dchi.dofnums[mpoint, 1]
    mpointdof3 = dchi.dofnums[mpoint, 3]
    cpointdof = dchi.dofnums[cpoint, 3]

    # Four cycles of the carrier frequency

    function computetrac!(forceout::FFltVec, XYZ::FFltMat, tangents::FFltMat, fe_label::FInt)
        dx = XYZ[1] - fens.xyz[mpoint, 1]
        dy = XYZ[2] - fens.xyz[mpoint, 2]
        dz = XYZ[3] - fens.xyz[mpoint, 3]
        d = (dx^2+dy^2+dz^2)/forcepatchradius
        forceout[1:3] .= 0.0
        if d < 1.0
            forceout[1] = forcedensity*exp(-20*d^2/forcepatchradius^2)
        end
        forceout[4:6] .= 0.0
        return forceout
    end

    if distributedforce
        lfemm = FEMMBase(IntegDomain(fes, TriRule(6)))
        fi = ForceIntensity(FFlt, 6, computetrac!);
        Fmag = distribloads(lfemm, geom0, dchi, fi, 2);
    else
        Fmag = fill(0.0, dchi.nfreedofs)
        Fmag[mpointdof1] = -totalforce/4 # only a quarter of the plate is modeled
    end

    function force!(F, t)
        mul = 0.0
        if t <= 1/modulation_frequency
            mul = 0.5 * (1 - cos(2*pi*modulation_frequency*t)) * sin(2*pi*carrier_frequency*t)
        end
        F .= mul .* Fmag
        return F
    end

    nsteps = Int(round(tend/dt))
    cdeflections = fill(0.0, nsteps+1)
    mdeflections = fill(0.0, nsteps+1)
    displacements = []
    nbtw = Int(round(nsteps/100))

    peek(step, U, V, t) = begin
        cdeflections[step+1] = U[cpointdof]
        mdeflections[step+1] = U[mpointdof1]
        if rem(step+1, nbtw) == 0
            push!(displacements, deepcopy(U))
        end
        nothing
    end

    @info "$nsteps steps"
    _cd_loop!(M, K, ksi, U0, V0, nsteps*dt, dt, force!, peek)

    if visualize
        # @gp  "set terminal windows 0 "  :-
        # @gp "clear"
        @gp  :- collect(0.0:dt:(nsteps*dt)) cdeflections " lw 2 lc rgb '$color' with lines title 'Deflection at the center' "  :-
        @gp  :- collect(0.0:dt:(nsteps*dt)) mdeflections " lw 4 lc rgb '$color' with lines title 'Deflection at the source' "  :-
        @gp  :- "set xlabel 'Time'" :-
        @gp  :- "set ylabel 'Deflection'" :-
        @gp  :- "set title 'Free-floating plate'"

        # Visualization
        @info "Dumping visualization"
        times = Float64[]
        vectors = []
        for i in 1:length(displacements)
            scattersysvec!(dchi, displacements[i])
            push!(vectors, ("U", deepcopy(dchi.values[:, 1:3])))
            push!(times, i*dt*nbtw)
        end
        vtkwritecollection("cracked_half_pipe_$nref", fens, fes, times; vectors = vectors)
    end
end

function test(nrefs = [4], color = "red")
    @info "Cracked half pipe, nrefs = $nrefs: parallel CSR"
    for nref in nrefs
        _execute(nref, color)
    end
    return true
end

function allrun(nrefs = [4], color = "blue")
    println("#####################################################")
    println("# test ")
    test(nrefs, color)
    return true
end # function allrun

allrun()

end # module
nothing


text:
addIntegratorMedium_! =
Dict{String,Function}(
    "FTCS"=>addIntegratorMediumFTCS_!,
    "Lax"=>addIntegratorMediumFTCS_!,
    "Leapfrog"=>addIntegratorMediumFTCS_!,
)

max_stars_repo_name: gatocor/embryogenesisJulia

max_issues_repo_name: gatocor/embryogenesisJulia

max_forks_repo_name: gatocor/embryogenesisJulia


text:
export BayesModelComb, bmc_sample_psdd

using Distributions: Dirichlet
using ..Utils: sample_vtree

"Bayesian Model Combination."
mutable struct BayesModelComb{T <: ProbCircuit}
    E::Vector{Ensemble{T}}
    W::Vector{Float64}
end

"Constructs a SamplePSDD BMC with `q*t` combinations, each with `n` models."
function bmc_sample_psdd(n::Integer, ϕ::Bdd, k::Integer, D::DataFrame, q::Integer, t::Integer;
        reuse::Union{Vector{StructProbCircuit}, Nothing} = nothing, vtree_bias::Real = -1.0,
        α::Union{Vector{Float64}, Nothing} = nothing, verbose::Bool = true, kwargs...)::BayesModelComb{StructProbCircuit}
    if isnothing(α) α = ones(n) end
    K = q*t
    M = K*n
    v = ncol(D)
    if isnothing(reuse)
        circs = Vector{StructProbCircuit}(undef, M)
        verbose && print("Sampling ", M, " PSDDs...\n  ")
        @qthreads for i ∈ 1:M
            circs[i] = sample_psdd(ϕ, sample_vtree(v, vtree_bias), k, D; kwargs...)
            verbose && print('*')
        end
        verbose && print('\n')
    else circs = reuse end
    E = Vector{Ensemble{StructProbCircuit}}(undef, K)
    dirichlet = Dirichlet(α)
    LL = Vector{Float64}(undef, K)
    W = Vector{Vector{Float64}}(undef, q)
    e = 1
    for i ∈ 1:t
        i_max, max_ll = -1, -Inf
        for j ∈ 1:q
            W[j] = rand(dirichlet)
            E[e] = Ensemble{StructProbCircuit}(circs[(e-1)*n+1:e*n], log.(W[j]))
            ll = sum(log_likelihood_per_instance(E[e], D))
            # Assume a uniform prior on the ensembles so that max p(e|D) = max p(D|e).
            if ll > max_ll i_max, max_ll = j, ll end
            LL[e] = ll
            verbose && println("BMC Iteration ", e, '/', K, '.')
            e += 1
        end
        α .+= W[i_max]
    end
    LL .= exp.(LL .- maximum(LL))
    LL .= LL ./ sum(LL)
    return BayesModelComb(E, log.(LL))
end

function weighted_query(B::BayesModelComb{T}, D::DataFrame, f::Function; kwargs...)::Vector{Float64} where T <: ProbCircuit
    n, m = nrow(D), length(B.E)
    LL = Matrix{Float64}(undef, n, m)
    @inbounds for i ∈ 1:m LL[:,i] .= f(B.E[i], D; kwargs...) .+ B.W[i] end
    return logsumexp(LL, 2)
end

@inline function log_likelihood_per_instance(B::BayesModelComb{T}, D::DataFrame; use_gpu::Bool = false)::Vector{Float64} where T <: ProbCircuit
    return weighted_query(B, D, log_likelihood_per_instance; use_gpu)
end

@inline function marginal(B::BayesModelComb{T}, D::DataFrame)::Vector{Float64} where T <: ProbCircuit
    return weighted_query(B, D, marginal)
end


text:
#!/usr/bin/env julia

using GitHub, BinaryBuilder, Pkg, Pkg.PlatformEngines, SHA, Pkg.BinaryPlatforms

"""
    extract_platform_key(path::AbstractString)

Given the path to a tarball, return the platform key of that tarball. If none
can be found, prints a warning and return the current platform suffix.
"""
function extract_platform_key(path::AbstractString)
    try
        return extract_name_version_platform_key(path)[3]
    catch
        @warn("Could not extract the platform key of $(path); continuing...")
        return platform_key_abi()
    end
end

"""
    extract_name_version_platform_key(path::AbstractString)

Given the path to a tarball, return the name, platform key and version of that
tarball. If any of those things cannot be found, throw an error.
"""
function extract_name_version_platform_key(path::AbstractString)
    m = match(r"^(.*?)\.v(.*?)\.([^\.\-]+-[^\.\-]+-([^\-]+-){0,2}[^\-]+).tar.gz$", basename(path))
    if m === nothing
        error("Could not parse name, platform key and version from $(path)")
    end
    name = m.captures[1]
    version = VersionNumber(m.captures[2])
    platkey = platform_key_abi(m.captures[3])
    return name, version, platkey
end

function product_hashes_from_github_release(repo_name::AbstractString, tag_name::AbstractString;
                                            verbose::Bool=true)
    # Get list of files within this release
    release = GitHub.gh_get_json(GitHub.DEFAULT_API, "/repos/$(repo_name)/releases/tags/$(tag_name)", auth=BinaryBuilder.Wizard.github_auth())

    # Try to extract the platform key from each, use that to find all tarballs
    function can_extract_platform(filename)
        # Short-circuit build.jl because that's quite often there.  :P
        if startswith(filename, "build") && endswith(filename, ".jl")
            return false
        end

        unknown_platform = typeof(extract_platform_key(filename)) <: UnknownPlatform
        if unknown_platform && verbose
            @info("Ignoring file $(filename); can't extract its platform key")
        end
        return !unknown_platform
    end
    assets = [a for a in release["assets"] if can_extract_platform(a["name"])]

    # Download each tarball, hash it, and reconstruct product_hashes.
    product_hashes = Dict()
    mktempdir() do d
        for asset in assets
            # For each asset (tarball), download it
            filepath = joinpath(d, asset["name"])
            url = asset["browser_download_url"]
            download(url, filepath)

            # Hash it
            hash = open(filepath) do file
                return bytes2hex(sha256(file))
            end

            println(compat_string_of_platform_key_abi(split(asset["name"], '.')[5]))

            # Then fit it into our product_hashes
            file_triplet = BinaryBuilder.triplet(extract_platform_key(asset["name"]))
            product_hashes[file_triplet] = (asset["name"], hash)

            if verbose
                @info("Calculated $hash for $(asset["name"])")
            end
        end
    end

    return product_hashes
end

# `repr` calls `BinaryBuilder.repr` which does not print the `prefix, `
# first argument that is needed by `BinaryProvider`.
function _repr(p::Product)
    return replace(repr(p), "(" => "(prefix, ")
end

function compat_string_of_platform_key_abi(platform)
    splited = split(platform, '-')
    if length(splited) == 3
        arch, vendor, abi = splited
        cxx = nothing
        fortran = nothing
    elseif length(splited) == 4
        arch, vendor, abi, other = splited
        if startswith(other, "cxx")
            cxx = other
            fortran = nothing
        elseif startswith(other, "libgfortran")
            cxx = nothing
            fortran = other
        end
    else
        arch, vendor, abi, fortran, cxx = splited
        @assert startswith(fortran, "libgfortran")
        @assert startswith(cxx, "cxx")
    end

    if vendor == "linux"
        result = "Linux("
    elseif vendor == "apple" && startswith(abi, "darwin")
        result = "MacOS("
    elseif vendor == "unknown" && startswith(abi, "freebsd")
        result = "FreeBSD("
    elseif vendor == "w64" && abi == "mingw32"
        result = "Windows("
    else
        error("Unknown vendor in platform '$platform'")
    end

    result *= ":$(arch)"

    if vendor == "linux"
        if abi == "gnu"
            result *= ", libc=:glibc"
        elseif abi == "musl"
            result *= ", libc=:musl"
        elseif abi == "gnueabihf"
            result *= ", libc=:glibc, call_abi=:eabihf"
        elseif abi == "musleabihf"
            result *= ", libc=:musl, call_abi=:eabihf"
        else
            error("Platform '$platform' has unknown ABI")
        end
    end

    # check for required compiler ABI
    if !(isnothing(cxx) && isnothing(fortran))
        result *= ", compiler_abi=CompilerABI("
        if fortran == "libgfortran3"
            result *= ":gcc4"
        elseif fortran == "libgfortran4"
            result *= ":gcc7"
        elseif fortran == "libgfortran5"
            result *= ":gcc8"
        else
            result *= ":gcc_any"
        end

        if !isnothing(cxx)
            result *= ", :$cxx"
        end
        result *= ")"
    end

    return result * ")"
end

function print_buildjl(io::IO, products::Vector, product_hashes::Dict,
                       bin_path::AbstractString)
    print(io, """
    using BinaryProvider # requires BinaryProvider 0.3.0 or later

    # Parse some basic command-line arguments
    const verbose = "--verbose" in ARGS
    const prefix = Prefix(get([a for a in ARGS if a != "--verbose"], 1, joinpath(@__DIR__, "usr")))
    """)

    # Print out products
    print(io, "products = [\n")
    for prod in products
        print(io, "    $(_repr(prod)),\n")
    end
    print(io, "]\n\n")

    # Print binary locations/tarball hashes
    print(io, """
    # Download binaries from hosted location
    bin_prefix = "$bin_path"

    # Listing of files generated by BinaryBuilder:
    """)

    println(io, "download_info = Dict(")
    for platform in sort(collect(keys(product_hashes)))
        fname, hash = product_hashes[platform]
        pkey = compat_string_of_platform_key_abi(platform)
        println(io, "    $(pkey) => (\"\$bin_prefix/$(fname)\", \"$(hash)\"),")
    end
    println(io, ")\n")

    print(io, """
    # Install unsatisfied or updated dependencies:
    unsatisfied = any(!satisfied(p; verbose=verbose) for p in products)
    dl_info = choose_download(download_info, platform_key_abi())
    if dl_info === nothing && unsatisfied
        # If we don't have a compatible .tar.gz to download, complain.
        # Alternatively, you could attempt to install from a separate provider,
        # build from source or something even more ambitious here.
        error("Your platform (\\\"\$(Sys.MACHINE)\\\", parsed as \\\"\$(triplet(platform_key_abi()))\\\") is not supported by this package!")
    end

    # If we have a download, and we are unsatisfied (or the version we're
    # trying to install is not itself installed) then load it up!
    if unsatisfied || !isinstalled(dl_info...; prefix=prefix)
        # Download and install binaries
        install(dl_info...; prefix=prefix, force=true, verbose=verbose)
    end

    # Write out a deps.jl file that will contain mappings for our products
    write_deps_file(joinpath(@__DIR__, "deps.jl"), products, verbose=verbose)
    """)
end

if length(ARGS) < 1 || length(ARGS) > 3
	@error("Usage: generate_buildjl.jl path/to/build_tarballs.jl [<repo_name> <tag_name>]")
    exit(1)
end

function registered_paths(ctx, uuid)
    @static if VERSION < v"1.5.0-DEV.863"
        return Pkg.Operations.registered_paths(ctx.env, uuid)
    else
        return Pkg.Operations.registered_paths(ctx, uuid)
    end
end

build_tarballs_path = ARGS[1]
@info "Build tarballs script: $(build_tarballs_path)"
src_name = basename(dirname(build_tarballs_path))
if 2 <= length(ARGS) <= 3
    repo_name = ARGS[2]
else
    repo_name = "JuliaBinaryWrappers/$(src_name)_jll.jl"
end
@info "Repo name: $(repo_name)"

if length(ARGS) == 3
    tag_name = ARGS[3]
else
    ctx = Pkg.Types.Context()
    # Force-update the registry here, since we may have pushed a new version recently
    BinaryBuilder.update_registry(ctx)
    versions = VersionNumber[]
    paths = registered_paths(ctx, BinaryBuilder.jll_uuid("$(src_name)_jll"))
    if any(p -> isfile(joinpath(p, "Package.toml")), paths)
        # Find largest version number that matches ours in the registered paths
        for path in paths
            append!(versions, Pkg.Operations.load_versions(ctx, joinpath(path, "Versions.toml")))
        end
    end
    if !isempty(versions)
        last_version = maximum(versions)
        tag_name = "$(src_name)-v$(last_version)"
    else
        @error("""Unable to determine latest version of $(src_name),
               please specify it as third argument to this script:
                   generate_buildjl.jl $(build_tarballs_path) $(repo_name) <tag_name>""")
        exit(1)
    end
end
@info "Tag name: $(tag_name)"

# First, snarf out the Product variables:
if !isfile(build_tarballs_path)
    @error("Unable to open $(build_tarballs_path)")
    exit(1)
end

m = Module(:__anon__)

# Setup anonymous module
Core.eval(m, quote
    eval(x) = $(Expr(:core, :eval))(__anon__, x)
    include(x) = $(Expr(:top, :include))(__anon__, x)
    include(mapexpr::Function, x) = $(Expr(:top, :include))(mapexpr, __anon__, x)
end)
Core.eval(m, quote
    using BinaryBuilder, Pkg.BinaryPlatforms

    # Override BinaryBuilder functionality so that it doesn't actually do anything
	# it just saves the inputs so that we can mess around with them:
	_name = nothing
	_version = nothing
	_products = nothing
    function build_tarballs(A, name, version, sources, script, platforms, products, dependencies; kwargs...)
		global _name = name
		global _version = version

        # Peel off the functionalization of Products
        if isa(products, Function)
            products = products(Prefix("."))
        end
		global _products = products
		return nothing
	end
end)
include_string(m, String(read(build_tarballs_path)))
name, version, products = Core.eval(m, quote
    _name, _version, _products
end)

product_hashes = product_hashes_from_github_release(repo_name, tag_name)

mkpath(joinpath(@__DIR__, "build"))
buildjl_path = joinpath(@__DIR__, "build", "build_$(name).v$(version).jl")
bin_path = "https://github.com/$(repo_name)/releases/download/$(tag_name)"
@info("Writing out to $(buildjl_path)")
open(buildjl_path, "w") do io
    print_buildjl(io, products, product_hashes, bin_path)
end

max_stars_repo_name: sharanry/Yggdrasil

max_issues_repo_name: sharanry/Yggdrasil

max_forks_repo_name: sharanry/Yggdrasil


text:
module SimpleFluid
using HoJMTK

@parameters t
const D = Differential(t)

include("core.jl")
include("sources.jl")
include("components.jl")

export Ground,
    connect_series,
    connect_parallel,
    connect_star,
    Pump,
    FluidResistor,
    VesselCompliance,
    FluidInertia,
    FluidGround
end


text:
#
# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
# Licensed under the MIT license. See LICENSE file in the project root for details.
#

using FMI
using Plots

# we use a FMU from the FMIZoo.jl
pathToFMU = FMIZoo.get_model_filename("SpringPendulum1D", "Dymola", "2022x")

myFMU = fmiLoad(pathToFMU)

t_start = 0.0
t_stop = 8.0
rvs = ["mass.s"]

#create an instance and simulate it
comp1 = fmiInstantiate!(myFMU; loggingOn=true)
param1 = Dict("spring.c"=>10.0, "mass.s"=>1.0)
data1 = fmiSimulateCS(comp1, t_start, t_stop; recordValues=rvs, parameters=param1)
fig = fmiPlot(data1)

#create another instance, change the spring stiffness and simulate it
comp2 = fmiInstantiate!(myFMU; loggingOn=true)
param2 = Dict("spring.c"=>1.0, "mass.s"=>2.0)
data2 = fmiSimulateCS(comp2, t_start, t_stop; recordValues=rvs, parameters=param2)
fmiPlot!(fig, data2)

fmiUnload(myFMU)


text:
using Kuber
using Test

#On GCE:
#- Bring up a Kubernetes cluster: https://cloud.google.com/container-engine/docs/clusters/operations
#    - `gcloud container clusters create cluster1 --num-nodes=2 --zone=us-central1-a --machine-type=n1-standard-1 --image-type=container_vm`
#    - Note: set image-type to container_vm as the v1.4.0 Google container vm does not have glusterfs client. See: https://groups.google.com/forum/#!topic/kubernetes-users/USCSQ9TZThY
#- Enable access to the cluster:
#    - Get kubernetes credentials: `gcloud container clusters get-credentials cluster1 --zone us-central1-a`
#    - See credentials: `gcloud container clusters describe cluster-1 --zone us-central1-a`
# - Start kubectl in proxy mode
#    - run `kubectl proxy`

function init_context(override=nothing, verbose=true)
    ctx = KuberContext()
    set_server(ctx, "http://localhost:8001")
    set_ns(ctx, "default")
    Kuber.set_api_versions!(ctx; override=override, verbose=verbose)
    ctx
end

function list_cluster_components(ctx)
    @testset "List component status" begin
        res = get(ctx, :ComponentStatus)
        @test isa(res, Kuber.kind_to_type(ctx, :ComponentStatusList))
    end

    @testset "Status of single component" begin
        res = get(ctx, :ComponentStatus, "scheduler")
        @test isa(res, Kuber.kind_to_type(ctx, :ComponentStatus))
    end

    @testset "List all endpoints" begin
        res = get(ctx, :Endpoints)
        @test isa(res, Kuber.kind_to_type(ctx, :EndpointsList))
    end

    @testset "List all namespaces" begin
        res = get(ctx, :Namespace)
        @test isa(res, Kuber.kind_to_type(ctx, :NamespaceList))
    end

    nothing
end

function list_namespace_objects(ctx)
    @testset "List pods in default namespace" begin
        res = get(ctx, :Pod)
        @test isa(res, Kuber.kind_to_type(ctx, :PodList))
    end

    @testset "List in system namespace" begin
        set_ns(ctx, "kube-system")
        res = get(ctx, :Pod)
        @test isa(res, Kuber.kind_to_type(ctx, :PodList))

        res = get(ctx, :PodTemplate)
        @test isa(res, Kuber.kind_to_type(ctx, :PodTemplateList))
    end

    @testset "List in default namespace" begin
        set_ns(ctx, "default")
        res = get(ctx, :PodTemplate)
        @test isa(res, Kuber.kind_to_type(ctx, :PodTemplateList))

        res = get(ctx, :ReplicationController)
        @test isa(res, Kuber.kind_to_type(ctx, :ReplicationControllerList))
    end

    nothing
end

function create_versioned_models(ctx)
    cron_batchv1beta1 = kuber_obj(ctx, """{
        "kind": "CronJob",
        "apiVersion": "batch/v1beta1",
        "metadata": {
            "name": "hello"
        },
        "spec": {
            "schedule": "*/1 * * * *",
            "jobTemplate": {
                "spec": {
                    "template": {
                        "spec": {
                            "containers": [{
                                "name": "hello",
                                "image": "busybox",
                                "args": ["/bin/sh", "-c", "date"]
                            }],
                            "restartPolicy": "OnFailure"
                        }
                    }
                }
            }
        }
    }""")
    @test isa(cron_batchv1beta1, Kuber.Kubernetes.IoK8sApiBatchV1beta1CronJob)

    cron_batchv2alpha1 = kuber_obj(ctx, """{
        "kind": "CronJob",
        "apiVersion": "batch/v2alpha1",
        "metadata": {
            "name": "hello"
        },
        "spec": {
            "schedule": "*/1 * * * *",
            "jobTemplate": {
                "spec": {
                    "template": {
                        "spec": {
                            "containers": [{
                                "name": "hello",
                                "image": "busybox",
                                "args": ["/bin/sh", "-c", "date"]
                            }],
                            "restartPolicy": "OnFailure"
                        }
                    }
                }
            }
        }
    }""")
    @test isa(cron_batchv2alpha1, Kuber.Kubernetes.IoK8sApiBatchV2alpha1CronJob)
end

function create_delete_job(ctx, testid)
    nginx_pod = kuber_obj(ctx, """{
        "kind": "Pod",
        "metadata":{
            "name": "nginx-pod$testid",
            "namespace": "default",
            "labels": {
                "name": "nginx-pod$testid"
            }
        },
        "spec": {
            "containers": [{
                "name": "nginx$testid",
                "image": "nginx",
                "ports": [{"containerPort": 80}],
                "resources": {
                    "limits": {
                        "memory": "128Mi",
                        "cpu": "500m"
                    }
                }
            }]
        }
    }""")

    nginx_service = kuber_obj(ctx, """{
        "kind": "Service",
        "metadata": {
            "name": "nginx-service$testid",
            "namespace": "default",
            "labels": {"name": "nginx-service$testid"}
        },
        "spec": {
            "ports": [{"port": 80}],
            "selector": {"name": "nginx-pod$testid"}
        }
    }""")

    nginx_rc = kuber_obj(ctx, """{
        "kind": "ReplicationController",
        "metadata": {
            "name": "nginx-rc$testid",
            "labels": {
                "name": "nginx-rc$testid"
            }
        },
        "spec": {
            "replicas": 3,
            "selector": {
                "name": "nginx-pod$testid"
            },
            "template": {
                "metadata":{
                    "name": "nginx-pod$testid",
                    "namespace": "default",
                    "labels": {
                        "name": "nginx-pod$testid"
                    }
                },
                "spec": {
                    "containers": [{
                        "name": "nginx$testid",
                        "image": "nginx",
                        "ports": [{"containerPort": 80}],
                        "resources": {
                            "limits": {
                                "memory": "64Mi",
                                "cpu": "100m"
                            }
                        }
                    }]
                }
            }
        }
    }""")

    nginx_rc_service = kuber_obj(ctx, """{
        "kind": "Service",
        "metadata": {
            "name": "nginx-rc-service$testid",
            "namespace": "default",
            "labels": {"name": "nginx-rc-service$testid"}
        },
        "spec": {
            "type": "LoadBalancer",
            "ports": [
                {"port": 80, "name": "http"}
            ],
            "selector": {"name": "nginx-pod$testid"}
        }
    }""")

    @testset "Kind to Type" begin
        @test isa(nginx_pod, Kuber.kind_to_type(ctx, "Pod"))
        @test isa(nginx_service, Kuber.kind_to_type(ctx, "Service"))
        @test isa(nginx_rc, Kuber.kind_to_type(ctx, "ReplicationController"))
        @test isa(nginx_rc_service, Kuber.kind_to_type(ctx, "Service"))
    end

    @testset "Create nginx pod" begin
        res = put!(ctx, nginx_pod)
        @test isa(res, Kuber.kind_to_type(ctx, :Pod))
    end

    @testset "Create nginx service" begin
        res = put!(ctx, nginx_service)
        @test isa(res, Kuber.kind_to_type(ctx, :Service))
    end

    @testset "Delete nginx service" begin
        res = delete!(ctx, :Service, "nginx-service$testid")
        @test isa(res, Kuber.kind_to_type(ctx, :Status))
    end

    @testset "Delete nginx pod" begin
        res = delete!(ctx, :Pod, "nginx-pod$testid")
        @test isa(res, Kuber.kind_to_type(ctx, :Pod))
    end

    nothing
end

function test_versioned(ctx, testid)
    @testset "List Objects" begin
        list_cluster_components(ctx)
        list_namespace_objects(ctx)
    end

    @testset "Versioned Models" begin
        create_versioned_models(ctx)
    end

    @testset "Create/Delete Objects" begin
        create_delete_job(ctx, testid)
    end
end

function test_all()
    ctx = init_context()
    @testset "Kuber Tests" begin
        @testset "Server Preferred API Versions" begin
            test_versioned(ctx, "1")
        end

        @testset "Overridden API Versions" begin
            @test ctx.apis[:Apiregistration][1].api == Kuber.Kubernetes.ApiregistrationV1Api
            @test ctx.apis[:Apps][1].api == Kuber.Kubernetes.AppsV1Api

            ctx2 = init_context(("apiregistration.k8s.io"=>"v1beta1", "apps"=>"v1beta2"), false)
            @test ctx2.apis[:Apiregistration][1].api == Kuber.Kubernetes.ApiregistrationV1beta1Api
            @test ctx2.apis[:Apps][1].api == Kuber.Kubernetes.AppsV1beta2Api

            test_versioned(ctx2, "2")
        end
    end
end

if !parse(Bool, get(ENV, "CI", "false"))
    test_all()
end


text:
"""
GenX: An Configurable Capacity Expansion Model
Copyright (C) 2021,  Massachusetts Institute of Technology
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
A complete copy of the GNU General Public License v2 (GPLv2) is available
in LICENSE.txt.  Users uncompressing this from an archive may not have
received this license file.  If not, see <http://www.gnu.org/licenses/>.
"""

################################################################################
## function output
##
## description: Writes results to multiple .csv output files in path directory
##
## returns: n/a
################################################################################
@doc raw"""
	write_outputs(EP::Model, path::AbstractString, setup::Dict, inputs::Dict)

Function for the entry-point for writing the different output files. From here, onward several other functions are called, each for writing specific output files, like costs, capacities, etc.
"""
function write_outputs(EP::Model, path::AbstractString, setup::Dict, inputs::Dict)

	println("Writing Output")

	if !haskey(setup, "OverwriteResults") || setup["OverwriteResults"] == 1
		# Overwrite existing results if dir exists
		# This is the default behaviour when there is no flag, to avoid breaking existing code
		if !(isdir(path))
		mkdir(path)
		end
	else
		# Find closest unused ouput directory name and create it
		path = choose_output_dir(path)
		mkdir(path)
	end

	# https://jump.dev/MathOptInterface.jl/v0.9.10/apireference/#MathOptInterface.TerminationStatusCode
	status = termination_status(EP)

	## Check if solved sucessfully - time out is included
	if status != MOI.OPTIMAL && status != MOI.LOCALLY_SOLVED
		if status != MOI.TIME_LIMIT # Model failed to solve, so record solver status and exit
			write_status(path, inputs, setup, EP)
			return
			# Model reached timelimit but failed to find a feasible solution
	#### Aaron Schwartz - Not sure if the below condition is valid anymore. We should revisit ####
		elseif isnan(objective_value(EP))==true
			# Model failed to solve, so record solver status and exit
			write_status(path, inputs, setup, EP)
			return
		end
	end

	write_status(path, inputs, setup, EP)
	elapsed_time_costs = @elapsed write_costs(path, inputs, setup, EP)
	println("Time elapsed for writing costs is")
	println(elapsed_time_costs)
	dfCap = write_capacity(path, inputs, setup, EP)
	dfPower = write_power(path, inputs, setup, EP)
	dfCharge = write_charge(path, inputs, setup, EP)
	elapsed_time_storage = @elapsed write_storage(path, inputs, setup, EP)
	println("Time elapsed for writing storage is")
	println(elapsed_time_storage)
	dfCurtailment = write_curtailment(path, inputs, setup, EP)
	elapsed_time_nse = @elapsed write_nse(path, inputs, setup, EP)
	println("Time elapsed for writing nse is")
	println(elapsed_time_nse)
	elapsed_time_power_balance = @elapsed write_power_balance(path, inputs, setup, EP)
	println("Time elapsed for writing power balance is")
	println(elapsed_time_power_balance)
	if inputs["Z"] > 1
		elapsed_time_flows = @elapsed write_transmission_flows(path, inputs, setup, EP)
		println("Time elapsed for writing transmission flows is")
		println(elapsed_time_flows)
		elapsed_time_losses = @elapsed write_transmission_losses(path, inputs, setup, EP)
		println("Time elapsed for writing transmission losses is")
		println(elapsed_time_losses)
		if setup["NetworkExpansion"] == 1
			elapsed_time_expansion = @elapsed write_nw_expansion(path, inputs, setup, EP)
			println("Time elapsed for writing network expansion is")
			println(elapsed_time_expansion)
		end
	end
	elapsed_time_emissions = @elapsed write_emissions(path, inputs, setup, EP)
	println("Time elapsed for writing emissions is")
	println(elapsed_time_emissions)
	if has_duals(EP) == 1
		elapsed_time_reliability = @elapsed write_reliability(path, inputs, setup, EP)
		println("Time elapsed for writing reliability is")
		println(elapsed_time_reliability)
		elapsed_time_stordual = @elapsed write_storagedual(path, inputs, setup, EP)
		println("Time elapsed for writing storage duals is")
		println(elapsed_time_stordual)
	end

	if setup["UCommit"] >= 1
		elapsed_time_commit = @elapsed write_commit(path, inputs, setup, EP)
		println("Time elapsed for writing commitment is")
		println(elapsed_time_commit)
		elapsed_time_start = @elapsed write_start(path, inputs, setup, EP)
		println("Time elapsed for writing startup is")
		println(elapsed_time_start)
		elapsed_time_shutdown = @elapsed write_shutdown(path, inputs, setup, EP)
		println("Time elapsed for writing shutdown is")
		println(elapsed_time_shutdown)
		if setup["Reserves"] == 1
			elapsed_time_reg = @elapsed write_reg(path, inputs, setup, EP)
			println("Time elapsed for writing regulation is")
			println(elapsed_time_reg)
			elapsed_time_rsv = @elapsed write_rsv(path, inputs, setup, EP)
			println("Time elapsed for writing reserves is")
			println(elapsed_time_rsv)
		end
	end

	# Output additional variables related inter-period energy transfer via storage
	if setup["OperationWrapping"] == 1 && !isempty(inputs["STOR_LONG_DURATION"])
		elapsed_time_lds_init = @elapsed write_opwrap_lds_stor_init(path, inputs, setup, EP)
		println("Time elapsed for writing lds init is")
		println(elapsed_time_lds_init)
		elapsed_time_lds_dstor = @elapsed write_opwrap_lds_dstor(path, inputs, setup, EP)
		println("Time elapsed for writing lds dstor is")
		println(elapsed_time_lds_dstor)
	end

	dfPrice = DataFrame()
	dfEnergyRevenue = DataFrame()
	dfChargingcost = DataFrame()
	dfSubRevenue = DataFrame()
	dfRegSubRevenue = DataFrame()
	if has_duals(EP) == 1
		dfPrice = write_price(path, inputs, setup, EP)
		dfEnergyRevenue = write_energy_revenue(path, inputs, setup, EP, dfPower, dfPrice, dfCharge)
		dfChargingcost = write_charging_cost(path, inputs, dfCharge, dfPrice, dfPower, setup)
		dfSubRevenue, dfRegSubRevenue = write_subsidy_revenue(path, inputs, setup, dfCap, EP)
	end

	elapsed_time_time_weights = @elapsed write_time_weights(path, inputs)
	println("Time elapsed for writing time weights is")
	println(elapsed_time_time_weights)
	dfESR = DataFrame()
	dfESRRev = DataFrame()
	if setup["EnergyShareRequirement"]==1 && has_duals(EP) == 1
		dfESR = write_esr_prices(path, inputs, setup, EP)
		dfESRRev = write_esr_revenue(path, inputs, setup, dfPower, dfESR)
	end
	dfResMar = DataFrame()
	dfResRevenue = DataFrame()
	if setup["CapacityReserveMargin"]==1 && has_duals(EP) == 1
		dfResMar = write_reserve_margin(path, setup, EP)
		elapsed_time_rsv_margin = @elapsed write_reserve_margin_w(path, inputs, setup, EP)
		println("Time elapsed for writing reserve margin is")
		println(elapsed_time_rsv_margin)
		dfResRevenue = write_reserve_margin_revenue(path, inputs, setup, dfPower, dfCharge, dfResMar, dfCap)
		elapsed_time_cap_value = @elapsed write_capacity_value(path, inputs, setup, dfPower, dfCharge, dfResMar, dfCap)
		println("Time elapsed for writing capacity value is")
		println(elapsed_time_cap_value)
	end

	elapsed_time_net_rev = @elapsed write_net_revenue(path, inputs, setup, EP, dfCap, dfESRRev, dfResRevenue, dfChargingcost, dfPower, dfEnergyRevenue, dfSubRevenue, dfRegSubRevenue)
	println("Time elapsed for writing net revenue is")
	println(elapsed_time_net_rev)
	## Print confirmation
	println("Wrote outputs to $path")

end # END output()

max_stars_repo_name: Betristor/LTESOM

max_issues_repo_name: Betristor/LTESOM

max_forks_repo_name: Betristor/LTESOM


text:
##
# MIME stuff
##

export as_mime

struct AsMIME
    mime::MIME
    x::Any
end

Base.showable(m::MIME, am::AsMIME) = m == am.mime
Base.show(io::IO, m::MIME, am::AsMIME) = Base.show(io, m, am.x)

function as_mime(m::MIME)
    x -> as_mime(m, x)
end

function as_mime(m::MIME, x)
    AsMIME(m, x)
end

export as_text, as_html, as_svg, as_png
const as_text = as_mime(MIME"text/plain"())
const as_html = as_mime(MIME"text/html"())
const as_svg = as_mime(MIME"image/svg+xml"())
const as_png = as_mime(MIME"image/png"())
# as_jpg = as_mime(MIME"image/jpg"())
# as_bmp = as_mime(MIME"image/bmp"())
# as_gif = as_mime(MIME"image/gif"())

##
# terminal classics
##

export Dump, Show, Print

"""
    Dump(x; maxdepth=8)

Every part of the representation of a value. The depth of the output is truncated at maxdepth.

This is a variant of [`Base.dump`](@ref) that returns the representation directly, instead of printing it to stdout.

See also: [`Print`](@ref) and [`with_terminal`](@ref).
"""
function Dump(x; maxdepth=8)
	sprint() do io
		dump(io, x; maxdepth=maxdepth)
	end |> Text
end

"""
    Show(mime::MIME, data)

An object that can be rendered as the `mime` MIME type, by writing `data` to the IO stream. For use in environments with rich output support. Read more about [`Base.show`](@ref).

# Examples

```julia
Show(MIME"text/html"(), "I can be <b>rendered</b> as <em>HTML</em>!")
```

```julia
Show(MIME"image/png"(), read("dog.png"))
```

`Base.showable` and `Base.show` are defined for a `Show`.

```julia
s = Show(MIME"text/latex"(), "\\\\frac{hello}{world}")

showable(MIME"text/latex"(), s) == true

repr(MIME"text/latex"(), s) == "\\\\frac{hello}{world}"
```

"""
struct Show
    mime::MIME
    data
end

Base.showable(m::MIME, x::Show) = x.mime == m
Base.show(io::IO, ::MIME, x::Show) = write(io, x.data)

"""
    Print(xs...)

The text that would be printed when calling `print(xs...)`. Use `string(xs...)` if you want to use the result as a `String`.

See also: [`Dump`](@ref) and [`with_terminal`](@ref).
"""
Print(xs...) = Text(string(xs...))

##
# IO context
##

export WithIOContext

"""
    WithIOContext(x, properties::Pair...)

A wrapper around `x` with extra IOContext properties set, just for the display of `x`.

# Examples

```julia
WithIOContext(rand(100,100), :compact => false)
```

```julia
large_df = DataFrame(rand(100,100))
WithIOContext(large_df, :displaysize => (9999,9999))
```
"""
struct WithIOContext
    x
    context_properties
    WithIOContext(x, props::Pair...; moreprops...) = new(x, [props..., moreprops...])
end

function Base.show(io::IO, m::MIME, w::WithIOContext)
	Base.show(IOContext(io, w.context_properties...), m, w.x)
end
# we need to add a more specific method for text/plain because Julia base has a fallback method here
function Base.show(io::IO, m::MIME"text/plain", w::WithIOContext)
	Base.show(IOContext(io, w.context_properties...), m, w.x)
end

Base.showable(m::MIME, w::WithIOContext) = Base.showable(m, w.x)