text:
# -------------------------------------
# abstract type defs
# -------------------------------------
abstract type AbstractConvexSet{T} end
abstract type AbstractConvexCone{T} <: AbstractConvexSet{T} end

## -------------------------------------
# Composite Convex Set type.
# -------------------------------------

#This must be forward declared since it is used in SplitVector,
#which is itself used in most of the AbstractConvexSet methods
# See https://github.com/JuliaLang/julia/issues/269

struct CompositeConvexSet{T} <: AbstractConvexSet{T}
	dim::Int
	sets::Vector{AbstractConvexSet}
	function CompositeConvexSet{T}(sets::Vector{<:AbstractConvexSet{T}}) where{T}
		# do not allow nesting of composite sets
		if any(set->isa(set, CompositeConvexSet), sets)
			error("Nesting of CompositeConvexSets not supported")
		end
		dim = sum(x -> x.dim, sets)
		new(dim, sets)
	end
end

include("./splitvector.jl")
include("./convexset.jl")


text:
### A Pluto.jl notebook ###
# v0.12.21

using Markdown
using InteractiveUtils

# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
    quote
        local el = $(esc(element))
        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
        el
    end
end

# ╔═╡ bc0bf1e8-8a36-11eb-3cc2-ff079d49df5b
begin
	using WaterLily, PlutoUI
	using LinearAlgebra: norm2
	include("../examples/TwoD_plots.jl")
end;

# ╔═╡ 5a2b2292-8bd9-11eb-1478-75b6bd59dc7b
md"Define the size $L$ and angle $\alpha$ of a square cylinder"

# ╔═╡ 63399f40-8bdd-11eb-35a9-1b23580343f8
@bind L PlutoUI.Slider(10.:30., default=16.)

# ╔═╡ 7e80b328-8bde-11eb-160f-73a67b8db0a2
Text(string("L=",L))

# ╔═╡ 7ac5d8a4-8a43-11eb-2ef0-e3c0aa4737b0
@bind α PlutoUI.Slider(range(0.,π/4,step=π/16), default=0.)

# ╔═╡ f956022e-8bde-11eb-311b-2dfb01626549
Text(α==0 ? "α=0" : string("α=π/",floor(Int,π/α)))

# ╔═╡ 1a9e4234-8bda-11eb-21f0-894c61269b18
md"Click `Start` (and then `stop`) to run (and then pause) the simulation."

# ╔═╡ 2c015c6a-8e81-11eb-01a1-f5b928f308e6
@bind tick PlutoUI.Clock(interval=0.01)

# ╔═╡ d48df7f8-8bd9-11eb-2f0b-1d3f58b046df
md"Define the `Simulation` by setting the size and the signed distance function of the box. Note that you need to use `norm2`, as it plays nicely with ForwardDiff."

# ╔═╡ d35fa7f0-9246-11eb-340e-156c8daaaa97
n,m = 3*2^6,2^7;

# ╔═╡ d9335082-9246-11eb-2c7c-2f3a03365bf9
# n-dimensional box SDF. See https://www.iquilezles.org/
body = AutoBody() do x,t
	x = [cos(α) sin(α); -sin(α) cos(α)] * (x .- m/2) # transform to body coords
	x = abs.(x) .- L                                 # position relative to corner
	norm2(max.(x, 0.))+min(maximum(x),0.)            # SDF
end;

# ╔═╡ 9a7c1caa-9248-11eb-284c-ef6bec18bf65
sim = Simulation((n+2,m+2),[1.,0.],L; body, ν=L/100.);

# ╔═╡ b8e4b74e-8a3a-11eb-27a0-87eea85497d9
begin
	tick
	sim_step!(sim,sim_time(sim)+0.25)
	@inside sim.flow.σ[I] = WaterLily.curl(3,I,sim.flow.u)*sim.L/sim.U
	as_png(flood(sim.flow.σ,clims=(-10,10)))
end

# ╔═╡ Cell order:
# ╟─5a2b2292-8bd9-11eb-1478-75b6bd59dc7b
# ╟─63399f40-8bdd-11eb-35a9-1b23580343f8
# ╟─7e80b328-8bde-11eb-160f-73a67b8db0a2
# ╟─7ac5d8a4-8a43-11eb-2ef0-e3c0aa4737b0
# ╟─f956022e-8bde-11eb-311b-2dfb01626549
# ╟─1a9e4234-8bda-11eb-21f0-894c61269b18
# ╟─2c015c6a-8e81-11eb-01a1-f5b928f308e6
# ╟─b8e4b74e-8a3a-11eb-27a0-87eea85497d9
# ╟─d48df7f8-8bd9-11eb-2f0b-1d3f58b046df
# ╠═bc0bf1e8-8a36-11eb-3cc2-ff079d49df5b
# ╠═d35fa7f0-9246-11eb-340e-156c8daaaa97
# ╠═d9335082-9246-11eb-2c7c-2f3a03365bf9
# ╠═9a7c1caa-9248-11eb-284c-ef6bec18bf65

ext: jl


text:
module Charts

import Genie, Stipple
import Genie.Renderer.Html: HTMLString, normal_element

using Stipple

export PlotLayout, PlotData, PlotAnnotation, Trace, plot, ErrorBar, Font, ColorBar
export PlotLayoutGrid, PlotLayoutAxis
export PlotConfig, PlotLayoutTitle, PlotLayoutLegend, PlotlyLine, PlotDataMarker

const DEFAULT_WRAPPER = Genie.Renderer.Html.template

const PLOT_TYPE_SCATTER = "scatter"
const PLOT_TYPE_SCATTERGL = "scattergl"
const PLOT_TYPE_BAR = "bar"
const PLOT_TYPE_PIE = "pie"
const PLOT_TYPE_HEATMAP = "heatmap"
const PLOT_TYPE_HEATMAPGL = "heatmapgl"
const PLOT_TYPE_IMAGE = "image"
const PLOT_TYPE_CONTOUR = "contour"
const PLOT_TYPE_TABLE = "table"
const PLOT_TYPE_BOX = "box"
const PLOT_TYPE_VIOLIN = "violin"
const PLOT_TYPE_HISTOGRAM = "histogram"
const PLOT_TYPE_HISTOGRAM2D = "histogram2d"
const PLOT_TYPE_HISTOGRAM2DCONTOUR = "histogram2dcontour"
const PLOT_TYPE_OHLC = "ohlc"
const PLOT_TYPE_CANDLESTICK = "candlestick"
const PLOT_TYPE_WATERFALL = "waterfall"
const PLOT_TYPE_FUNNEL = "funnel"
const PLOT_TYPE_FUNNELAREA = "funnelarea"
const PLOT_TYPE_INDICATOR = "indicator"
const PLOT_TYPE_SCATTER3D = "scatter3d"
const PLOT_TYPE_SURFACE = "surface"
const PLOT_TYPE_MESH3D = "mesh3d"
const PLOT_TYPE_CONE = "cone"
const PLOT_TYPE_STREAMTUBE = "streamtube"
const PLOT_TYPE_VOLUME = "volume"
const PLOT_TYPE_ISOSURFACE = "isosurface"

const LAYOUT_TITLE_REF_CONTAINER = "container"
const LAYOUT_TITLE_REF_PAPER = "paper"
const LAYOUT_AUTO = "auto"
const LAYOUT_LEFT = "left"
const LAYOUT_CENTER = "center"
const LAYOUT_RIGHT = "right"
const LAYOUT_TOP = "top"
const LAYOUT_MIDDLE = "middle"
const LAYOUT_BOTTOM = "bottom"
const LAYOUT_ORIENTATION_VERTICAL = "v"
const LAYOUT_ORIENTATION_HORIZONTAL = "h"
const LAYOUT_ITEMSIZING_TRACE = "trace"
const LAYOUT_ITEMSIZING_CONSTANT = "constant"
const LAYOUT_CLICK_TOGGLE = "toggle"
const LAYOUT_CLICK_TOGGLEOTHERS = "toggleothers"
const LAYOUT_HIDE = "hide"
const LAYOUT_SHOW = "show"
const LAYOUT_ABOVE = "above"
const LAYOUT_BELOW = "below"
const LAYOUT_OVERLAY = "overlay"
const LAYOUT_GROUP = "group"
const LAYOUT_STACK = "stack"

Genie.Renderer.Html.register_normal_element("plotly", context = @__MODULE__)

Base.@kwdef mutable struct Font
  family::String = "\"Open Sans\", verdana, arial, sans-serif"
  size::Union{Int,Float64} = 12
  color::String = "#444"
end

function Font(fontsize::Union{Int,Float64})
  fs = Font()
  fs.size = fontsize
  return fs
end

Base.:(==)(x::Font, y::Font) = x.family == y.family && x.size == y.size && x.color == y.color

Base.hash(f::Font) = hash("$(f.family)$(f.size)$(f.color)")

#===#

Base.@kwdef mutable struct ColorBar
  thicknessmode::Union{String,Nothing} = nothing # "fraction" | "pixels", default is pixels
  thickness::Union{Int,Nothing} = nothing # 30
  lenmode::Union{String,Nothing} = nothing # "fraction" | "pixels", default is fraction
  len::Union{Float64,Int,Nothing} = nothing # 1
  x::Union{Float64,Nothing} = nothing # 1.02
  xanchor::Union{String,Nothing} = nothing # "left" | "center" | "right", default is left
  xpad::Union{Int,Nothing} = nothing # 10
  yanchor::Union{String,Nothing} = nothing # "top" | "middle" | "bottom", default is middle
  ypad::Union{Int,Nothing} = nothing # 10
  outlinecolor::Union{String,Nothing} = nothing # "#444"
  bordercolor::Union{String,Nothing} = nothing # "#444"
  borderwidth::Union{Int,Nothing} = nothing # 0
  bgcolor::Union{String,Nothing} = nothing # "rgba(0,0,0,0)"
  tickmode::Union{String,Nothing} = nothing
  nticks::Union{Int,Nothing} = nothing
  tick0::Union{Float64,Int,String,Nothing} = nothing
  dtick::Union{Float64,Int,String,Nothing} = nothing
  tickvals::Union{Vector{Float64},Vector{Int},Nothing} = nothing
  ticktext::Union{Vector{String},Nothing} = nothing
  ticks::Union{String,Nothing} = nothing # "outside" | "inside" | ""
  ticklabelposition::Union{String,Nothing} = nothing # "outside" | "inside" | "outside top" | "inside top" | "outside bottom" | "inside bottom", default is outside
  ticklen::Union{Int,Nothing} = nothing # 5
  tickwidth::Union{Int,Nothing} = nothing # 1
  tickcolor::Union{String,Nothing} = nothing # "#444"
  showticklabels::Union{Bool,Nothing} = nothing # true
  tickfont::Union{Font,Nothing} = nothing
  tickangle::Union{String,Int,Float64,Nothing} = nothing
  tickformat::Union{String,Nothing} = nothing
  tickformatstops::Union{Dict,Nothing} = nothing
  tickprefix::Union{String,Nothing} = nothing
  showtickprefix::Union{String,Nothing} = nothing # "all" | "first" | "last" | "none", default is all
  ticksuffix::Union{String,Nothing} = nothing
  showticksuffix::Union{String,Nothing} = nothing # "all" | "first" | "last" | "none",
  separatethousands::Union{Bool,Nothing} = nothing
  exponentformat::Union{String,Nothing} = nothing # none" | "e" | "E" | "power" | "SI" | "B", default is B
  minexponent::Union{Int,Nothing} = nothing
  showexponent::Union{String,Nothing} = nothing
  # needs special treatment:
  title_text::Union{String,Nothing} = nothing # ""
  title_font::Union{Font,Nothing} = nothing # Font()
  title_side::Union{String,Nothing} = nothing # LAYOUT_LEFT
end

function Base.show(io::IO, cb::ColorBar)
  output = "ColorBar: \n"
  for f in fieldnames(typeof(cb))
    prop = getproperty(cb, f)
    if prop !== nothing
      output *= "$f = $prop \n"
    end
  end

  print(io, output)
end

function Base.Dict(cb::ColorBar)
  trace = Dict{Symbol, Any}()

  d = Dict{Symbol, Any}()
  (cb.title_text !== nothing) && (d[:text] = cb.title_text)
  (cb.title_font !== nothing) && (d[:font] = cb.title_font)
  (cb.title_side !== nothing) && (d[:side] = cb.title_side)
  (length(d) > 0) && (trace[:title] = d)

  optionals!(trace, cb, [
    :thicknessmode, :thickness, :lenmode, :len, :x, :xanchor, :xpad, :yanchor, :ypad, :outlinecolor, :bordercolor, :borderwidth, :bgcolor, :tickmode, :nticks, :tick0, :dtick, :tickvals, :ticktext, :ticks, :ticklabelposition, :ticklen, :tickwidth, :tickcolor, :showticklabels, :tickfont, :tickangle, :tickformat, :tickformatstops, :tickprefix, :showtickprefix, :ticksuffix, :showticksuffix, :separatethousands, :exponentformat, :minexponent, :showexponent
  ])
end

function optionals!(d::Dict, cb::ColorBar, opts::Vector{Symbol}) :: Dict
  for o in opts
    if getproperty(cb, o) !== nothing
      d[o] = getproperty(cb, o)
    end
  end

  d
end

function Stipple.render(cb::ColorBar, fieldname::Union{Symbol,Nothing} = nothing)
  Dict(cb)
end

function ColorBar(text, title_font_size::Union{Int,Float64}, side)
  cb = ColorBar()
  cb.title_text = text
  cb.title_font = Font(title_font_size)
  cb.title_side = side
  cb
end

#===#

Base.@kwdef mutable struct ErrorBar
  visible::Union{Bool,Nothing} = nothing
  type::Union{String,Nothing} = nothing # "percent" | "constant" | "sqrt" | "data"
  symmetric::Union{Bool,Nothing} = nothing
  array::Union{Vector{Float64},Nothing} = nothing
  arrayminus::Union{Vector{Float64},Nothing} = nothing
  value::Union{Float64,Nothing} = nothing # 10
  valueminus::Union{Float64,Nothing} = nothing # 10
  traceref::Union{Int,Nothing} = nothing # 0
  tracerefminus::Union{Int,Nothing} = nothing # 0
  copy_ystyle::Union{Bool,Nothing} = nothing
  color::Union{String,Nothing} = nothing
  thickness::Union{Int,Nothing} = nothing # 2
  width::Union{Int,Nothing} = nothing # 0
end

function Base.show(io::IO, eb::ErrorBar)
  output = "Errorbar: \n"
  for f in fieldnames(typeof(eb))
    prop = getproperty(eb, f)
    if prop !== nothing
      output *= "$f = $prop \n"
    end
  end

  print(io, output)
end

function ErrorBar(error_array::Vector; color::Union{String,Nothing} = nothing)
  eb = ErrorBar(visible=true, array=error_array, type="data", symmetric=true)
  (color !== nothing) && (eb.color = color)
  eb
end

function ErrorBar(error_array::Vector, error_arrayminus::Vector; color::Union{String,Nothing} = nothing)
  eb = ErrorBar(visible=true, array=error_array, type="data", symmetric=false, arrayminus=error_arrayminus)
  (color !== nothing) && (eb.color = color)
  eb
end

function ErrorBar(error_value::Number; color::Union{String,Nothing} = nothing)
  eb = ErrorBar(visible=true, value=error_value, type="percent", symmetric=true)
  (color !== nothing) && (eb.color = color)
  eb
end

function ErrorBar(error_value::Number, error_valueminus::Number; color::Union{String,Nothing} = nothing)
  eb = ErrorBar(visible=true, value=error_value, type="percent", symmetric=false, valueminus=error_valueminus)
  (color !== nothing) && (eb.color = color)
  eb
end

function Base.Dict(eb::ErrorBar)
  trace = Dict{Symbol, Any}()

  optionals!(trace, eb, [
    :visible, :type, :symmetric, :array, :arrayminus, :value, :valueminus, :traceref, :tracerefminus, :copy_ystyle, :color, :thickness, :width
  ])
end

function optionals!(d::Dict, eb::ErrorBar, opts::Vector{Symbol}) :: Dict
  for o in opts
    if getproperty(eb, o) !== nothing
      d[o] = getproperty(eb, o)
    end
  end

  d
end

function Stipple.render(eb::ErrorBar, fieldname::Union{Symbol,Nothing} = nothing)
  Dict(eb)
end

#===#

Base.@kwdef mutable struct PlotAnnotation
  visible::Union{Bool,Nothing} = nothing
  text::Union{String,Nothing} = nothing
  textangle::Union{Float64,Int,Nothing} = nothing
  font::Union{Font,Nothing} = nothing
  width::Union{Float64,Int,Nothing} = nothing
  height::Union{Float64,Int,Nothing} = nothing
  opacity::Union{Float64,Nothing} = nothing
  align::Union{String,Nothing} = nothing
  valign::Union{String,Nothing} = nothing
  bgcolor::Union{String,Nothing} = nothing
  bordercolor::Union{String,Nothing} = nothing
  borderpad::Union{Int,Nothing} = nothing
  borderwidth::Union{Int,Nothing} = nothing
  showarrow::Union{Bool,Nothing} = nothing
  arrowcolor::Union{String,Nothing} = nothing
  arrowhead::Union{Int,Nothing} = nothing
  startarrowhead::Union{Int,Nothing} = nothing
  arrowside::Union{String,Nothing} = nothing
  arrowsize::Union{Float64,Nothing} = nothing
  startarrowsize::Union{Float64,Nothing} = nothing
  arrowwidth::Union{Float64,Nothing} = nothing
  standoff::Union{Int,Nothing} = nothing
  startstandoff::Union{Int,Nothing} = nothing
  ax::Union{String,Int,Float64,Nothing} = nothing
  ay::Union{String,Int,Float64,Nothing} = nothing
  axref::Union{String,Nothing} = nothing
  ayref::Union{String,Nothing} = nothing
  xref::Union{String,Int,Float64,Nothing} = nothing
  x::Union{String,Int,Float64,Nothing} = nothing
  xanchor::Union{String,Nothing} = nothing
  xshift::Union{Int,Float64,Nothing} = nothing
  yref::Union{String,Int,Float64,Nothing} = nothing
  y::Union{String,Int,Float64,Nothing} = nothing
  yanchor::Union{String,Nothing} = nothing
  yshift::Union{Int,Float64,Nothing} = nothing
  # TODO: clicktoshow
  # TODO: xclick
  # TODO: yclick
  hoverlabel::Union{Dict,Nothing} = nothing
  captureevents::Union{Bool,Nothing} = nothing
  name::Union{String,Nothing} = nothing
  templateitemname::Union{String,Nothing} = nothing
end

function Base.show(io::IO, an::PlotAnnotation)
  output = "Annotation: \n"
  for f in fieldnames(typeof(an))
    prop = getproperty(an, f)
    if prop !== nothing
      output *= "$f = $prop \n"
    end
  end

  print(io, output)
end

function Base.Dict(an::PlotAnnotation)
  trace = Dict{Symbol,Any}()

  if an.font !== nothing
    trace[:font] = Dict(
      :family => an.font.family,
      :size => an.font.size,
      :color => an.font.color
    )
  end

  if an.hoverlabel !== nothing
    trace[:hoverlabel] = an.hoverlabel
  end

  optionals!(trace, an, [:visible, :text, :textangle, :width, :height, :opacity,
          :align, :valign, :bgcolor, :bordercolor, :borderpad, :borderwidth, :showarrow,
          :arrowcolor, :arrowhead, :startarrowhead, :arrowside, :arrowsize, :startarrowsize,
          :arrowwidth, :standoff, :startstandoff, :ax, :ay, :axref, :ayref, :xref, :x,
          :xanchor, :xshift, :yref, :y, :yanchor, :yshift, :captureevents, :name, :templateitemname])

end

function optionals!(d::Dict, an::PlotAnnotation, opts::Vector{Symbol}) :: Dict
  for o in opts
    if getproperty(an, o) !== nothing
      d[o] = getproperty(an, o)
    end
  end

  d
end

function Stipple.render(anv::Vector{PlotAnnotation}, fieldname::Union{Symbol,Nothing} = nothing)
  [Dict(an) for an in anv]
end

#===#

Base.@kwdef mutable struct PlotLayoutGrid
  rows::Union{Int,Nothing} = nothing # >= 1
  roworder::Union{String,Nothing} = nothing # "top to bottom" | "bottom to top"
  columns::Union{Int,Nothing} = nothing # >= 1
  subplots::Union{Matrix{String},Nothing} = nothing
  xaxes::Union{Vector{String},Nothing} = nothing
  yaxes::Union{Vector{String},Nothing} = nothing
  pattern::Union{String,Nothing} = nothing # "independent" | "coupled"
  xgap::Union{Float64,Nothing} = nothing # [0.0, 1.0]
  ygap::Union{Float64,Nothing} = nothing # [0.0, 1.0]
  domain_x::Union{Vector{Float64},Nothing} = nothing # fraction, e.g [0, 1]
  domain_y::Union{Vector{Float64},Nothing} = nothing # fraction, e.g [0, 1]
  xside::Union{String,Nothing} = nothing # "bottom" | "bottom plot" | "top plot" | "top"
  yside::Union{String,Nothing} = nothing # "bottom" | "bottom plot" | "top plot" | "top"
end

function Base.show(io::IO, lg::PlotLayoutGrid)
  output = "Layout Grid: \n"
  for f in fieldnames(typeof(lg))
    prop = getproperty(lg, f)
    if prop !== nothing
      output *= "$f = $prop \n"
    end
  end

  print(io, output)
end

function Base.Dict(lg::PlotLayoutGrid)
  trace = Dict{Symbol,Any}()

  if (lg.domain_x !== nothing) & (lg.domain_y !== nothing)
    trace[:domain] = Dict(
      :x => lg.domain_x,
      :y => lg.domain_y
    )
  elseif lg.domain_x !== nothing
    trace[:domain] = Dict(
      :x => lg.domain_x
    )
  elseif lg.domain_y !== nothing
    trace[:domain] = Dict(
      :y => lg.domain_y
    )
  end

  optionals!(trace, lg, [:rows, :roworder, :columns, :subplots, :xaxes, :yaxes,
                         :pattern, :xgap, :ygap, :xside, :yside])

end

function optionals!(d::Dict, lg::PlotLayoutGrid, opts::Vector{Symbol}) :: Dict
  for o in opts
    if getproperty(lg, o) !== nothing
      d[o] = getproperty(lg, o)
    end
  end

  d
end

#===#

Base.@kwdef mutable struct PlotLayoutAxis
  xy::String = "x" # "x" or "y"
  index::Int = 1 # 1, 2, 3 etc. for subplots

  visible::Union{Bool,Nothing} = nothing
  title::Union{String,Nothing} = nothing # "axis title"
  font::Union{Font,Nothing} = nothing
  title_text::Union{String,Nothing} = nothing
  title_font::Union{Font,Nothing} = nothing
  title_standoff::Union{Int,Nothing} = nothing
  automargin::Union{Bool,Nothing} = nothing
  ticks::Union{String,Nothing} = nothing
  showline::Union{Bool,Nothing} = nothing
  zeroline::Union{Bool,Nothing} = nothing
  linecolor::Union{String,Nothing} = nothing
  linewidth::Union{Int,Nothing} = nothing
  mirror::Union{Bool,String,Nothing} = nothing
  ticklabelposition::Union{String,Nothing} = nothing
  tickson::Union{String,Nothing} = nothing
  ticklabelmode::Union{String,Nothing} = nothing
  ticklen::Union{Int,Nothing} = nothing
  tickwidth::Union{Int,Nothing} = nothing
  tickcolor::Union{String,Nothing} = nothing
  showticklabels::Union{Bool,Nothing} = nothing
  showspikes::Union{Bool,Nothing} = nothing
  spikecolor::Union{String,Nothing} = nothing
  spikethickness::Union{Int,Nothing} = nothing
  spikedash::Union{String,Nothing} = nothing
  spikemode::Union{String,Nothing} = nothing
  spikesnap::Union{String,Nothing} = nothing
  showgrid::Union{Bool,Nothing} = nothing
  gridcolor::Union{String,Nothing} = nothing
  gridwidth::Union{Int,Nothing} = nothing
  side::Union{String,Nothing} = nothing
  anchor::Union{String,Nothing} = nothing
  position::Union{Float64,Nothing} = nothing
  domain::Union{Vector{Float64},Nothing} = nothing
  scaleanchor::Union{String,Nothing} = nothing
  scaleratio::Union{Int,Nothing} = nothing
  constrain::Union{String,Nothing} = nothing
  constraintoward::Union{String,Nothing} = nothing
  autorange::Union{Bool,String,Nothing} = nothing
  rangemode::Union{String,Nothing} = nothing
  range::Union{Vector{Int},Vector{Float64},Nothing} = nothing
  fixedrange::Union{Bool,Nothing} = nothing
  type::Union{String,Nothing} = nothing
  autotypenumbers::Union{String,Nothing} = nothing
  tickmode::Union{String,Nothing} = nothing
  nticks::Union{Int,Nothing} = nothing
  tick0::Union{Float64,Int,String,Nothing} = nothing
  dtick::Union{Float64,Int,String,Nothing} = nothing
  tickvals::Union{Vector{Float64},Vector{Int},Nothing} = nothing
  ticktext::Union{Vector{String},Nothing} = nothing
  tickformat::Union{String,Nothing} = nothing
  tickfont::Union{Font,Nothing} = nothing
  tickangle::Union{String,Int,Float64,Nothing} = nothing
  tickprefix::Union{String,Nothing} = nothing
  ticksuffix::Union{String,Nothing} = nothing
  showexponent::Union{String,Nothing} = nothing
  minexponent::Union{Int,Nothing} = nothing
  hoverformat::Union{String,Nothing} = nothing
  zerolinecolor::Union{String,Nothing} = nothing
  zerolinewidth::Union{Int,Nothing} = nothing
  showdividers::Union{Bool,Nothing} = nothing
  dividercolor::Union{String,Nothing} = nothing
  dividerwidth::Union{Int,Nothing} = nothing
  overlaying::Union{String,Nothing} = nothing
  layer::Union{String,Nothing} = nothing
  categoryorder::Union{String,Nothing} = nothing
  categoryarray::Union{Vector{Float64},Nothing} = nothing
  calendar::Union{String,Nothing} = nothing
end

function Base.show(io::IO, la::PlotLayoutAxis)
  output = "Layout Axis: \n"
  for f in fieldnames(typeof(la))
    prop = getproperty(la, f)
    if prop !== nothing
      output *= "$f = $prop \n"
    end
  end

  print(io, output)
end

function Base.Dict(la::PlotLayoutAxis)
  trace = Dict{Symbol,Any}()

  if la.title_text !== nothing
    d = Dict{Symbol,Any}(:text => la.title_text)
    (la.title_font !== nothing) && (d[:font] = la.title_font)
    (la.title_standoff !== nothing) && (d[:standoff] = la.title_standoff)
    trace[:title] = d
  end

  d = optionals!(trace, la, [:visible, :title, :font, :automargin, :ticks, :showline, :zeroline,
                         :linecolor, :linewidth, :mirror, :ticklabelposition, :showgrid,
                         :gridcolor, :gridwidth, :side, :anchor, :position, :domain, :scaleanchor,
                         :scaleratio, :constrain, :constraintoward, :autorange,
                         :rangemode, :range, :fixedrange, :type, :autotypenumbers,
                         :tickmode, :nticks, :tick0, :dtick, :tickvals, :ticktext, :tickformat, :tickfont,
                         :tickangle, :tickprefix, :ticksuffix, :showexponent, :minexponent,
                         :hoverformat, :zerolinecolor, :zerolinewidth, :showdividers, :dividercolor,
                         :dividerwidth, :overlaying, :layer, :categoryorder, :categoryarray, :calendar,
                         :tickson, :ticklabelmode, :ticklen, :tickwidth, :tickcolor, :showticklabels,
                         :showspikes, :spikecolor, :spikethickness, :spikedash, :spikemode, :spikesnap])

  k = Symbol(la.xy * "axis" * ((la.index > 1) ? "$(la.index)" : ""))
  Dict(k => d)
end

function optionals!(d::Dict, la::PlotLayoutAxis, opts::Vector{Symbol}) :: Dict
  for o in opts
    if getproperty(la, o) !== nothing
      d[o] = getproperty(la, o)
    end
  end

  d
end

function Stipple.render(la::PlotLayoutAxis, fieldname::Union{Symbol,Nothing} = nothing)
  [Dict(la)]
end

function Stipple.render(lav::Vector{PlotLayoutAxis}, fieldname::Union{Symbol,Nothing} = nothing)
  [Dict(la) for la in lav]
end

#===#

Base.@kwdef mutable struct PlotLayoutTitle
  text::Union{String,Nothing} = nothing # ""
  font::Union{Font,Nothing} = nothing # Font()
  xref::Union{String,Nothing} = nothing # LAYOUT_TITLE_REF_CONTAINER
  yref::Union{String,Nothing} = nothing # LAYOUT_TITLE_REF_CONTAINER
  x::Union{Float64,String,Nothing} = nothing # 0.5
  y::Union{Float64,String,Nothing} = nothing # LAYOUT_AUTO
  xanchor::Union{String,Nothing} = nothing # LAYOUT_AUTO
  yanchor::Union{String,Nothing} = nothing # LAYOUT_AUTO
  pad_t::Union{Int,Nothing} = nothing # 0
  pad_r::Union{Int,Nothing} = nothing # 0
  pad_b::Union{Int,Nothing} = nothing # 0
  pad_l::Union{Int,Nothing} = nothing # 0
end

function Base.show(io::IO, plt::PlotLayoutTitle)
  output = "Layout Title: \n"
  for f in fieldnames(typeof(plt))
    prop = getproperty(plt, f)
    if prop !== nothing
      output *= "$f = $prop \n"
    end
  end

  print(io, output)
end

function Base.Dict(plt::PlotLayoutTitle)
  trace = Dict{Symbol, Any}()

  d = Dict{Symbol, Any}()
  (plt.pad_t !== nothing) && (d[:t] = plt.pad_t)
  (plt.pad_r !== nothing) && (d[:r] = plt.pad_r)
  (plt.pad_b !== nothing) && (d[:b] = plt.pad_b)
  (plt.pad_l !== nothing) && (d[:l] = plt.pad_l)
  (length(d) > 0) && (trace[:pad] = d)

  optionals!(trace, plt, [:text, :font, :xref, :yref, :x, :y, :xanchor, :yanchor])
end

function optionals!(d::Dict, plt::PlotLayoutTitle, opts::Vector{Symbol}) :: Dict
  for o in opts
    if getproperty(plt, o) !== nothing
      d[o] = getproperty(plt, o)
    end
  end

  d
end

function Stipple.render(plt::PlotLayoutTitle, fieldname::Union{Symbol,Nothing} = nothing)
  Dict(plt)
end

#===#

Base.@kwdef mutable struct PlotLayoutLegend
  bgcolor::Union{String,Nothing} = nothing
  bordercolor::Union{String,Nothing} = nothing # "#444"
  borderwidth::Union{Int,Nothing} = nothing # 0
  font::Union{Font,Nothing} = nothing # Font()
  orientation::Union{String,Nothing} = nothing # LAYOUT_ORIENTATION_VERTICAL
  traceorder::Union{String,Nothing} = nothing # "normal"
  tracegroupgap::Union{Int,Nothing} = nothing # 10
  itemsizing::Union{String,Nothing} = nothing # LAYOUT_ITEMSIZING_TRACE
  itemwidth::Union{Int,Nothing} = nothing # 30
  itemclick::Union{String,Bool,Nothing} = nothing # LAYOUT_CLICK_TOGGLE
  itemdoubleclick::Union{String,Bool,Nothing} = nothing #  LAYOUT_CLICK_TOGGLEOTHERS
  x::Union{Int,Float64,Nothing} = nothing # 1.02
  xanchor::Union{String,Nothing} = nothing # LAYOUT_LEFT
  y::Union{Int,Float64,Nothing} = nothing # 1
  yanchor::Union{String,Nothing} = nothing # LAYOUT_AUTO
  # TODO: uirevision::Union{Int,String} = ""
  valign::Union{String,Nothing} = nothing # LAYOUT_MIDDLE
  title_text::Union{String,Nothing} = nothing # ""
  title_font::Union{Font,Nothing} = nothing # Font()
  title_side::Union{String,Nothing} = nothing # LAYOUT_LEFT
end

function Base.show(io::IO, pll::PlotLayoutLegend)
  output = "Layout Legend: \n"
  for f in fieldnames(typeof(pll))
    prop = getproperty(pll, f)
    if prop !== nothing
      output *= "$f = $prop \n"
    end
  end

  print(io, output)
end

function Base.Dict(pll::PlotLayoutLegend)
  trace = Dict{Symbol, Any}()

  d = Dict{Symbol, Any}()
  (pll.title_text !== nothing) && (d[:text] = pll.title_text)
  (pll.title_font !== nothing) && (d[:font] = pll.title_font)
  (pll.title_side !== nothing) && (d[:side] = pll.title_side)
  (length(d) > 0) && (trace[:title] = d)

  optionals!(trace, pll, [:bgcolor, :bordercolor, :borderwidth, :font, :orientation, :traceorder, :tracegroupgap, :itemsizing, :itemwidth, :itemclick, :itemdoubleclick, :x, :xanchor, :y, :yanchor, :valign])
end

function optionals!(d::Dict, pll::PlotLayoutLegend, opts::Vector{Symbol}) :: Dict
  for o in opts
    if getproperty(pll, o) !== nothing
      d[o] = getproperty(pll, o)
    end
  end

  d
end

function Stipple.render(pll::PlotLayoutLegend, fieldname::Union{Symbol,Nothing} = nothing)
  Dict(pll)
end

#===#

#===#

Base.@kwdef mutable struct PlotAnnotation
  visible::Union{Bool,Nothing} = nothing
  text::Union{String,Nothing} = nothing
  textangle::Union{Float64,Int,Nothing} = nothing
  font::Union{Font,Nothing} = nothing
  width::Union{Float64,Int,Nothing} = nothing
  height::Union{Float64,Int,Nothing} = nothing
  opacity::Union{Float64,Nothing} = nothing
  align::Union{String,Nothing} = nothing
  valign::Union{String,Nothing} = nothing
  bgcolor::Union{String,Nothing} = nothing
  bordercolor::Union{String,Nothing} = nothing
  borderpad::Union{Int,Nothing} = nothing
  borderwidth::Union{Int,Nothing} = nothing
  showarrow::Union{Bool,Nothing} = nothing
  arrowcolor::Union{String,Nothing} = nothing
  arrowhead::Union{Int,Nothing} = nothing
  startarrowhead::Union{Int,Nothing} = nothing
  arrowside::Union{String,Nothing} = nothing
  arrowsize::Union{Float64,Nothing} = nothing
  startarrowsize::Union{Float64,Nothing} = nothing
  arrowwidth::Union{Float64,Nothing} = nothing
  standoff::Union{Int,Nothing} = nothing
  startstandoff::Union{Int,Nothing} = nothing
  ax::Union{String,Int,Float64,Nothing} = nothing
  ay::Union{String,Int,Float64,Nothing} = nothing
  axref::Union{String,Nothing} = nothing
  ayref::Union{String,Nothing} = nothing
  xref::Union{String,Int,Float64,Nothing} = nothing
  x::Union{String,Int,Float64,Nothing} = nothing
  xanchor::Union{String,Nothing} = nothing
  xshift::Union{Int,Float64,Nothing} = nothing
  yref::Union{String,Int,Float64,Nothing} = nothing
  y::Union{String,Int,Float64,Nothing} = nothing
  yanchor::Union{String,Nothing} = nothing
  yshift::Union{Int,Float64,Nothing} = nothing
  # TODO: clicktoshow
  # TODO: xclick
  # TODO: yclick
  hoverlabel::Union{Dict,Nothing} = nothing
  captureevents::Union{Bool,Nothing} = nothing
  name::Union{String,Nothing} = nothing
  templateitemname::Union{String,Nothing} = nothing
end

function Base.show(io::IO, an::PlotAnnotation)
  output = "Annotation: \n"
  for f in fieldnames(typeof(an))
    prop = getproperty(an, f)
    if prop !== nothing
      output *= "$f = $prop \n"
    end
  end

  print(output)
end

function Base.Dict(an::PlotAnnotation)
  trace = Dict{Symbol,Any}()

  if an.font !== nothing
    trace[:font] = Dict(
      :family => an.font.family,
      :size => an.font.size,
      :color => an.font.color
    )
  end

  if an.hoverlabel !== nothing
    trace[:hoverlabel] = an.hoverlabel
  end

  optionals!(trace, an, [:visible, :text, :textangle, :width, :height, :opacity,
          :align, :valign, :bgcolor, :bordercolor, :borderpad, :borderwidth, :showarrow,
          :arrowcolor, :arrowhead, :startarrowhead, :arrowside, :arrowsize, :startarrowsize,
          :arrowwidth, :standoff, :startstandoff, :ax, :ay, :axref, :ayref, :xref, :x,
          :xanchor, :xshift, :yref, :y, :yanchor, :yshift, :captureevents, :name, :templateitemname])

end

function optionals!(d::Dict, an::PlotAnnotation, opts::Vector{Symbol}) :: Dict
  for o in opts
    if getproperty(an, o) !== nothing
      d[o] = getproperty(an, o)
    end
  end

  d
end

function Stipple.render(anv::Vector{PlotAnnotation}, fieldname::Union{Symbol,Nothing} = nothing)
  [Dict(an) for an in anv]
end

#===#

Base.@kwdef mutable struct PlotLayout
  title::Union{PlotLayoutTitle,Nothing} = nothing
  xaxis::Union{Vector{PlotLayoutAxis},Nothing} = nothing
  yaxis::Union{Vector{PlotLayoutAxis},Nothing} = nothing

  showlegend::Union{Bool,Nothing} = nothing # true
  legend::Union{PlotLayoutLegend,Nothing} = nothing
  annotations::Union{Vector{PlotAnnotation},Nothing} = nothing
  grid::Union{PlotLayoutGrid,Nothing} = nothing

  margin_l::Union{Int,Nothing} = nothing # 80
  margin_r::Union{Int,Nothing} = nothing # 80
  margin_t::Union{Int,Nothing} = nothing # 100
  margin_b::Union{Int,Nothing} = nothing # 80
  margin_pad::Union{Int,Nothing} = nothing # 0
  margin_autoexpand::Union{Bool,Nothing} = nothing # true
  autosize::Union{Bool,Nothing} = nothing # true
  width::Union{Int,String,Nothing} = nothing # 700
  height::Union{Int,String,Nothing} = nothing # 450
  font::Union{Font,Nothing} = nothing
  uniformtext_mode::Union{String,Bool,Nothing} = nothing # false
  uniformtext_minsize::Union{Int,Nothing} = nothing # 0
  separators::Union{String,Nothing} = nothing # ".,"
  paper_bgcolor::Union{String,Nothing} = nothing # "#fff"
  plot_bgcolor::Union{String,Nothing} = nothing # "#fff"

  # TODO: implement the following fields in function Stipple.render(pl::PlotLayout...
  autotypenumbers::String = "convert types"
  # TODO: colorscale settings
  colorway::Vector{String} = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
                              "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"]
  modebar_orientation::String = LAYOUT_ORIENTATION_HORIZONTAL
  modebar_bgcolor::String = "transparent"
  modebar_color::String = ""
  modebar_activecolor::String = ""
  # TODO: modebar_uirevision::String = ""
  hovermode::Union{String,Bool} = "closest"
  clickmode::String = "event"
  dragmode::String = "zoom"
  selectdirection::String = "any"
  hoverdistance::Int = 20
  spikedistance::Int = 20
  hoverlabel_bgcolor::String = ""
  hoverlabel_bordercolor::String = ""
  hoverlabel_font::Font = Font()
  hoverlabel_align::String = LAYOUT_AUTO
  hoverlabel_namelength::Int = 15
  transition_duration::Int = 500
  transition_easing::String = "cubic-in-out"
  transition_ordering::String = "layout first"
  # TODO: datarevision
  # TODO: editrevision
  # TODO: selectionrevision
  # TODO: template
  # TODO: meta
  # TODO: computed

  calendar::String = "gregorian"
  newshape_line_color::String = ""
  newshape_line_width::Int = 4
  newshape_line_dash::String = "solid"
  newshape_fillcolor::String = "rgba(0,0,0,0)"
  newshape_fillrule::String = "evenodd"
  newshape_opacity::Float64 = 1.0
  newshape_layer::String = LAYOUT_ABOVE
  newshape_drawdirection::String = "diagonal"
  activeshape_fillcolor::String = "rgb(255,0,255)"
  activeshape_opacity::Float64 = 0.5
  # TODO: hidesources
  barmode::Union{String,Nothing} = nothing # LAYOUT_GROUP
  barnorm::Union{String,Nothing} = nothing
  bargap::Union{Float64,Nothing} = nothing # 0.5
  bargroupgap::Union{Float64,Nothing} = nothing # 0
  # TODO: hiddenlabels
  # TODO: piecolorway
  extendpiecolors::Bool = true
  boxmode::String = LAYOUT_OVERLAY
  boxgap::Float64 = 0.3
  boxgroupgap::Float64 = 0.3
  violinmode::String = LAYOUT_OVERLAY
  violingap::Float64 = 0.3
  violingroupgap::Float64 = 0.3
  waterfallmode::String = LAYOUT_GROUP
  waterfallgap::Float64 = 0.3
  waterfallgroupgap::Float64 = 0.0
  funnelmode::String = LAYOUT_STACK
  funnelgap::Float64 = 0.0
  funnelgroupgap::Float64 = 0.0
  # TODO: funnelareacolorway
  extendfunnelareacolors::Bool = true
  # TODO: sunburstcolorway
  extendsunburstcolors::Bool = true
  # TODO: treemapcolorway
  extendtreemapcolors::Bool = true
  annotations::Union{Vector{PlotAnnotation},Nothing} = nothing
end

function Base.show(io::IO, l::PlotLayout)
  default = PlotLayout()
  output = "layout: \n"
  for f in fieldnames(typeof(l))
    prop = getproperty(l, f)
    if prop != getproperty(default, f)
      output *= "$f = $prop \n"
    end
  end

  print(io, output)
end

#===#

Base.@kwdef mutable struct PlotlyLine
  # for all Plotly lines:
  color::Union{String,Nothing} = nothing
  width::Union{Int,Nothing} = nothing # 2
  # Scatter - line:
  shape::Union{String,Nothing} = nothing # "linear" | "spline" | "hv" | "vh" | "hvh" | "vhv"
  smoothing::Union{Float64,String,Nothing} = nothing
  dash::Union{String,Nothing} = nothing # "solid", "dot", "dash", "longdash", "dashdot", "longdashdot" or "5px,10px,2px,2px"
  simplify::Union{Bool,Nothing} = nothing
  # Scatter - marker - line:
  cauto::Union{Bool,Nothing} = nothing
  cmin::Union{Float64,Nothing} = nothing
  cmax::Union{Float64,Nothing} = nothing
  cmid::Union{Float64,Nothing} = nothing
  colorscale::Union{Matrix,String,Nothing} = nothing
  autocolorscale::Union{Bool,Nothing} = nothing
  reversescale::Union{Bool,Nothing} = nothing
  # Box - marker - line
  outliercolor::Union{String,Nothing} = nothing
  outlierwidth::Union{Int,Nothing} = nothing # 1
end

function Base.show(io::IO, pdl::PlotlyLine)
  output = "Layout Legend: \n"
  for f in fieldnames(typeof(pdl))
    prop = getproperty(pdl, f)
    if prop !== nothing
      output *= "$f = $prop \n"
    end
  end

  print(io, output)
end

function Base.Dict(pdl::PlotlyLine)
  trace = Dict{Symbol, Any}()

  optionals!(trace, pdl, [:color, :width, :shape, :smoothing, :dash, :simplify, :cauto, :cmin, :cmax, :cmid, :colorscale, :autocolorscale, :reversescale, :outliercolor, :outlierwidth])
end

function optionals!(d::Dict, pdl::PlotlyLine, opts::Vector{Symbol}) :: Dict
  for o in opts
    if getproperty(pdl, o) !== nothing
      d[o] = getproperty(pdl, o)
    end
  end

  d
end

function Stipple.render(pdl::PlotlyLine, fieldname::Union{Symbol,Nothing} = nothing)
  Dict(pdl)
end

#===#

Base.@kwdef mutable struct PlotDataMarker
  symbol::Union{String,Nothing} = nothing
  opacity::Union{Float64,Vector{Float64},Nothing} = nothing
  size::Union{Int,Vector{Int},Nothing} = nothing
  maxdisplayed::Union{Int,Nothing} = nothing
  sizeref::Union{Float64,Nothing} = nothing
  sizemin::Union{Float64,Nothing} = nothing
  sizemode::Union{String,Nothing} = nothing
  line::Union{PlotlyLine,Nothing} = nothing
  # TODO: gradient
  color::Union{String,Vector{Float64},Nothing} = nothing
  cauto::Union{Bool,Nothing} = nothing
  cmin::Union{Float64,Nothing} = nothing
  cmax::Union{Float64,Nothing} = nothing
  cmid::Union{Float64,Nothing} = nothing
  colorscale::Union{Matrix,String,Nothing} = nothing
  autocolorscale::Union{Bool,Nothing} = nothing
  reversescale::Union{Bool,Nothing} = nothing
  showscale::Union{Bool,Nothing} = nothing
  colorbar::Union{ColorBar,Nothing} = nothing
  coloraxis::Union{String,Nothing} = nothing
  # Specific for Pie charts:
  colors::Union{Vector{String},Nothing} = nothing
end

function Base.show(io::IO, pdm::PlotDataMarker)
  output = "Layout Legend: \n"
  for f in fieldnames(typeof(pdm))
    prop = getproperty(pdm, f)
    if prop !== nothing
      output *= "$f = $prop \n"
    end
  end

  print(io, output)
end

function Base.Dict(pdm::PlotDataMarker)
  trace = Dict{Symbol, Any}()
  (pdm.line !== nothing) && (trace[:line] = Dict(pdm.line))
  (pdm.colorbar !== nothing) && (trace[:colorbar] = Dict(pdm.colorbar))

  optionals!(trace, pdm, [:symbol, :opacity, :size, :maxdisplayed, :sizeref, :sizemin,
      :sizemode, :color, :cauto, :cmin, :cmax, :cmid, :colorscale, :autocolorscale,
      :reversescale, :showscale, :coloraxis, :colors])
end

function optionals!(d::Dict, pdm::PlotDataMarker, opts::Vector{Symbol}) :: Dict
  for o in opts
    if getproperty(pdm, o) !== nothing
      d[o] = getproperty(pdm, o)
    end
  end

  d
end

function Stipple.render(pdm::PlotDataMarker, fieldname::Union{Symbol,Nothing} = nothing)
  Dict(pdm)
end

#===#

Base.@kwdef mutable struct PlotData
  plot::String = PLOT_TYPE_SCATTER

  align::Union{String,Nothing} = nothing
  alignmentgroup::Union{String,Nothing} = nothing
  alphahull::Union{Int,Float64,Nothing} = nothing
  anchor::Union{String,Nothing} = nothing
  aspectratio::Union{Float64,Int,Nothing} = nothing
  autobinx::Union{Bool,Nothing} = nothing
  autobiny::Union{Bool,Nothing} = nothing
  autocolorscale::Union{Bool,Nothing} = nothing
  autocontour::Union{Bool,Nothing} = nothing
  automargin::Union{Bool,Nothing} = nothing
  bandwidth::Union{Float64,Int,Nothing} = nothing
  base::Union{Float64,Int,String,Nothing} = nothing
  baseratio::Union{Float64,Int,Nothing} = nothing
  bingroup::Union{String,Nothing} = nothing
  box::Union{Dict,Nothing} = nothing
  boxmean::Union{Bool,String,Nothing} = nothing
  boxpoints::Union{Bool,String,Nothing} = nothing
  caps::Union{Dict,Nothing} = nothing
  cauto::Union{Bool,Nothing} = nothing
  cells::Union{Dict,Nothing} = nothing
  cliponaxis::Union{Bool,Nothing} = nothing
  close::Union{Vector,Nothing} = nothing
  cmax::Union{Float64,Int,Nothing} = nothing
  cmid::Union{Float64,Int,Nothing} = nothing
  cmin::Union{Float64,Int,Nothing} = nothing
  color::Union{String,Nothing} = nothing
  coloraxis::Union{String,Nothing} = nothing
  colorbar::Union{Dict,ColorBar,Nothing} = nothing
  colorscale::Union{Matrix,String,Nothing} = nothing
  columnorder::Union{Vector,Nothing} = nothing
  columnwidth::Union{Float64,Int,Vector,Nothing} = nothing
  connectgaps::Union{Bool,Nothing} = nothing
  connector::Union{Dict,Nothing} = nothing
  constraintext::Union{String,Nothing} = nothing
  contour::Union{Dict,Nothing} = nothing
  contours::Union{Dict,Nothing} = nothing
  cumulative::Union{Dict,Nothing} = nothing
  customdata::Union{Vector,Nothing} = nothing
  decreasing::Union{Dict,Nothing} = nothing
  delaunayaxis::Union{Char,String,Nothing} = nothing
  delta::Union{Dict,Nothing} = nothing
  direction::Union{String,Nothing} = nothing
  dlabel::Union{Int,Nothing} = nothing
  domain::Union{Dict,Nothing} = nothing
  dx::Union{Int,Nothing} = nothing
  dy::Union{Int,Nothing} = nothing
  error_x::Union{Dict,ErrorBar,Nothing} = nothing
  error_y::Union{Dict,ErrorBar,Nothing} = nothing
  error_z::Union{Dict,ErrorBar,Nothing} = nothing
  facecolor::Union{Vector,Nothing} = nothing
  fill::Union{String,Nothing} = nothing
  fillcolor::Union{String,Nothing} = nothing
  flatshading::Union{Bool,Nothing} = nothing
  gauge::Union{Dict,Nothing} = nothing
  groupnorm::Union{String,Nothing} = nothing
  header::Union{Dict,Nothing} = nothing
  hidesurface::Union{Bool,Nothing} = nothing
  high::Union{Dict,Nothing} = nothing
  histfunc::Union{String,Nothing} = nothing
  histnorm::Union{String,Nothing} = nothing
  hole::Union{Float64,Nothing} = nothing
  hovertext::Union{Vector{String},String,Nothing} = nothing
  hoverinfo::Union{String,Nothing} = nothing
  hoverlabel::Union{Dict,Nothing} = nothing
  hoveron::Union{String,Nothing} = nothing
  hoverongaps::Union{Bool,Nothing} = nothing
  hovertemplate::Union{Vector{String},String,Nothing} = nothing
  i::Union{Vector,Nothing} = nothing
  intensity::Union{Vector,Nothing} = nothing
  intensitymode::Union{String,Nothing} = nothing
  j::Union{Vector,Nothing} = nothing
  k::Union{Vector,Nothing} = nothing
  ids::Union{Vector{String},Nothing} = nothing
  increasing::Union{Dict,Nothing} = nothing
  insidetextanchor::Union{String,Nothing} = nothing
  insidetextfont::Union{Font,Nothing} = nothing
  insidetextorientation::Union{String,Nothing} = nothing
  isomax::Union{Float64,Int,Nothing} = nothing
  isomin::Union{Float64,Int,Nothing} = nothing
  jitter::Union{Float64,Nothing} = nothing
  labels::Union{Vector,Nothing} = nothing
  label0::Union{Int,Nothing} = nothing
  legendgroup::Union{String,Nothing} = nothing
  lighting::Union{Dict,Nothing} = nothing
  lightposition::Union{Dict,Nothing} = nothing
  line::Union{Dict,PlotlyLine,Nothing} = nothing
  low::Union{Vector,Nothing} = nothing
  lowerfence::Union{Vector,Nothing} = nothing
  marker::Union{Dict,PlotDataMarker,Nothing} = nothing
  maxdisplayed::Union{Int,Nothing} = nothing
  mean::Union{Vector,Nothing} = nothing
  measure::Union{Vector,Nothing} = nothing
  meanline::Union{Dict,Nothing} = nothing
  median::Union{Vector,Nothing} = nothing
  meta::Union{Float64,Int,String,Nothing} = nothing
  mode::Union{String,Nothing} = nothing
  name::Union{String,Nothing} = nothing
  nbinsx::Union{Int,Nothing} = nothing
  nbinsy::Union{Int,Nothing} = nothing
  ncontours::Union{Int,Nothing} = nothing
  notched::Union{Bool,Nothing} = nothing
  notchwidth::Union{Float64,Nothing} = nothing
  notchspan::Union{Vector,Nothing} = nothing
  number::Union{Dict,Nothing} = nothing
  offset::Union{Float64,Int,Vector,Nothing} = nothing
  offsetgroup::Union{String,Nothing} = nothing
  opacity::Union{Float64,Nothing} = nothing
  opacityscale::Union{Float64,Int,Vector,String,Nothing} = nothing
  colormodel::Union{String,Nothing} = nothing # image trace color models: "rgb" | "rgba" | "rgba256" | "hsl" | "hsla"
  open::Union{Vector,Nothing} = nothing
  orientation::Union{String,Nothing} = nothing
  outsidetextfont::Union{Font,Nothing} = nothing
  points::Union{Bool,String,Nothing} = nothing
  pointpos::Union{Float64,Nothing} = nothing
  projection::Union{Dict,Nothing} = nothing
  pull::Union{Float64,Vector,Nothing} = nothing
  q1::Union{Vector,Nothing} = nothing
  q3::Union{Vector,Nothing} = nothing
  quartilemethod::Union{String,Nothing} = nothing
  reversescale::Union{Bool,Nothing} = nothing
  rotation::Union{Int,Nothing} = nothing
  scalegroup::Union{String,Nothing} = nothing
  scalemode::Union{String,Nothing} = nothing
  scene::Union{String,Nothing} = nothing
  sd::Union{Vector,Nothing} = nothing
  selected::Union{Dict,Nothing} = nothing
  selectedpoints::Union{Float64,Int,String,Nothing} = nothing
  showlegend::Union{Bool,Nothing} = nothing
  showscale::Union{Bool,Nothing} = nothing
  side::Union{String,Nothing} = nothing
  sizemode::Union{String,Nothing} = nothing
  sizeref::Union{Float64,Int,Nothing} = nothing
  slices::Union{Dict,Nothing} = nothing
  sort::Union{Bool,Nothing} = nothing
  source::Union{String,Nothing} = nothing
  spaceframe::Union{Dict,Nothing} = nothing
  span::Union{Vector,Nothing} = nothing
  spanmode::Union{String,Nothing} = nothing
  stackgaps::Union{String,Nothing} = nothing
  stackgroup::Union{String,Nothing} = nothing
  starts::Union{Dict,Nothing} = nothing
  surface::Union{Dict,Nothing} = nothing
  surfaceaxis::Union{Int,String,Nothing} = nothing
  surfacecolor::Union{String,Nothing} = nothing
  text::Union{Vector{String},String,Nothing} = nothing
  textangle::Union{String,Nothing} = nothing
  textfont::Union{Font,Nothing} = nothing
  textinfo::Union{String,Nothing} = nothing
  textposition::Union{String,Nothing} = nothing
  texttemplate::Union{Vector{String},String,Nothing} = nothing
  tickwidth::Union{Float64,Nothing} = nothing
  totals::Union{Dict,Nothing} = nothing
  transpose::Union{Bool,Nothing} = nothing
  u::Union{Vector,Nothing} = nothing
  uirevision::Union{Float64,Int,String,Nothing} = nothing
  unselected::Union{Dict,Nothing} = nothing
  upperfence::Union{Vector,Nothing} = nothing
  v::Union{Vector,Nothing} = nothing
  values::Union{Vector,Nothing} = nothing
  vertexcolor::Union{Vector,Nothing} = nothing
  visible::Union{String,Bool,Nothing} = nothing
  w::Union{Vector,Nothing} = nothing
  whiskerwidth::Union{Float64,Nothing} = nothing
  width::Union{Int,Vector{Int},Nothing} = nothing
  x::Union{Vector,Nothing} = nothing
  x0::Union{Int,String,Nothing} = nothing
  xaxis::Union{String,Nothing} = nothing
  xbingroup::Union{String,Nothing} = nothing
  xbins::Union{Dict,Nothing} = nothing
  xcalendar::Union{String,Nothing} = nothing
  xgap::Union{Int,Nothing} = nothing
  xperiod::Union{Float64,Int,String,Nothing} = nothing
  xperiodalignment::Union{String,Nothing} = nothing
  xperiod0::Union{Float64,Int,String,Nothing} = nothing
  xtype::Union{String,Nothing} = nothing
  y::Union{Vector,Nothing} = nothing
  y0::Union{Int,String,Nothing} = nothing
  yaxis::Union{String,Nothing} = nothing
  ybingroup::Union{String,Nothing} = nothing
  ybins::Union{Dict,Nothing} = nothing
  ycalendar::Union{String,Nothing} = nothing
  ygap::Union{Int,Nothing} = nothing
  yperiod::Union{Float64,Int,String,Nothing} = nothing
  yperiodalignment::Union{String,Nothing} = nothing
  yperiod0::Union{Float64,Int,String,Nothing} = nothing
  ytype::Union{String,Nothing} = nothing
  z::Union{Matrix,Nothing} = nothing
  zauto::Union{Bool,Nothing} = nothing
  zcalendar::Union{String,Nothing} = nothing
  zhoverformat::Union{String,Nothing} = nothing
  zmax::Union{Int,Nothing} = nothing
  zmid::Union{Int,Nothing} = nothing
  zmin::Union{Int,Nothing} = nothing
  zsmooth::Union{String,Nothing} = nothing
end

const Trace = PlotData

# =============

# Reference: https://github.com/plotly/plotly.js/blob/master/src/plot_api/plot_config.js?package=plotly&version=3.6.0

Base.@kwdef mutable struct PlotConfig
  responsive::Union{Bool,Nothing} = nothing # default: false
  editable::Union{Bool,Nothing} = nothing # default: false
  scrollzoom::Union{Bool,String,Nothing} = nothing  # ['cartesian', 'gl3d', 'geo', 'mapbox'], [true, false]; default: gl3d+geo+mapbox'
  staticplot::Union{Bool,Nothing} = nothing # default: false
  displaymodebar::Union{Bool,String,Nothing} = nothing # ['hover', true, false], default: "hover"
  displaylogo::Union{Bool,Nothing} = nothing # default: true
  toimage_format::Union{String,Nothing} = nothing # one of ["png", "svg", "jpeg", "webp"]
  toimage_filename::Union{String,Nothing} = nothing # "newplot"
  toimage_height::Union{Int,Nothing} = nothing # 500
  toimage_width::Union{Int,Nothing} = nothing # 700
  toimage_scale::Union{Int,Float64,Nothing} = nothing # 1
end

function Base.show(io::IO, pc::PlotConfig)
  output = "configuration: \n"
  for f in fieldnames(typeof(pc))
    prop = getproperty(pc, f)
    if prop !== nothing
      output *= "$f = $prop \n"
    end
  end

  print(io, output)
end

function Base.Dict(pc::PlotConfig)
  trace = Dict{Symbol, Any}()

  if (pc.toimage_format in ["png", "svg", "jpeg", "webp"])
    d = Dict{Symbol, Any}(:format => pc.toimage_format)
    d[:filename] = (pc.toimage_filename === nothing) ? "newplot" : pc.toimage_filename
    d[:height] = (pc.toimage_height === nothing) ? 500 : pc.toimage_height
    d[:width] = (pc.toimage_width === nothing) ? 700 : pc.toimage_width
    d[:scale] = (pc.toimage_scale === nothing) ? 1 : pc.toimage_scale
    trace[:toImageButtonOptions] = d
  end

  optionals!(trace, pc, [:responsive, :editable, :scrollzoom, :staticplot, :displaymodebar, :displaylogo])
end

function optionals!(d::Dict, pc::PlotConfig, opts::Vector{Symbol}) :: Dict
  for o in opts
    if getproperty(pc, o) !== nothing
      d[o] = getproperty(pc, o)
    end
  end

  d
end

function Stipple.render(pc::PlotConfig, fieldname::Union{Symbol,Nothing} = nothing)
  Dict(pc)
end

# =============

function plot(fieldname::Symbol;
              layout::Union{Symbol,PlotLayout} = PlotLayout(),
              config::Union{Symbol,PlotConfig} = PlotConfig(),
              wrap::Function = DEFAULT_WRAPPER,
              args...) :: String

  k = (Symbol(":data"), Symbol(":layout"), Symbol(":config"))
  v = Any["$fieldname", layout, config]

  wrap() do
    plotly(; args..., NamedTuple{k}(v)...)
  end
end

# =============

function Base.show(io::IO, pd::PlotData)
  output = "$(pd.plot): \n"
  for f in fieldnames(typeof(pd))
    prop = getproperty(pd, f)
    if prop !== nothing
      output *= "$f = $prop \n"
    end
  end

  print(io, output)
end

#===#

function Base.Dict(pd::PlotData)
  trace = Dict{Symbol,Any}(
    :type => pd.plot
  )

  if pd.textfont !== nothing
    trace[:textfont] = Dict(
      :family => pd.textfont.family,
      :size => pd.textfont.size,
      :color => pd.textfont.color
    )
  end

  if pd.insidetextfont !== nothing
    trace[:insidetextfont] = Dict(
      :family => pd.insidetextfont.family,
      :size => pd.insidetextfont.size,
      :color => pd.insidetextfont.color
    )
  end

  if pd.outsidetextfont !== nothing
    trace[:outsidetextfont] = Dict(
      :family => pd.outsidetextfont.family,
      :size => pd.outsidetextfont.size,
      :color => pd.outsidetextfont.color
    )
  end

  (pd.line !== nothing) && (trace[:line] = Dict(pd.line))
  (pd.marker !== nothing) && (trace[:marker] = Dict(pd.marker))
  (pd.error_x !== nothing) && (trace[:error_x] = Dict(pd.error_x))
  (pd.error_y !== nothing) && (trace[:error_y] = Dict(pd.error_y))
  (pd.error_z !== nothing) && (trace[:error_z] = Dict(pd.error_z))
  (pd.colorbar !== nothing) && (trace[:colorbar] = Dict(pd.colorbar))

  optionals!(trace, pd, [:align, :alignmentgroup, :alphahull, :anchor, :aspectratio, :autobinx, :autobiny,
                        :autocolorscale, :autocontour, :automargin,
                        :bandwidth, :base, :baseratio, :bingroup, :box, :boxmean, :boxpoints,
                        :cauto, :cells, :cliponaxis, :close, :color, :cmax, :cmid, :cmin,
                        :coloraxis, :colorscale, :columnorder, :columnwidth,
                        :connectgaps, :connector, :constraintext, :contour, :contours, :cumulative, :customdata,
                        :decreasing, :delta, :delaunayaxis, :direction, :dlabel, :domain, :dx, :dy,
                        :facecolor, :fill, :fillcolor, :flatshading,
                        :gauge, :groupnorm,
                        :header, :hidesurface, :high, :histfunc, :histnorm,
                        :hole, :hovertext, :hoverinfo, :hovertemplate, :hoverlabel, :hoveron, :hoverongaps,
                        :i, :intensity, :intensitymode, :ids, :increasing, :insidetextanchor, :insidetextorientation, :isomax, :isomin,
                        :j, :jitter, :k,
                        :labels, :label0, :legendgroup, :lighting, :lightposition, :low, :lowerfence,
                        :maxdisplayed, :meanline, :measure, :median, :meta,
                        :mode,
                        :name, :nbinsx, :nbinsy, :ncontours, :notched, :notchwidth, :notchspan, :number,
                        :offset, :offsetgroup, :opacity, :opacityscale, :colormodel, :open, :orientation,
                        :points, :pointpos, :projection, :pull,
                        :q1, :q3, :quartilemethod,
                        :reversescale, :rotation,
                        :scalegroup, :scalemode, :scene, :sd,
                        :selected, :selectedpoints, :showlegend,
                        :showscale, :side, :sizemode, :sizeref, :slices, :sort, :source, :spaceframe, :span, :spanmode,
                        :stackgaps, :stackgroup, :starts, :surface, :surfaceaxis, :surfacecolor,
                        :text, :textangle, :textinfo,
                        :textposition, :texttemplate, :tickwidth, :totals, :transpose,
                        :uirevision, :upperfence, :unselected,
                        :values, :vertexcolor, :visible,
                        :whiskerwidth, :width,
                        :x, :x0, :xaxis, :xbingroup, :xbins, :xcalendar, :xgap, :xperiod, :xperiodalignment, :xperiod0, :xtype,
                        :y, :y0, :yaxis, :ybingroup, :ybins, :ycalendar, :ygap, :yperiod, :yperiodalignment, :yperiod0, :ytype,
                        :z, :zauto, :zcalendar, :zhoverformat, :zmax, :zmid, :zmin, :zsmooth])
end

function optionals!(d::Dict, pd::PlotData, opts::Vector{Symbol}) :: Dict
  for o in opts
    if getproperty(pd, o) !== nothing
      d[o] = getproperty(pd, o)
    end
  end

  d
end

function Stipple.render(pd::PlotData, fieldname::Union{Symbol,Nothing} = nothing)
  [Dict(pd)]
end

function Stipple.render(pdv::Vector{PlotData}, fieldname::Union{Symbol,Nothing} = nothing)
  [Dict(pd) for pd in pdv]
end

#===#

function Base.Dict(pl::PlotLayout, fieldname::Union{Symbol,Nothing} = nothing)
  layout = Dict{Symbol, Any}()

  if pl.font !== nothing
    layout[:font] = Dict{Symbol, Any}(
      :family => pl.font.family,
      :size => pl.font.size,
      :color => pl.font.color
    )
  end

  d1 = Dict{Symbol, Any}()
  (pl.margin_l !== nothing) && (d1[:l] = pl.margin_l)
  (pl.margin_r !== nothing) && (d1[:r] = pl.margin_r)
  (pl.margin_t !== nothing) && (d1[:t] = pl.margin_t)
  (pl.margin_b !== nothing) && (d1[:b] = pl.margin_b)
  (pl.margin_pad !== nothing) && (d1[:pad] = pl.margin_pad)
  (pl.margin_autoexpand !== nothing) && (d1[:autoexpand] = pl.margin_autoexpand)
  (length(d1) > 0) && (layout[:margin] = d1)

  d2 = Dict{Symbol, Any}()
  (pl.uniformtext_mode !== nothing) && (d2[:mode] = pl.uniformtext_mode)
  (pl.uniformtext_minsize !== nothing) && (d2[:minsize] = pl.uniformtext_minsize)
  (length(d2) > 0) && (layout[:uniformtext] = d2)

  (pl.title !== nothing) && (layout[:title] = Dict(pl.title))
  (pl.legend !== nothing) && (layout[:legend] = Dict(pl.legend))
  (pl.annotations !== nothing) && (layout[:annotations] = Dict.(pl.annotations))
  (pl.grid !== nothing) && (layout[:grid] = Dict(pl.grid))

  optionals!(layout, pl, [ :showlegend, :autosize, :separators, :paper_bgcolor, :plot_bgcolor,
    :width, :height, :barmode, :barnorm, :bargap, :bargroupgap
  ])

  if pl.xaxis !== nothing
    for d in Dict.(pl.xaxis)
      merge!(layout, d)
    end
  end

  if pl.yaxis !== nothing
    for d in Dict.(pl.yaxis)
      merge!(layout, d)
    end
  end

  layout
end

function optionals!(d::Dict, pl::PlotLayout, opts::Vector{Symbol}) :: Dict
  for o in opts
    if getproperty(pl, o) !== nothing
      d[o] = getproperty(pl, o)
    end
  end

  d
end

function Stipple.render(pl::PlotLayout, fieldname::Union{Symbol,Nothing} = nothing)
  Dict(pl)
end

# #===#

end


text:
export StopAfterStep, StopAfterEpisode, StopWhenDone, ComposedStopCondition

using ProgressMeter

#####
# ComposedStopCondition
#####

"""
    ComposedStopCondition(stop_conditions; reducer = any)

The result of `stop_conditions` is reduced by `reducer`.
"""
struct ComposedStopCondition{T<:Function}
    stop_conditions::Vector{Any}
    reducer::T
end

ComposedStopCondition(stop_conditions; reducer = any) =
    ComposedStopCondition(stop_conditions, reducer)

function (s::ComposedStopCondition)(args...)
    s.reducer(sc(args...) for sc in s.stop_conditions)
end

#####
# StopAfterStep
#####
"""
    StopAfterStep(step; cur = 1, is_show_progress = true, tag = "TRAINING")

Return `true` after being called for `step`.
"""
mutable struct StopAfterStep{Tl}
    step::Int
    cur::Int
    progress::Tl
    tag::String
end

function StopAfterStep(step; cur = 1, is_show_progress = true, tag = "TRAINING")
    if is_show_progress
        progress = Progress(step)
        ProgressMeter.update!(progress, cur)
    else
        progress = nothing
    end
    StopAfterStep(step, cur, progress, tag)
end

function (s::StopAfterStep)(args...)
    !isnothing(s.progress) && next!(
        s.progress;
        # showvalues = [(Symbol(s.tag, "/", :STEP), s.cur)],  # https://github.com/timholy/ProgressMeter.jl/pull/131
    )
    @debug s.tag STEP = s.cur

    res = s.cur >= s.step
    s.cur += 1
    res
end

#####
# StopAfterEpisode
#####

"""
    StopAfterEpisode(episode; cur = 0, is_show_progress = true, tag = "TRAINING")

Return `true` after being called `episode`. If `is_show_progress` is `true`, the `ProgressMeter` will be used to show progress.
"""
mutable struct StopAfterEpisode{Tl}
    episode::Int
    cur::Int
    progress::Tl
    tag::String
end

function StopAfterEpisode(episode; cur = 0, is_show_progress = true, tag = "TRAINING")
    if is_show_progress
        progress = Progress(episode)
        ProgressMeter.update!(progress, cur)
    else
        progress = nothing
    end
    StopAfterEpisode(episode, cur, progress, tag)
end

function (s::StopAfterEpisode)(agent, env, obs)
    !isnothing(s.progress) && next!(
        s.progress;
        # showvalues = [(Symbol(s.tag, "/", :EPISODE), s.cur)],  # https://github.com/timholy/ProgressMeter.jl/pull/131
    )
    @debug s.tag EPISODE = s.cur

    get_terminal(obs) && (s.cur += 1)
    s.cur >= s.episode
end

#####
# StopWhenDone
#####

"""
    StopWhenDone()

Return `true` if the `terminal` field of an observation is `true`.
"""
struct StopWhenDone end

(s::StopWhenDone)(agent, env, obs) = get_terminal(obs)


text:
# ----- RTorch Model ----- #
using RCall

"""
    RTorchModel

Contructor for RTorch neural network.

"""
struct RTorchModel <: AbstractDifferentiableModel
    nn::Any
end

function logits(M::RTorchModel, X::AbstractArray)
  nn = M.nn
  ŷ = rcopy(R"as_array($nn(torch_tensor(t($X))))")
  ŷ = isa(ŷ, AbstractArray) ? ŷ : [ŷ]
  return ŷ'
end

probs(M::RTorchModel, X::AbstractArray)= σ.(logits(M, X))


text:
export rule

@rule NormalMixture((:m, k), Marginalisation) (q_out::Any, q_switch::Any, q_p::GammaDistributionsFamily) = begin
    pv    = probvec(q_switch)
    T     = eltype(pv)
    z_bar = clamp.(pv, tiny, one(T) - tiny)
    return NormalMeanVariance(mean(q_out), inv(z_bar[k] * mean(q_p)))
end

@rule NormalMixture((:m, k), Marginalisation) (q_out::Any, q_switch::Any, q_p::Wishart) = begin
    pv    = probvec(q_switch)
    T     = eltype(pv)
    z_bar = clamp.(pv, tiny, one(T) - tiny)
    return MvNormalMeanCovariance(mean(q_out), cholinv(z_bar[k] * mean(q_p)))
end


text:
"""
    Complexification{T <: Number} <: Plexification{T}

A complexification as a pair.
"""
struct Complexification{T <: Number} <: Plexification{T}
    l::T
    r::T
end

function (==)(x::Complexification, y::Complexification)
    x.l == y.l && x.r == y.r
end

function real(z::Complexification)
    real(z.l)
end

function zero(::Type{Complexification{T}}) where T <: Number
    Complexification{T}(zero(T), zero(T))
end

function zero(::Complexification{T}) where T <: Number
    zero(Complexification{T})
end

function one(::Type{Complexification{T}}) where T <: Number
    Complexification{T}(one(T), zero(T))
end

function one(::Complexification{T}) where T <: Number
    one(Complexification{T})
end

function (-)(z::Complexification)
    Complexification(-(z.l), -(z.r))
end

function (+)(x::Complexification{T}, y::Complexification{T}) where T <: Number
    Complexification{T}(x.l + y.l, x.r + y.r)
end

function (+)(a::T, z::Complexification{T}) where T <: Number
    Complexification{T}(a + z.l, z.r)
end

function (+)(z::Complexification{T}, a::T) where T <: Number
    Complexification{T}(z.l + a, z.r)
end

function (+)(a::Real, z::Complexification)
    Complexification(a + z.l, z.r)
end

function (+)(z::Complexification, a::Real)
    Complexification(z.l + a, z.r)
end

function (-)(x::Complexification{T}, y::Complexification{T}) where T <: Number
    Complexification{T}(x.l - y.l, x.r - y.r)
end

function (-)(a::T, z::Complexification{T}) where T <: Number
    Complexification{T}(a - z.l, -(z.r))
end

function (-)(z::Complexification{T}, a::T) where T <: Number
    Complexification{T}(z.l - a, z.r)
end

function (-)(a::Real, z::Complexification)
    Complexification(a - z.l, -(z.r))
end

function (-)(z::Complexification, a::Real)
    Complexification(z.l - a, z.r)
end

function (*)(x::Complexification{T}, y::Complexification{T}) where T <: Number
    Complexification{T}(
        (x.l * y.l) - (x.r * y.r),
        (x.l * y.r) + (x.r * y.l)
    )
end

function (*)(a::T, z::Complexification{T}) where T <: Number
    Complexification{T}(a * z.l, a * z.r)
end

function (*)(z::Complexification{T}, a::T) where T <: Number
    Complexification{T}(z.l * a , z.r * a)
end

function (*)(a::Real, z::Complexification)
    Complexification(a * z.l, a * z.r)
end

function (*)(z::Complexification, a::Real)
    Complexification(z.l * a, z.r * a)
end

function (/)(z::Complexification, a::Real)
    if iszero(a)
        error(ZeroDenominator)
    end

    Complexification(z.l / a, z.r / a)
end

function (\)(a::Real, z::Complexification)
    if iszero(a)
        error(ZeroDenominator)
    end

    Complexification(a \ z.l, a \ z.r)
end


text:
##### halo points ≥ 2 NEEDED!!
const θmax = 1.0e20

##### calculate CFL number: c=uΔt/Lx
@inline CFLx(i, j, k, g::AbstractGrid, u, ΔT) = @inbounds abs(u[i, j, k] * ΔT / ΔxC(i, j, k, g))
@inline CFLy(i, j, k, g::AbstractGrid, v, ΔT) = @inbounds abs(v[i, j, k] * ΔT / ΔyC(i, j, k, g))
@inline CFLz(i, j, k, g::AbstractGrid, w, ΔT) = @inbounds abs(w[i, j, k] * ΔT / ΔzC(i, j, k, g))

##### calculate d₀ and d₁
@inline d0(CFL) = @inbounds (2.0 - CFL) * (1.0 - CFL) / 6.0
@inline d1(CFL) = @inbounds (1.0 - CFL * CFL) / 6.0

##### calculate volume transport, unit: m³/s
@inline Trans_x(i, j, k, g::AbstractGrid, u) = @inbounds Ax(i, j, k, g) * u[i, j, k]
@inline Trans_y(i, j, k, g::AbstractGrid, v) = @inbounds Ay(i, j, k, g) * v[i, j, k]
@inline Trans_z(i, j, k, g::AbstractGrid, w) = @inbounds Az(i, j, k, g) * w[i, j, k]

##### calculate θ⁺ and θ⁻
@inline θx⁺(i, j, k, c) = abs(δx⁰(i, j, k, c))*θmax ≤ abs(δx⁻(i, j, k, c)) ?
    copysign(θmax, δx⁻(i, j, k, c)*δx⁰(i, j, k, c)) : δx⁻(i, j, k, c)/δx⁰(i, j, k, c)

@inline θx⁻(i, j, k, c) = abs(δx⁰(i, j, k, c))*θmax ≤ abs(δx⁺(i, j, k, c)) ?
    copysign(θmax, δx⁺(i, j, k, c)*δx⁰(i, j, k, c)) : δx⁺(i, j, k, c)/δx⁰(i, j, k, c)

@inline θy⁺(i, j, k, c) = abs(δy⁰(i, j, k, c))*θmax ≤ abs(δy⁻(i, j, k, c)) ?
    copysign(θmax, δy⁻(i, j, k, c)*δy⁰(i, j, k, c)) : δy⁻(i, j, k, c)/δy⁰(i, j, k, c)

@inline θy⁻(i, j, k, c) = abs(δy⁰(i, j, k, c))*θmax ≤ abs(δy⁺(i, j, k, c)) ?
    copysign(θmax, δy⁺(i, j, k, c)*δy⁰(i, j, k, c)) : δy⁺(i, j, k, c)/δy⁰(i, j, k, c)

@inline θz⁺(i, j, k, c) = abs(δz⁰(i, j, k, c))*θmax ≤ abs(δz⁻(i, j, k, c)) ?
    copysign(θmax, δz⁻(i, j, k, c)*δz⁰(i, j, k, c)) : δz⁻(i, j, k, c)/δz⁰(i, j, k, c)

@inline θz⁻(i, j, k, c) = abs(δz⁰(i, j, k, c))*θmax ≤ abs(δz⁺(i, j, k, c)) ?
    copysign(θmax, δz⁺(i, j, k, c)*δz⁰(i, j, k, c)) : δz⁺(i, j, k, c)/δz⁰(i, j, k, c)

##### calculate Ψ⁺ and Ψ⁻
@inline Ψx⁺(i, j, k, g::AbstractGrid, u, c, ΔT) =
            max(0.0, min(min(1.0, d0(CFLx(i, j, k, g, u, ΔT)) + d1(CFLx(i, j, k, g, u, ΔT)) * θx⁺(i, j, k, c)),
                         θx⁺(i, j, k, c) * (1.0 - CFLx(i, j, k, g, u, ΔT)) / (CFLx(i, j, k, g, u, ΔT) + 1.0e-20)))

@inline Ψx⁻(i, j, k, g::AbstractGrid, u, c, ΔT) =
            max(0.0, min(min(1.0, d0(CFLx(i, j, k, g, u, ΔT)) + d1(CFLx(i, j, k, g, u, ΔT)) * θx⁻(i, j, k, c)),
                         θx⁻(i, j, k, c) * (1.0 - CFLx(i, j, k, g, u, ΔT)) / (CFLx(i, j, k, g, u, ΔT) + 1.0e-20)))

@inline Ψy⁺(i, j, k, g::AbstractGrid, v, c, ΔT) =
            max(0.0, min(min(1.0, d0(CFLy(i, j, k, g, v, ΔT)) + d1(CFLy(i, j, k, g, v, ΔT)) * θy⁺(i, j, k, c)),
                         θy⁺(i, j, k, c) * (1.0 - CFLy(i, j, k, g, v, ΔT)) / (CFLy(i, j, k, g, v, ΔT) + 1.0e-20)))

@inline Ψy⁻(i, j, k, g::AbstractGrid, v, c, ΔT) =
            max(0.0, min(min(1.0, d0(CFLy(i, j, k, g, v, ΔT)) + d1(CFLy(i, j, k, g, v, ΔT)) * θy⁻(i, j, k, c)),
                         θy⁻(i, j, k, c) * (1.0 - CFLy(i, j, k, g, v, ΔT)) / (CFLy(i, j, k, g, v, ΔT) + 1.0e-20)))

@inline Ψz⁺(i, j, k, g::AbstractGrid, w, c, ΔT) =
            max(0.0, min(min(1.0, d0(CFLz(i, j, k, g, w, ΔT)) + d1(CFLz(i, j, k, g, w, ΔT)) * θz⁺(i, j, k, c)),
                         θz⁺(i, j, k, c) * (1.0 - CFLz(i, j, k, g, w, ΔT)) / (CFLz(i, j, k, g, w, ΔT) + 1.0e-20)))

@inline Ψz⁻(i, j, k, g::AbstractGrid, w, c, ΔT) =
            max(0.0, min(min(1.0, d0(CFLz(i, j, k, g, w, ΔT)) + d1(CFLz(i, j, k, g, w, ΔT)) * θz⁻(i, j, k, c)),
                         θz⁻(i, j, k, c) * (1.0 - CFLz(i, j, k, g, w, ΔT)) / (CFLz(i, j, k, g, w, ΔT) + 1.0e-20)))

##### advection flux
@inline adv_flux_x(i, j, k, g::AbstractGrid, u, c, ΔT) =
    0.5 * (Trans_x(i, j, k, g, u) + abs(Trans_x(i, j, k, g, u))) * (c[i-1, j, k] + Ψx⁺(i, j, k, g, u, c, ΔT) * δx⁰(i, j, k, c)) +
    0.5 * (Trans_x(i, j, k, g, u) - abs(Trans_x(i, j, k, g, u))) * (c[i,   j, k] - Ψx⁻(i, j, k, g, u, c, ΔT) * δx⁰(i, j, k, c))

@inline adv_flux_y(i, j, k, g::AbstractGrid, v, c, ΔT) =
    0.5 * (Trans_y(i, j, k, g, v) + abs(Trans_y(i, j, k, g, v))) * (c[i, j-1, k] + Ψy⁺(i, j, k, g, v, c, ΔT) * δy⁰(i, j, k, c)) +
    0.5 * (Trans_y(i, j, k, g, v) - abs(Trans_y(i, j, k, g, v))) * (c[i, j,   k] - Ψy⁻(i, j, k, g, v, c, ΔT) * δy⁰(i, j, k, c))

@inline adv_flux_z(i, j, k, g::AbstractGrid, w, c, ΔT) =
    0.5 * (Trans_z(i, j, k, g, w) + abs(Trans_z(i, j, k, g, w))) * (c[i, j, k  ] + Ψz⁻(i, j, k, g, w, c, ΔT) * δz⁰(i, j, k, c)) +
    0.5 * (Trans_z(i, j, k, g, w) - abs(Trans_z(i, j, k, g, w))) * (c[i, j, k-1] - Ψz⁺(i, j, k, g, w, c, ΔT) * δz⁰(i, j, k, c))


text:
using AdventOfCodeSolutions
using Test

function input(puzzle::Puzzle{2020, 5, n}) where n
    io = openInput(puzzle)
    split(read(io, String), "\n", keepempty=false)
end

seatRow(str) = map(s -> s == 'F' ? 0 : 1, collect(str[1:7]))
seatColumn(str) = map(s -> s == 'L' ? 0 : 1, collect(str[8:end]))
parseSeat(str) = parse(Int, join(str), base=2)

function decode(str)
    row = seatRow(str) |> parseSeat
    column = seatColumn(str) |> parseSeat
    return (row = row, column = column)
end

seatId(str::AbstractString) = seatId(decode(str)...)
seatId(row, column) = 8 * row + column
seatId(seat) = seatId(seat...)

solve(::Puzzle{2020, 5, 1}, input) = max(seatId.(input)...)

notVeryFrontOrBack((row, column),) = row != 0 && row != 127
hasGap((left, right),) = left == right - 2

AdjacentElements(c) = Iterators.zip(c, Iterators.drop(c, 1))

function solve(::Puzzle{2020, 5, 2}, input)
    seats = decode.(input)
    takenSeats = seatId.(filter(notVeryFrontOrBack, seats)) |> sort

    (left, right) = Iterators.filter(hasGap, AdjacentElements(takenSeats)) |> first
    return left + 1
end

@testset "Day 5" begin
    @test(seatId("BFFFBBFRRR") == 567)
    @test(seatId("FFFBBBFRRR") == 119)
    @test(seatId("BBFFBBFRLL") == 820)
    @test(solve(Puzzle(5, 1), input(Puzzle(5, 1))) == 894)
    @test(solve(Puzzle(5, 2), input(Puzzle(5, 2))) == 579)
end


text:
# syntax: proto3
using ProtoBuf
import ProtoBuf.meta

mutable struct SourceContext <: ProtoType
    file_name::AbstractString
    SourceContext(; kwargs...) = (o=new(); fillunset(o); isempty(kwargs) || ProtoBuf._protobuild(o, kwargs); o)
end #mutable struct SourceContext

export SourceContext


text:
export Status
export SlackMethod
export AbstractHttp
export AbstractHttpResponse
export RequestException
export makerequest

import Base.==

abstract AbstractHttp
abstract AbstractHttpResponse

statuscode(r::AbstractHttpResponse) = r.status
text(r::AbstractHttpResponse) = r.text

immutable Status
    ok::Bool
    error::Nullable{UTF8String}
    warnings::Nullable{UTF8String}
end

function nulleq{T}(a::Nullable{T}, b::Nullable{T})
    !isnull(a) && !isnull(b) && get(a) == get(b) || isnull(a) && isnull(b)
end

function ==(a::Status, b::Status)
    nulleq(a.error, b.error) && nulleq(a.warnings, b.warnings) && a.ok == b.ok
end

type RequestException <: Exception
    error::UTF8String
end
type HttpException <: Exception end

function makerequest(m::Any, http::AbstractHttp)
    query_vars = toquery(m)
    name = method_name(typeof(m))
    url = "https://slack.com/api/$(name)"
    resp = post(http, url; query=query_vars)
    if statuscode(resp) != 200
        throw(HttpException())
    end
    body = text(resp)
    status = DandelionSlack.deserialize(Status, body)
    if !status.ok
        throw(RequestException(get(status.error)))
    end
    response_type = getresponsetype(typeof(m))

    status, DandelionSlack.deserialize(response_type, body)
end


text:
using LinearAlgebra
using CompScienceMeshes
using SauterSchwab3D
using StaticArrays

const pI   = point(6,5,3)
const pII  = point(5,2,3)
const pIII = point(7,1,0)
const pIV  = point(3,4,0)

const qIII = point(7,5,6)

const P = simplex(pI,pII,pIV,pIII)
const Q = simplex(pI,qIII,pII)

const sing = SauterSchwab3D.singularity_detection(P,Q)

Accuracy2 = 20
ce_ref = CommonEdge5D(sing,SauterSchwab3D._legendre(Accuracy2,0.0,1.0))

function integrand(x,y)
      return ((x-pI)'*(y-pIV))*exp(-im*1*norm(x-y))/(4pi*norm(x-y))
end

function INTEGRAND(u,v)
   n1 = neighborhood(P,u)
   n2 = neighborhood(Q,v)
   x = cartesian(n1)
   y = cartesian(n2)
   output = integrand(x,y)*jacobian(n1)*jacobian(n2)

   return output
end

print("Ref: ")
ref = sauterschwab_parameterized(INTEGRAND, ce_ref)
println(ref)
println()

res_tp =[]
res_sp =[]
res_gm =[]
n1 = []
n2 = []
n3 = []

for i in 2:1:17
   Accuracy = i
   ce = CommonEdge5D(sing,SauterSchwab3D._legendre(Accuracy,0.0,1.0))

   int_tp = sauterschwab_parameterized(INTEGRAND, ce)

   num_pts = 5*length(ce.qps)^5
   push!(n1,num_pts)

   push!(res_tp,int_tp)

end

for i in 2:1:7
   Accuracy = i
   ce_s = CommonEdge5D_S(sing,(SauterSchwab3D._legendre(Accuracy,0.0,1.0),
                          SauterSchwab3D._shunnham2D(Accuracy),
                          SauterSchwab3D._shunnham3D(Accuracy)))

   int_sp = sauterschwab_parameterized(INTEGRAND, ce_s)

   num_pts = 3*length(ce_s.qps[1])*length(ce_s.qps[2])^2 + 2*length(ce_s.qps[2])*length(ce_s.qps[3])
   push!(n2,num_pts)
   push!(res_sp,int_sp)
end

for i in 2:1:12
   Accuracy = i
   ce_gm = CommonEdge5D_S(sing,(SauterSchwab3D._legendre(Accuracy,0.0,1.0),
                          SauterSchwab3D._grundmannMoeller2D(2*Accuracy-1),
                          SauterSchwab3D._grundmannMoeller3D(2*Accuracy-1)))

   int_gm = sauterschwab_parameterized(INTEGRAND, ce_gm)

   num_pts = 3*length(ce_gm.qps[1])*length(ce_gm.qps[2])^2 + 2*length(ce_gm.qps[2])*length(ce_gm.qps[3])
   push!(n3,num_pts)
   push!(res_gm,int_gm)
end

err_tp = norm.(res_tp.-ref)/norm(ref)
err_sp = norm.(res_sp.-ref)/norm(ref)
err_gm = norm.(res_gm.-ref)/norm(ref)

println(err_tp)
println(err_sp)
println(err_gm)

using Plots
plot( yaxis=:log, xaxis=:log, fontfamily="Times")
plot!(n1,err_tp, label="Gauss Tensor-Product",markershape=:circle)
plot!(n2,err_sp, label="Simplex Tensor-Product",markershape=:rect)
#plot!(n3,err_gm, label="Simplex-Product GM",markershape=:x)
plot!(xlims=(5e1,1e5),ylims=(1e-6,1))
plot!(xlabel="#Quad. pts/Func. evals", ylabel="Rel. Error.", title="Common Edge 5D",legend=:bottomleft)

using BenchmarkTools

ref = -4.263868529192268 - 0.14818591340482545im

ce = CommonEdge5D(sing,SauterSchwab3D._legendre(6,0.0,1.0))

int_tp = sauterschwab_parameterized(INTEGRAND, ce)

num_pts = 5*length(ce.qps)^5
println("#Pts: ",num_pts)
err_tp = norm.(int_tp.-ref)/norm(ref)
println("Rel Err: ",err_tp)
@time for i in 1:100  sauterschwab_parameterized(INTEGRAND, ce) end

ce_s = CommonEdge5D_S(sing,(SauterSchwab3D._legendre(7,0.0,1.0),
                          SauterSchwab3D._shunnham2D(7),
                          SauterSchwab3D._shunnham3D(7)))

int_sp = sauterschwab_parameterized(INTEGRAND, ce_s)
num_pts = 3*length(ce_s.qps[1])*length(ce_s.qps[2])^2 + 2*length(ce_s.qps[2])*length(ce_s.qps[3])

println("#Pts: ",num_pts)
err_sp = norm.(int_sp.-ref)/norm(ref)
println("Rel Err: ",err_sp)
@time for i in 1:100 sauterschwab_parameterized(INTEGRAND, ce_s) end
#=
 ce_gm = CommonEdge5D_S((SauterSchwab3D._legendre(7,0.0,1.0),
                          SauterSchwab3D._grundmannMoeller2D(2*7-1),
                          SauterSchwab3D._grundmannMoeller3D(2*7-1)))

   int_gm = sauterschwab_parameterized(INTEGRAND, ce_s)

   num_pts = 3*length(ce_gm.qps[1])*length(ce_gm.qps[2])^2 + 2*length(ce_gm.qps[2])*length(ce_gm.qps[3])
println("#Pts: ",num_pts)
err_gm = norm.(int_gm.-ref)/norm(ref)
println("Rel Err: ",err_gm)
=#


text:
"""
The base abstract type for the collection of candidate solutions
in the population-based optimization methods.
"""
abstract type Population end

"""
The base abstract types for population that also stores the candidates
fitness.

`F` is the fitness type.
"""
abstract type PopulationWithFitness{F} <: Population end

const AbstractPopulationMatrix = AbstractMatrix{Float64}

"""
The simplest `Population` implementation -- a matrix of floats, each column is an individual.
"""
const PopulationMatrix = Matrix{Float64}

popsize(pop::AbstractPopulationMatrix) = size(pop, 2)
numdims(pop::AbstractPopulationMatrix) = size(pop, 1)
params_mean(pop::AbstractPopulationMatrix) = mean(pop, dims=1)
params_std(pop::AbstractPopulationMatrix) = std(pop, dims=1)

popsize(pop::AbstractVector{<:Candidate}) = length(pop)
numdims(pop::AbstractVector{<:Candidate}) = isempty(pop) ? 0 : length(pop[1].params)

viewer(pop::PopulationMatrix, indi_ix) = view(pop, :, indi_ix)

"""
The default implementation of `PopulationWithFitness{F}`.
"""
mutable struct FitPopulation{F} <: PopulationWithFitness{F}
    # The population is a matrix of floats, each column being an individual.
    individuals::PopulationMatrix

    nafitness::F
    fitness::Vector{F}
    ntransient::Int                  # how many transient members are in the population

    candi_pool::Vector{Candidate{F}} # pool of reusable candidates

    function FitPopulation(individuals::AbstractPopulationMatrix,
                           nafitness::F,
                           fitness::Vector{F} = fill(nafitness, popsize(individuals));
                           ntransient::Integer=0) where {F}
        popsize(individuals) == length(fitness) ||
            throw(DimensionMismatch("Fitness vector length does not match the population size"))
        new{F}(individuals, nafitness, copy(fitness), ntransient,
               Vector{Candidate{F}}())
    end
end

FitPopulation(fs::FitnessScheme, individuals::PopulationMatrix;
              ntransient::Integer=0) =
  FitPopulation(individuals, nafitness(fs), ntransient=ntransient)

FitPopulation(fs::FitnessScheme, popsize::Int = 100, dims::Int = 1;
              ntransient::Integer=0) =
  FitPopulation(fs, PopulationMatrix(undef, dims, popsize); ntransient=ntransient)

popsize(pop::FitPopulation) = popsize(pop.individuals)-pop.ntransient
numdims(pop::FitPopulation) = numdims(pop.individuals)

# resize the population
function Base.resize!(pop::FitPopulation, newpopsize::Integer)
    new_individuals = PopulationMatrix(undef, numdims(pop), newpopsize+pop.ntransient)
    new_individuals[:, 1:min(newpopsize,popsize(pop))] = view(pop.individuals, :, 1:min(newpopsize,popsize(pop)))
    pop.individuals = new_individuals
    resize!(pop.fitness, newpopsize + pop.ntransient)
    pop
end

# indices of the persistent individuals
@inline persistent_range(pop::FitPopulation) = 1:popsize(pop)
# indices of the transient individuals
@inline transient_range(pop::FitPopulation) = popsize(pop):(popsize(pop)+pop.ntransient-1)
persistent_individuals(pop::FitPopulation) = view(pop.individuals, :, persistent_range(pop))

params_mean(pop::FitPopulation) = mean(persistent_individuals(pop), 1)
params_std(pop::FitPopulation) = std(persistent_individuals(pop), 1)

fitness(pop::FitPopulation, ix::Int) = pop.fitness[ix]

Base.getindex(pop::FitPopulation, rows, cols) = pop.individuals[rows, cols]
Base.getindex(pop::FitPopulation, indi_ixs) = pop.individuals[:, indi_ixs]

"""
    viewer(population, individual_index)

Get vector-slice of the population matrix for the specified
individual.
Does not allocate any additional space, while still providing the same lookup performance.
"""
viewer(pop::FitPopulation, indi_ix) = view(pop.individuals, :, indi_ix)

Base.setindex!(pop::FitPopulation, indi::Individual, ::Colon, indi_ix::Integer) =
    setindex!(pop, indi, indi_ix)

function Base.setindex!(pop::FitPopulation, indi::Individual, indi_ix::Integer)
    pop.individuals[:, indi_ix] = indi
    pop.fitness[indi_ix] = pop.nafitness
    return pop
end

function Base.setindex!(pop::FitPopulation{F}, indi::FitIndividual{F}, indi_ix::Integer) where F
    pop.individuals[:, indi_ix] = params(indi)
    pop.fitness[indi_ix] = fitness(indi)
    return pop
end

function Base.append!(pop::FitPopulation{F}, extra_pop::FitPopulation{F}) where {F}
    pop.ntransient == 0 || throw(error("Appending to the population with transients not supported (yet)"))
    numdims(pop) == numdims(extra_pop) ||
        throw(DimensionMismatch("Cannot append population, "*
                                "the number of parameters differs "*
                                "($(numdims(pop)) vs $(numdims(extra_pop)))"))
    pop.individuals = hcat(pop.individuals, extra_pop.individuals)
    append!(pop.fitness, extra_pop.fitness)
    return pop
end

fitness_type(pop::FitPopulation{F}) where {F} = F
candidate_type(pop::FitPopulation{F}) where {F} = Candidate{F}

"""
    acquire_candi(pop::FitPopulation[, {ix::Int, candi::Candidate}])

Get individual from a pool, or create one if the pool is empty.
By default the individual is not initialized, but if `ix` or `candi` is specified,
the corresponding fields of the new candidate are set to the given values.
"""
function acquire_candi(pop::FitPopulation{F}) where {F}
    if isempty(pop.candi_pool)
        return Candidate{F}(fill!(Individual(undef, numdims(pop)), NaN), -1, pop.nafitness)
    end
    res = pop!(pop.candi_pool)
    # reset reference to genetic operation
    res.extra = NO_GEN_OP
    res.tag = 0
    return res
end

acquire_candis(pop::FitPopulation{F}, n::Integer) where F =
    Candidate{F}[acquire_candi(pop) for _ in 1:n]

# Get an individual from a pool and sets it to ix-th individual from population.
function acquire_candi(pop::FitPopulation, ix::Int)
    x = acquire_candi(pop)
    copyto!(x.params, viewer(pop, ix))
    x.index = ix
    x.fitness = fitness(pop, ix)
    return x
end

# Get an individual from a pool and sets it to another candidate.
acquire_candi(pop::FitPopulation{F}, candi::Candidate{F}) where F =
    copy!(acquire_candi(pop), candi)

"""
Put the candidate back to the pool.
"""
release_candi(pop::FitPopulation{F}, candi::Candidate{F}) where F =
    push!(pop.candi_pool, candi)

"""
Put the candidate back into the pool and copy the values
into the corresponding individual of the population (`candi.index` should be set).
"""
function accept_candi!(pop::FitPopulation{F}, candi::Candidate{F}) where F
    pop.individuals[:, candi.index] = candi.params
    pop.fitness[candi.index] = candi.fitness
    release_candi(pop, candi)
end

"""
Reset the candidate fitness.

Need it when the candidate parameters have changed, but the stored fitness
is still for the old parameter set.
"""
function reset_fitness!(candi::Candidate{F}, pop::FitPopulation{F}) where F
    candi.fitness = pop.nafitness
    return candi
end

candi_pool_size(pop::FitPopulation) = length(pop.candi_pool)

"""
Generate a population for a given problem.

The default method to generate a population, uses Latin Hypercube Sampling.
"""
function population(problem::OptimizationProblem,
                    options::Parameters = EMPTY_PARAMS,
                    nafitness::F = nafitness(fitness_scheme(problem));
                    ntransient::Integer = 0) where F
    if !haskey(options, :Population)
        pop = rand_individuals_lhs(search_space(problem), get(options, :PopulationSize, 50) + ntransient)
    else
        pop = options[:Population]
    end
    if isa(pop, Population)
        return pop
    elseif isa(pop, AbstractPopulationMatrix)
        return FitPopulation(pop, nafitness, ntransient=ntransient)
    else
        throw(ArgumentError("\"Population\" parameter is of unsupported type: $(typeof(pop))"))
    end
end


text:
using PlotUtils
using Test

# TODO: real tests

# ----------------------
# colors

const C = RGBA{Float64}

@testset "colors" begin

    @test plot_color(nothing) == C(0,0,0,0)
    @test plot_color(false) == C(0,0,0,0)
    @test_throws ErrorException plot_color(true)

    @test plot_color(:red) == parse(C, :red)
    @test plot_color("red") == parse(C, "red")
    @test_throws ErrorException plot_color("notacolor")

    @test plot_color(colorant"red") == C(1,0,0,1)

    grad = cgrad()
    @test typeof(grad) == ColorGradient
    @test plot_color(grad) === grad

    grad = cgrad([:red, "blue"])
    @test grad.colors == C[colorant"red", colorant"blue"]
    @test grad.values == collect(range(0,stop=1,length=2))

    grad = cgrad([:red, "blue"], alpha = 0.5)
    @test grad.colors == C[C(1,0,0,0.5), C(0,0,1,0.5)]
    @test grad.values == collect(range(0,stop=1,length=2))

    grad = cgrad([:red,:blue], [0,0.1,1])
    @test grad.colors == C[C(1,0,0), C(0.5,0,0.5), C(0,0,1)]
    @test grad.values == [0,0.1,1]

    cs = plot_color(rand(10))
    @test typeof(cs) == Vector{C}
    @test length(cs) == 10

    cs = plot_color(rand(4,4))
    @test typeof(cs) == Matrix{C}
    @test length(cs) == 16
    @test size(cs) == (4,4)

    cs = plot_color(rand(10), 0.5)
    @test typeof(cs) == Vector{C}
    @test length(cs) == 10
    for c in cs
        @test alpha(c) == 0.5
    end

    cs = plot_color(rand(4,4), 0.5)
    @test typeof(cs) == Matrix{C}
    @test length(cs) == 16
    @test size(cs) == (4,4)
    for c in cs
        @test alpha(c) == 0.5
    end
end

# ----------------------
# gradients

@testset "gradients" begin
    grad = cgrad(:inferno)
    @test length(grad.colors) == 30
    @test length(grad.values) == 30
    @test RGB(grad.colors[1]) == RGB(0.001462, 0.000466, 0.013866)
    @test RGB(grad.colors[end]) == RGB(0.988362, 0.998364, 0.644924)
end

# ----------------------
# ticks

@testset "ticks" begin
    @test optimize_ticks(-1,2) == ([-1.0,0.0,1.0,2.0],-1.0,2.0)

    @testset "small range $x, $(i)ϵ" for x in exp10.(-12:12), i in -5:5
        y = x + i*eps(x)
        x,y = minmax(x,y)
        ticks = PlotUtils.optimize_ticks(x, y)[1]
        @test issorted(ticks)
        @test all(x .<= ticks .<= y)
        # Fails:
        # @test allunique(ticks)
    end
end


text:
module Codecs

using Libz
import Snappy

export Codec

struct Codec{Name}
end

function create(name::String)
    if name == "deflate"
        Codec{:deflate}()
    elseif name == "null"
        Codec{:null}()
    elseif name == "snappy"
        Codec{:snappy}()
    end
end

name(::Codec{:null}) = "null"
name(::Codec{:deflate}) = "deflate"
name(::Codec{:snappy}) = "snappy"

compress(::Codec{:null}, data) = data
decompress(::Codec{:null}, data) = data

function compress(::Codec{:deflate}, data)
    stream = Libz.ZlibDeflateOutputStream(data; raw = true, gzip = false)
    read(stream)
end

function decompress(::Codec{:deflate}, data)
    stream = Libz.ZlibInflateInputStream(data; raw = true, gzip = false)
    read(stream)
end

compress(::Codec{:snappy}, data) = Snappy.compress(data)
decompress(::Codec{:snappy}, data) = Snappy.uncompress(data)

end


text:
module baobzi

mutable struct baobzi_t
end

mutable struct baobzi_input_t
    func::Ptr{Cvoid}
    data::Ptr{Cvoid}
    dim::Cint
    order::Cint
    tol::Cdouble
end

function init(fin, dim, order, center, half_length, tol)
    fanon = (x, p) -> fin(x)
    fbind = @cfunction($fanon, Cdouble, (Ptr{Cdouble}, Ptr{Cvoid},))
    input = baobzi_input_t(fbind.ptr, C_NULL, dim, order, tol)

    output_ptr = ccall(
        (:baobzi_init, :libbaobzi),
        Ptr{baobzi_t},
        (Ref{baobzi_input_t}, Ptr{Cdouble}, Ptr{Cdouble},),
        input, center, half_length
    )

    return output_ptr
end

function eval(func::Ptr{baobzi_t}, x)
    return ccall(
        (:baobzi_eval, :libbaobzi),
        Cdouble,
        (Ptr{Cvoid}, Ptr{Cdouble},),
        func, x
    )
end

function free(func::Ptr{baobzi_t})
    ccall(
        (:baobzi_free, :libbaobzi),
        Ptr{Cvoid},
        (Ptr{Cvoid},),
        func
    )
end

function save(func::Ptr{baobzi_t}, filename::String)
    ccall(
        (:baobzi_save, :libbaobzi),
        Ptr{Cvoid},
        (Ptr{Cvoid}, Cstring),
        func, filename
    )
end

function restore(filename::String)
    output_ptr = ccall(
        (:baobzi_restore, :libbaobzi),
        Ptr{baobzi_t},
        (Cstring,),
        filename
    )

    return output_ptr
end

end

max_stars_repo_name: ahbarnett/baobzi

max_issues_repo_name: ahbarnett/baobzi

max_forks_repo_name: ahbarnett/baobzi