text:
using LightGBM
using Test
using DelimitedFiles
using StatsBase
using DataFrames,CSV,HTTP

@testset "LightGBM.jl" begin
    # Use binary example for generic tests.
    println("Start runtest.jl ENV[\"LIGHTGBM_PATH\"] is ",ENV["LIGHTGBM_PATH"])
    if isfile( joinpath(ENV["LIGHTGBM_PATH"],"examples/binary_classification/binary.test") )
        binary_test = readdlm(joinpath(ENV["LIGHTGBM_PATH"] , "examples/binary_classification/binary.test"), '\t');
        binary_train = readdlm(joinpath(ENV["LIGHTGBM_PATH"] , "examples/binary_classification/binary.train"), '\t');
    else
        res = HTTP.get("https://raw.githubusercontent.com/microsoft/LightGBM/v2.3.1/examples/binary_classification/binary.test");
        work=String(res.body);
        binary_test =convert(Matrix,CSV.read(IOBuffer(work),delim='\t',header=false));

        res = HTTP.get("https://raw.githubusercontent.com/microsoft/LightGBM/v2.3.1/examples/binary_classification/binary.train");
        work=String(res.body);
        binary_train =convert(Matrix,CSV.read(IOBuffer(work),delim='\t',header=false));
    end

    X_train = binary_train[:, 2:end]
    y_train = binary_train[:, 1]
    X_test = binary_test[:, 2:end]
    y_test = binary_test[:, 1]

    # Test wrapper functions.
    train_ds = LightGBM.LGBM_DatasetCreateFromMat(X_train, "objective=binary");
    @test LightGBM.LGBM_DatasetGetNumData(train_ds) == 7000
    @test LightGBM.LGBM_DatasetGetNumFeature(train_ds) == 28
    @test LightGBM.LGBM_DatasetSetField(train_ds, "label", y_train) == nothing
    @test LightGBM.LGBM_DatasetGetField(train_ds, "label") == y_train
    bst = LightGBM.LGBM_BoosterCreate(train_ds, "lambda_l1=10. metric=auc, verbosity=-1")

    test_ds = LightGBM.LGBM_DatasetCreateFromMat(X_test, "objective=binary", train_ds);
    @test LightGBM.LGBM_DatasetSetField(test_ds, "label", y_test) == nothing
    @test LightGBM.LGBM_BoosterAddValidData(bst, test_ds) == nothing
    @test LightGBM.LGBM_BoosterUpdateOneIter(bst) == 0
    @test LightGBM.LGBM_BoosterGetEvalCounts(bst) == 1
    @test LightGBM.LGBM_BoosterGetEvalNames(bst)[1] == "auc"

    # Test binary estimator.
    estimator = LightGBM.LGBMBinary(num_iterations = 20,
                                    learning_rate = .1,
                                    early_stopping_round = 1,
                                    feature_fraction = .8,
                                    bagging_fraction = .9,
                                    bagging_freq = 1,
                                    num_leaves = 1000,
                                    metric = ["auc", "binary_logloss"],
                                    is_training_metric = true,
                                    max_bin = 255,
                                    min_sum_hessian_in_leaf = 0.,
                                    min_data_in_leaf = 1);

    # Test fitting.
    LightGBM.fit(estimator, X_train, y_train, verbosity = 0);
    LightGBM.fit(estimator, X_train, y_train, (X_test, y_test), verbosity = 0); #boost_from_average=false

    # Test setting feature names
    jl_feature_names = ["testname_$i" for i in 1:28]
    LightGBM.LGBM_DatasetSetFeatureNames(estimator.booster.datasets[1], jl_feature_names)
    lgbm_feature_names = LightGBM.LGBM_DatasetGetFeatureNames(estimator.booster.datasets[1])
    @test jl_feature_names == lgbm_feature_names

    # Test prediction, and loading and saving models.
    test_filename = tempname()
        LightGBM.savemodel(estimator, test_filename);
    try
        pre = LightGBM.predict(estimator, X_train, verbosity = 0);
        LightGBM.loadmodel(estimator, test_filename);
        post = LightGBM.predict(estimator, X_train, verbosity = 0);
        @test isapprox(pre, post)
    finally
        rm(test_filename);
    end

    # Test cross-validation.
    splits = (collect(1:3500), collect(3501:7000));
    LightGBM.cv(estimator, X_train, y_train, splits; verbosity = 0);

    # Test exhaustive search.
    params = [Dict(:num_iterations => num_iterations,
                   :num_leaves => num_leaves) for
                   num_iterations in (1, 2),
                   num_leaves in (5, 10)];
    LightGBM.search_cv(estimator, X_train, y_train, splits, params; verbosity = 0);

    # Test regression estimator.
    if isfile( joinpath(ENV["LIGHTGBM_PATH"],"examples/regression/regression.test") )
        regression_test = readdlm(joinpath(ENV["LIGHTGBM_PATH"] , "examples/regression/regression.test"), '\t');
        regression_train = readdlm(joinpath(ENV["LIGHTGBM_PATH"] , "examples/regression/regression.train"), '\t');
    else
        res = HTTP.get("https://raw.githubusercontent.com/microsoft/LightGBM/v2.3.1/examples/regression/regression.test");
        work=String(res.body);
        regression_test =convert(Matrix,CSV.read(IOBuffer(work),delim='\t',header=false));

        res = HTTP.get("https://raw.githubusercontent.com/microsoft/LightGBM/v2.3.1/examples/regression/regression.train");
        work=String(res.body);
        regression_train =convert(Matrix,CSV.read(IOBuffer(work),delim='\t',header=false));
    end

    X_train = regression_train[:, 2:end]
    y_train = regression_train[:, 1]
    X_test = regression_test[:, 2:end]
    y_test = regression_test[:, 1]

    estimator = LightGBM.LGBMRegression(num_iterations = 100,
                                        learning_rate = .05,
                                        feature_fraction = .9,
                                        bagging_fraction = .8,
                                        bagging_freq = 5,
                                        num_leaves = 31,
                                        metric = ["l2"],
                                        metric_freq = 1,
                                        is_training_metric = true,
                                        max_bin = 255,
                                        min_sum_hessian_in_leaf = 5.,
                                        min_data_in_leaf = 100,
                                        max_depth = -1);

    scores = LightGBM.fit(estimator, X_train, y_train, (X_test, y_test), verbosity = 0);
    @test scores["test_1"]["l2"][end] < .5

    # Test multiclass estimator.
    if isfile( joinpath(ENV["LIGHTGBM_PATH"],"examples/multiclass_classification/multiclass.test") )
        multiclass_test = readdlm(joinpath(ENV["LIGHTGBM_PATH"] , "examples/multiclass_classification/multiclass.test"), '\t');
        multiclass_train = readdlm(joinpath(ENV["LIGHTGBM_PATH"] , "examples/multiclass_classification/multiclass.train"), '\t');
    else
        res = HTTP.get("https://raw.githubusercontent.com/microsoft/LightGBM/v2.3.1/examples/multiclass_classification/multiclass.test");
        work=String(res.body);
        multiclass_test =convert(Matrix,CSV.read(IOBuffer(work),delim='\t',header=false));

        res = HTTP.get("https://raw.githubusercontent.com/microsoft/LightGBM/v2.3.1/examples/multiclass_classification/multiclass.train");
        work=String(res.body);
        multiclass_train =convert(Matrix,CSV.read(IOBuffer(work),delim='\t',header=false));
    end

    X_train = Matrix(multiclass_train[:, 2:end])
    y_train = Array(multiclass_train[:, 1])
    X_test = Matrix(multiclass_test[:, 2:end])
    y_test = Array(multiclass_test[:, 1])

    estimator = LightGBM.LGBMMulticlass(num_iterations = 100,
                                        learning_rate = .05,
                                        feature_fraction = .9,
                                        bagging_fraction = .8,
                                        bagging_freq = 5,
                                        num_leaves = 31,
                                        metric = ["multi_logloss"],
                                        metric_freq = 1,
                                        is_training_metric = true,
                                        max_bin = 255,
                                        min_sum_hessian_in_leaf = 5.,
                                        min_data_in_leaf = 100,
                                        num_class = 5,
                                        early_stopping_round = 10);

    scores = LightGBM.fit(estimator, X_train, y_train, (X_test, y_test), verbosity = 0);
    @test scores["test_1"]["multi_logloss"][end] < 1.4

    # Test row major multiclass
    X_train = Matrix(multiclass_train[:, 2:end]')
    X_test = Matrix(multiclass_test[:, 2:end]')

    estimator = LightGBM.LGBMMulticlass(num_iterations = 100,
                                        learning_rate = .05,
                                        feature_fraction = .9,
                                        bagging_fraction = .8,
                                        bagging_freq = 5,
                                        num_leaves = 31,
                                        metric = ["multi_logloss"],
                                        metric_freq = 1,
                                        is_training_metric = true,
                                        max_bin = 255,
                                        min_sum_hessian_in_leaf = 5.,
                                        min_data_in_leaf = 100,
                                        num_class = 5,
                                        early_stopping_round = 10);

    scores = LightGBM.fit(estimator, X_train, y_train, (X_test, y_test), verbosity = 0,
                          is_row_major = true);
    @test scores["test_1"]["multi_logloss"][end] < 1.4

    include("weightsTest.jl")
    include("initScoreTest.jl")

end


text:
#=

Slice sampling for EGeoHiSSE

Ignacio Quintero Mächler

t(-_-t)

November 20 2017

=#

"""
    slice_sampler(tip_val     ::Dict{Int64,Array{Float64,1}},
                  ed          ::Array{Int64,2},
                  el          ::Array{Float64,1},
                  x           ::Array{Float64,1},
                  y           ::Array{Float64},
                  cov_mod     ::String,
                  out_file    ::String,
                  h           ::Int64;
                  constraints ::NTuple{N,String}  = (" ",),
                  niter       ::Int64             = 10_000,
                  nthin       ::Int64             = 10,
                  λpriors     ::Float64           = .1,
                  μpriors     ::Float64           = .1,
                  gpriors     ::Float64           = .1,
                  lpriors     ::Float64           = .1,
                  qpriors     ::Float64           = .1,
                  βpriors     ::NTuple{2,Float64} = (0.0, 5.0),
                  optimal_w   ::Float64           = 0.8,
                  screen_print::Int64             = 5) where {N}

Run slice-sampling Markov Chain given posterior function.
"""
function slice_sampler(lhf         ::Function,
                       p           ::Array{Float64,1},
                       fp          ::Array{Float64,1},
                       nnps        ::Array{Int64,1},
                       nps         ::Array{Int64,1},
                       phid        ::Array{Int64,1},
                       mvps        ::Array{Array{Int64,1},1},
                       nngps       ::Array{Array{Bool,1},1},
                       mvhfs       ::Array{Array{Int64,1},1},
                       npars       ::Int64,
                       niter       ::Int64,
                       nthin       ::Int64,
                       nburn       ::Int64,
                       ntakew      ::Int64,
                       winit       ::Float64,
                       optimal_w   ::Float64,
                       screen_print::Int64)

  # estimate optimal w
  p, fp, w =
    w_sampler(lhf, p, fp, nnps, nps, phid, mvps, nngps, mvhfs,
      npars, optimal_w, screen_print, nburn, ntakew, winit)

  # slice-sampler
  its, hlog, ps =
    loop_slice_sampler(lhf, p, fp, nnps, nps, phid, mvps, nngps, mvhfs,
      w, npars, niter, nthin, screen_print)

  # save samples
  R = hcat(its, hlog, ps)

  return R
end

# """
#     slice_sampler(tip_val    ::Dict{Int64,Array{Float64,1}},
#                   edges      ::Array{Int64,2},
#                   edlen      ::Array{Float64,1},
#                   out_file   ::String;
#                   constraints::String  = NaN,
#                   niter      ::Int64   = 10_000,
#                   nthin      ::Int64   = 10,
#                   model      ::String  = "musse",
#                   λpriors    ::Float64 = .1,
#                   μpriors    ::Float64 = .1,
#                   qpriors    ::Float64 = .1)

# Run slice-sampling Markov Chain for MuSSE model.
# """
# function slice_sampler(tip_val    ::Dict{Int64,Array{Float64,1}},
#                        edges      ::Array{Int64,2},
#                        edlen      ::Array{Float64,1},
#                        out_file   ::String;
#                        constraints::NTuple{N,String} = (" ",),
#                        niter      ::Int64            = 10_000,
#                        nthin      ::Int64            = 10,
#                        λpriors    ::Float64          = .1,
#                        μpriors    ::Float64          = .1,
#                        qpriors    ::Float64          = .1) where {N}

#   # k areas
#   k = length(tip_val[1])

#   # make ode and define parameters
#   mod_ode = make_musse(k)

#   npars = k + k*k
#   p = fill(0.2,npars)
#   δ = length(tip_val)/sum(edlen)
#   p[1:k]      .= δ + rand()    # set λs
#   p[(k+1):2k] .= p[1] - δ      # set μs

#   pardic = build_par_names(k)

#   # parameter update
#   pupd = Base.OneTo(npars)

#   #constraints
#   conp = set_constraints(constraints, pardic)

#   # remove contraints for being updated
#   pupd = setdiff(pupd, keys(conp))

#   # create likelihood, prior and posterior functions
#   llf = make_llf(tip_val, edges, edlen, mod_ode, p, sbrlen = sum(edlen))
#   lpf = make_lpf(λpriors, μpriors, qpriors, k)
#   lhf = make_lhf(llf, lpf, conp)

#   # set up slice-sampling
#   nlogs = fld(niter,nthin)
#   its   = zeros(Float64,nlogs)
#   h     = zeros(Float64,nlogs)
#   ps    = zeros(Float64,nlogs,npars)

#   lthin = 0::Int64
#   lit   = 0::Int64

#   # cat
#   printstyled("running MuSSE model \n", color=:green)

#   # estimate w
#   p, w = w_sampler(lhf, p, pupd, npars)

#   # start iterations
#   prog = Progress(niter, 5, "running slice-sampler....", 20)

#   hc = lhf(p)::Float64

#   for it in Base.OneTo(niter)

#     for j in pupd
#       S     = hc - Random.randexp()
#       L, R  = find_nonneg_int(p, j, S, lhf, w[j])
#       p, hc = sample_int(p, j, L, R, S, lhf)
#     end

#     # log samples
#     lthin += 1
#     if lthin == nthin
#       @inbounds begin
#         lit += 1
#         setindex!(its, it, lit)
#         setindex!(h,   hc, lit)
#         setindex!(ps,   p, lit, :)
#       end
#       lthin = 0
#     end

#     next!(prog)
#   end

#   # save samples
#   R = hcat(its, h, ps)

#   # column names
#   col_nam = ["Iteration", "Posterior"]

#   # add λ names
#   for i in 0:(k-1)
#     push!(col_nam, "lamdba$i")
#   end

#   # add μ names
#   for i in 0:(k-1)
#     push!(col_nam, "mu$i")
#   end

#   # add q names
#   for j in 0:(k-1), i in 0:(k-1)
#     if i == j
#       continue
#     end
#     push!(col_nam, "q$i$j")
#   end

#   R = vcat(reshape(col_nam, 1, lastindex(col_nam)), R)

#   writedlm(out_file*".log", R)

#   return R
# end

ext: jl

max_stars_repo_name: hmorlon/JPANDA

max_issues_repo_name: hmorlon/JPANDA

max_forks_repo_name: hmorlon/JPANDA


text:
using BSON
using PyCall
matplotlib = pyimport("matplotlib")
plt = pyimport("matplotlib.pyplot")
matplotlib.use("Agg")
using DelimitedFiles

# Load current dir
pushfirst!(PyVector(pyimport("sys")."path"), "")
plot_yz = pyimport("plot_yz")
blue2red = pyimport("blue2red")

# multiple pages
PdfPages = pyimport("matplotlib.backends.backend_pdf").PdfPages

include("sample/sample_lam.jl")

pyrange(n) = collect(range(0, stop=n-1))
function boxplot(x; showmeans=true, whis=[2.5, 97.5], showfliers=false, kw...)
  plt.boxplot(x, showmeans=showmeans, whis=whis, showfliers=showfliers; kw...)
end

function add_gridlines_Z(Z)
  J, K = size(Z)
  for j in pyrange(J)
    plt.axhline(y=j+.5, color="grey", linewidth=.5)
  end

  for k in pyrange(K)
    plt.axvline(x=k+.5, color="grey", linewidth=.5)
  end
end

axhlines(x; kw...) = for xi in x plt.axhline(xi; kw...) end
cm_greys = plt.cm.get_cmap("Greys", 5)

function plot_Z(Z; colorbar=true)
  J, K = size(Z)
  p = plt.imshow(Z, aspect="auto", vmin=0, vmax=1, cmap=cm_greys)
  add_gridlines_Z(Z)
  plt.yticks(pyrange(J), pyrange(J) .+ 1, fontsize=10)
  plt.xticks(pyrange(K), pyrange(K) .+ 1, fontsize=10, rotation=90)
  if colorbar
    plt.colorbar()
  end
  return p
end

# For BSON
using Cytof5, Flux, Distributions

function post_process(output_path; thresh=.99, w_thresh=.01)
  VMIN, VMAX = VLIM = (-4, 4)
  # cm = blue2red.cm(9)

  out = BSON.load(output_path)
  if :simdat in keys(out)
    has_simdat = true
    simdat = decompress_simdat!(out[:simdat])
  else
    has_simdat = false
    y = Matrix{Float64}.(out[:y])
  end
  RESULTS_DIR = join(split(output_path, "/")[1:end-1], "/")
  IMG_DIR = "$(RESULTS_DIR)/img/"
  mkpath(IMG_DIR)

  c = out[:c]
  elbo = out[:metrics][:elbo] / sum(c.N)
  state = out[:state]
  state_hist = out[:state_hist]
  metrics = out[:metrics]

  println("metrics...")
  # ELBO
  plt.plot(elbo[200:end])
  plt.xlabel("iteration")
  plt.ylabel("ELBO / sum(N)")
  plt.savefig("$(IMG_DIR)/elbo.pdf")
  plt.close()

  # Metrics
  plt.figure()
  begin
    local counter = 0
    for (k, m) in metrics
      counter += 1
      plt.subplot(length(metrics), 1, counter)
      plt.plot(metrics[k][1:end]/sum(c.N))
      if counter == length(metrics)
        plt.xlabel("iteration")
      end
      plt.ylabel(string(k))
    end
    plt.tight_layout()
    plt.savefig("$(IMG_DIR)/metrics.pdf")
  end
  plt.close()

  NSAMPS = 200
  samples = [Cytof5.VB.rsample(state)[2] for n in 1:NSAMPS]
  trace = [Cytof5.VB.rsample(s)[2] for s in state_hist]

  println("y samples ...")
  # y_samps
  if has_simdat
    m = [isnan.(yi) for yi in simdat[:y]]
  else
    m = [isnan.(yi) for yi in y]
  end
  if has_simdat
    y_samps = [Tracker.data.(Cytof5.VB.rsample(state, simdat[:y], c)[3]) for n in 1:10]
  else
    y_samps = [Tracker.data.(Cytof5.VB.rsample(state, y, c)[3]) for n in 1:10]
  end
  for i in 1:c.I
    plt.hist(vec(y_samps[end][i][m[i]]))
    plt.xlim([-8, 1])
    plt.savefig("$(IMG_DIR)/y$(i)_imputed_hist.pdf")
    plt.close()
  end

  println("prob miss...")
  # Prob miss
  ygrid = collect(range(-8, stop=0, length=500))
  pm = hcat([Cytof5.VB.prob_miss(ygrid, c.beta[i]...) for i in 1:c.I]...)
  plt.figure()
  for i in 1:c.I
    plt.plot(ygrid, pm[:, i], label="sample $i")
  end
  plt.legend()
  plt.xlabel("y")
  plt.ylabel("prob. of missing")
  plt.savefig("$(IMG_DIR)/prob_miss.pdf")
  plt.close()

  # Z
  println("Z ...")
  if c.use_stickbreak
    Z = [Int.(cumprod(reshape(s.v, 1, c.K)) .> s.H).data for s in samples]
  else
    Z = [Int.(reshape(s.v, 1, c.K) .> s.H).data for s in samples]
  end
  Z_mean = mean(Z)
  plot_Z(Z_mean)
  plt.savefig("$(IMG_DIR)/Z.pdf")
  plt.close()

  # True Z
  if has_simdat
    plot_Z(simdat[:Z])
    plt.xlabel("features")
    plt.ylabel("markers")
    plt.savefig("$(IMG_DIR)/Z_true.pdf")
    plt.close()

    plot_Z(simdat[:Z]')
    plt.ylabel("features")
    plt.xlabel("markers")
    plt.savefig("$(IMG_DIR)/ZT_true.pdf")
    plt.close()
  end

  # W
  println("W ...")
  W = [s.W.data for s in samples]
  plt.figure()
  for i in 1:c.I
    plt.subplot(c.I, 1, i)
    Wi = hcat([w[i, :] for w in W]...)
    boxplot(Wi')
    plt.ylabel("W$i", rotation=0, labelpad=15)
    plt.xticks(rotation=90)
    if has_simdat
      for wik in simdat[:W][i, :]
        plt.axhline(wik, ls="--", lw=.5)
      end
    end
  end
  plt.tight_layout()
  # plt.show()
  plt.savefig("$(IMG_DIR)/W.pdf")
  plt.close()

  # mu
  println("mu ...")
  mu = hcat([[-cumsum(s.delta0.data); cumsum(s.delta1.data)] for s in samples]...)
  boxplot(mu');
  plt.axhline(0, alpha=.5)
  plt.axvline(c.L[0] + .5, alpha=.5)
  if has_simdat
    for z in (0, 1)
      axhlines(simdat[:mus][z], color="grey", ls="--", lw=.5)
    end
  end
  plt.savefig("$(IMG_DIR)/mu.pdf")
  plt.close()

  # sig2
  println("sig2 ...")
  sig2 = hcat([s.sig2.data for s in samples]...)
  boxplot(sig2');
  if has_simdat
    axhlines(simdat[:sig2], ls="--", color="grey", lw=.5);
  end
  plt.savefig("$(IMG_DIR)/sig2.pdf")
  plt.close()

  # eps
  println("eps ...")
  eps = hcat([s.eps.data for s in samples]...)
  boxplot(eps');
  plt.savefig("$(IMG_DIR)/eps.pdf")
  plt.close()

  # v
  println("v ...")
  v = hcat([s.v.data for s in samples]...)
  boxplot(v');
  plt.savefig("$(IMG_DIR)/v.pdf")
  plt.close()

  # v cumprod
  v_cumprod = hcat([cumprod(s.v.data) for s in samples]...)
  boxplot(v_cumprod');
  plt.savefig("$(IMG_DIR)/v_cumprod.pdf")
  plt.close()

  # alpha
  println("alpha ...")
  alpha = vcat([s.alpha.data for s in samples]...);
  plt.hist(alpha, density=true)
  plt.xlabel("alpha")
  plt.ylabel("density")
  plt.axvline(mean(alpha), color="black", linestyle="--")
  plt.axvline(quantile(alpha, .025), color="black", linestyle="--")
  plt.axvline(quantile(alpha, .975), color="black", linestyle="--")
  plt.savefig("$(IMG_DIR)/alpha.pdf")
  plt.close()

  ### trace plots ###
  println("traces ... ")
  mkpath("$(IMG_DIR)/trace/")

  # TODO:
  # Z trace
  # if c.use_stickbreak
  #   Z_trace = [Int.(reshape(cumprod(t.v), 1, c.K) .> t.H).data for t in trace]
  # else
  #   Z_trace = [Int.(reshape(t.v, 1, c.K) .> t.H).data for t in trace]
  # end
  # pdf_pages = PdfPages("$(IMG_DIR)/trace/Z.pdf")
  # for z in Z_trace
  #   fig = plt.figure()
  #   fig = plot_Z(z, fig)
  #   fig.xlabel("features")
  #   fig.ylabel("markers")
  #   pdf_pages.savefig(fig)
  # end
  # pdf_pages.close()

  # mu_trace
  mu_trace = hcat([[-cumsum(s.delta0.data); cumsum(s.delta1.data)] for s in trace]...)
  plt.plot(mu_trace')
  plt.xlabel("iteration")
  plt.ylabel("mu")
  plt.savefig("$(IMG_DIR)/trace/mu.pdf")
  plt.close()

  # sig2_trace
  sig2_trace = hcat([s.sig2.data for s in trace]...)
  plt.plot(sig2_trace')
  plt.xlabel("iter")
  plt.ylabel("sig2")
  plt.savefig("$(IMG_DIR)/trace/sig2.pdf")
  plt.close()

  # eps_trace
  eps_trace = hcat([s.eps.data for s in trace]...)
  plt.plot(eps_trace')
  plt.xlabel("iter")
  plt.ylabel("eps")
  plt.savefig("$(IMG_DIR)/trace/eps.pdf")
  plt.close()

  # W trace
  W_trace = cat([s.W.data for s in trace]..., dims=3)
  for i in 1:c.I
    plt.plot(W_trace[i, :, :]')
    plt.xlabel("iter")
    plt.ylabel("W$i")
    plt.savefig("$(IMG_DIR)/trace/W$(i).pdf")
    plt.close()
  end

  # v trace
  v_trace = hcat([s.v.data for s in trace]...)
  plt.plot(v_trace')
  plt.xlabel("iter")
  plt.ylabel("v")
  plt.savefig("$(IMG_DIR)/trace/v.pdf")
  plt.close()

  # alpha trace
  alpha_trace = vcat([s.alpha.data for s in trace]...)
  plt.plot(alpha_trace)
  plt.xlabel("iter")
  plt.ylabel("alpha")
  plt.savefig("$(IMG_DIR)/trace/alpha.pdf")
  plt.close()

  ### yZ ###
  println("yz ... ")
  # nlam = 2
  nlam = 30
  @time if has_simdat
    lam = [sample_lam(state, simdat[:y], c) for b in 1:nlam]
  else
    lam = [sample_lam(state, y, c) for b in 1:nlam]
  end
  lam_mode = lam_f(lam, Distributions.mode)
  # println(lam_mode[1][1:10])
  W_mean= dropdims(mean(cat(W..., dims=3), dims=3), dims=3)

  mkpath("$(IMG_DIR)/yz")
  for i in 1:c.I
    # lambda
    # lami_est, k_ord = relabel_lam(lam_mode[i], W_mean[i, :])
    lami_est, k_ord = relabel_lam2(lam_mode[i], W_mean[i, :])
    writedlm("$(IMG_DIR)/yz/clustering_$(i).txt", lami_est)

    if has_simdat
      yi = simdat[:y][i]
    else
      yi = y[i]
    end

    # Zi
    plt.figure(figsize=(6,6))
    plot_yz.plot_Z(Z_mean, W_mean[i, :], lam_mode[i], w_thresh=w_thresh,
                   fs_markers=18, fs_celltypes=18, fs_lab=18)
    plt.savefig("$(IMG_DIR)/yz/Z$(i)_post_minpresence$(w_thresh).pdf", bbox_inches="tight")
    plt.close()

    plt.figure(figsize=(6,6))
    plot_yz.plot_Z(Z_mean .> .5, W_mean[i, :], lam_mode[i], w_thresh=w_thresh,
                   fs_markers=18, fs_celltypes=18, fs_lab=18)
    plt.savefig("$(IMG_DIR)/yz/Z$(i)_est_minpresence$(w_thresh).pdf", bbox_inches="tight")
    plt.close()

    # yz_i
    plt.figure(figsize=(8,8))
    plot_yz.plot_yz(yi, Z_mean, W_mean[i, :],
                    lam_mode[i], w_thresh=w_thresh,
                    cm_y=blue2red.cm(9), vlim_y=VLIM)
    plt.savefig("$(IMG_DIR)/yz/yz$(i)_post_highres.pdf", dpi=500)
    plt.savefig("$(IMG_DIR)/yz/yz$(i)_post.pdf")
    plt.close()

    # yi
    plt.figure(figsize=(8,8))
    plot_yz.plot_y(yi, W_mean[i, :], lam_mode[i], fs_lab=18,
                   cm=blue2red.cm(9), vlim=VLIM, fs_xlab=18, fs_ylab=18)
    plt.savefig("$(IMG_DIR)/yz/y$(i)_post_highres.pdf", dpi=500, bbox_inches="tight")
    plt.savefig("$(IMG_DIR)/yz/y$(i)_post.pdf", bbox_inches="tight")
    plt.close()
  end
end


text:
# Function definitions for interop w/ PropCheck.jl
# seperated into their own file to make reproducing tests easier

genArbUInt(i=igen(0x5)) = Integrated(Generator{ArbUInt}(rng -> ArbUInt(root(generate(rng, PropCheck.vector(i, igen(ArbDigit)))))))
PropCheck.generate(rng, ::Type{ArbUInt}) = root(generate(rng, genArbUInt()))
function PropCheck.shrink(t::ArbUInt)
    shrunks = shrink(reverse(t.data))
    isempty(shrunks) && return unique!(shrunks)
    finalize = ArbUInt ∘ reverse!
    lower_zeroed = copy(t.data)
    lower_zeroed[begin:div(end,2)] .= zero(ArbDigit)
    customs = Iterators.map(finalize, [
        t.data[begin:div(end,2)],
        lower_zeroed
    ])
    PropCheck.iunique(customs, Iterators.map(finalize, shrunks))
end


text:
using HPAT

function makePairs(A::Vector{Int64})
    out = []
    for i in 1:length(A)-1
        for j in i+1:length(A)
            push!(out, (A[i],A[j]))
        end
    end
    return out
end

@acc hpat function q01(my_categories, my_stores, num_top_items, count_threshod, file_name)

    store_sales = DataSource(DataTable{:ss_item_sk=Int64,:ss_ticket_number=Int64,:ss_store_sk=Int64}, HDF5, file_name)
    item = DataSource(DataTable{:item_sk=Int64,:i_category_id=Int64}, HDF5, file_name)

    sale_items = join(store_sales, item, :ss_item_sk==:i_item_sk, :item_sk)

    sale_items = sale_items[:i_category_id in my_categories && :ss_store_sk in my_stores]

    # get array of items sold in a transaction
    sold_in_ticket = aggregate(sale_items, :ss_ticket_number, :item_arr = collect(:item_sk))

    # remove duplicates in each sale
    map!(union, sold_in_ticket[:item_arr])

    # sort items of sale
    map!(sort!, sold_in_ticket[:item_arr])

    # make pair of items sold
    map!(makePairs, sold_in_ticket[:item_arr])

    all_pairs = flatten(sold_in_ticket[:item_arr])
    pair_counts = collect(countmap(all_pairs))

    high_counts = filter(a->a[2]>count_threshod, pair_counts)
    sort!(high_counts, by=a->a[2])
    return high_counts[1:num_top_items]
end

println(q01([1,2,3], [20,30], 100, 50, "data.hdf5"))


text:
"""
    nonlinear stage-wise objective
"""
struct NonlinearStageObjective <: Objective
    l_stage
    l_terminal
end

function nonlinear_stage_objective(l_stage, l_terminal)
    return NonlinearStageObjective(l_stage, l_terminal)
end

function objective(Z, obj::NonlinearStageObjective, idx, T)
    J = 0.0

    for t = 1:T-1
        x = view(Z, idx.x[t])
        u = view(Z, idx.u[t])

        J += obj.l_stage(x, u, t)
    end

    x = view(Z, idx.x[T])
    J += obj.l_terminal(x)

    return J
end

function objective_gradient!(∇J, Z, obj::NonlinearStageObjective, idx, T)

    for t = 1:T-1
        x = view(Z, idx.x[t])
        u = view(Z, idx.u[t])

        lx(y) = obj.l_stage(y, u, t)
        lu(y) = obj.l_stage(x, y, t)

        ∇J[idx.x[t]] += ForwardDiff.gradient(lx, x)
        ∇J[idx.u[t]] += ForwardDiff.gradient(lu, u)

    end

    x = view(Z, idx.x[T])
    ∇J[idx.x[T]] += ForwardDiff.gradient(obj.l_terminal, x)

    return nothing
end


text:
# Autogenerated wrapper script for libspatialite_jll for x86_64-apple-darwin
export libspatialite, mod_spatialite

using SQLite_jll
using GEOS_jll
using PROJ_jll
using Libiconv_jll
using Zlib_jll
using XML2_jll
JLLWrappers.@generate_wrapper_header("libspatialite")
JLLWrappers.@declare_library_product(libspatialite, "@rpath/libspatialite.7.dylib")
JLLWrappers.@declare_library_product(mod_spatialite, "mod_spatialite.7.dylib")
function __init__()
    JLLWrappers.@generate_init_header(SQLite_jll, GEOS_jll, PROJ_jll, Libiconv_jll, Zlib_jll, XML2_jll)
    JLLWrappers.@init_library_product(
        libspatialite,
        "lib/libspatialite.7.dylib",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        mod_spatialite,
        "lib/mod_spatialite.7.dylib",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
using Documenter
using PotentialFitting, PotentialCalculation

makedocs(sitename="PotentialFitting.jl",
         pages=["Home" => "index.md",
                "Install" => "install.md",
                "Manual" => Any[
                    "Fitting Potential" => "use.md",
                    "Example Use Case" => "exampleusecase.md",

                ],
                "Reference" => "reference.md"
         ]

)

deploydocs(
    repo = "github.com/MatrixLabTools/PotentialFitting.jl.git",
)


text:
"""
    HorizontalStructure

Horizontal memory structure. Decomposes stochastic program by scenario.

"""
struct HorizontalStructure{N, M, SP <: NTuple{M, AbstractScenarioProblems}} <: AbstractBlockStructure{N}
    decisions::NTuple{N, Decisions}
    scenarioproblems::SP
    constraint_map::Dict{Tuple{CI, Int}, CI}

    function HorizontalStructure(decisions::NTuple{N, Decisions}, scenarioproblems::NTuple{M,AbstractScenarioProblems}) where {N, M}
        M == N - 1 || error("Inconsistent number of stages $N and number of scenario types $M")
        SP = typeof(scenarioproblems)
        return new{N,M,SP}(decisions, scenarioproblems, Dict{Tuple{CI, Int}, CI}())
    end
end

function StochasticStructure(decisions::NTuple{N, Decisions}, scenario_types::ScenarioTypes{M}, instantiation::Union{Horizontal, DistributedHorizontal}) where {N, M}
    scenarioproblems = ntuple(Val(M)) do i
        ScenarioProblems(scenario_types[i], instantiation)
    end
    return HorizontalStructure(decisions, scenarioproblems)
end

function StochasticStructure(decisions::NTuple{N, Decisions}, scenarios::NTuple{M, Vector{<:AbstractScenario}}, instantiation::Union{Horizontal, DistributedHorizontal}) where {N, M}
    scenarioproblems = ntuple(Val(M)) do i
        ScenarioProblems(scenarios[i], instantiation)
    end
    return HorizontalStructure(decisions, scenarioproblems)
end

# Base overloads #
# ========================== #
function Base.print(io::IO, structure::HorizontalStructure{2})
    print(io, "Horizontal scenario problems \n")
    print(io, "============== \n")
    for (id, subproblem) in enumerate(subproblems(structure, 2))
        @printf(io, "Subproblem %d (p = %.2f):\n", id, probability(scenario(structure, 2, id)))
        print(io, subproblem)
        print(io, "\n")
    end
end

# MOI #
# ========================== #
function MOI.get(structure::HorizontalStructure, attr::MOI.AbstractModelAttribute)
    if attr isa Union{MOI.ObjectiveFunctionType, MOI.ObjectiveSense}
        # Should be the same in all subproblems, query the first
        return MOI.get(structure, ScenarioDependentModelAttribute(2, 1, attr))
    end
    error("The horizontal structure is completely decomposed into subproblems. All model attributes are scenario dependent.")
end
function MOI.get(structure::HorizontalStructure, attr::MOI.AbstractVariableAttribute, index::MOI.VariableIndex)
    error("The horizontal structure is completely decomposed into subproblems. All model attributes are scenario dependent.")
end
function MOI.get(structure::HorizontalStructure, attr::MOI.AbstractConstraintAttribute, cindex::MOI.ConstraintIndex)
    error("The horizontal structure is completely decomposed into subproblems. All model attributes are scenario dependent.")
end
function MOI.get(structure::HorizontalStructure, attr::ScenarioDependentModelAttribute)
    n = num_scenarios(structure, attr.stage)
    1 <= attr.scenario_index <= n || error("Scenario index $attr.scenario_index not in range 1 to $n.")
    return MOI.get(scenarioproblems(structure, attr.stage), attr)
end
function MOI.get(structure::HorizontalStructure, attr::ScenarioDependentVariableAttribute, index::MOI.VariableIndex)
    n = num_scenarios(structure, attr.stage)
    1 <= attr.scenario_index <= n || error("Scenario index $attr.scenario_index not in range 1 to $n.")
    return MOI.get(scenarioproblems(structure, attr.stage), attr, index)
end
function MOI.get(structure::HorizontalStructure, attr::ScenarioDependentConstraintAttribute, ci::MOI.ConstraintIndex)
    n = num_scenarios(structure, attr.stage)
    1 <= attr.scenario_index <= n || error("Scenario index $attr.scenario_index not in range 1 to $n.")
    mapped_ci = structure.constraint_map[(ci, attr.scenario_index)]
    return MOI.get(scenarioproblems(structure, attr.stage), attr, mapped_ci)
end
function MOI.get(structure::HorizontalStructure, attr::ScenarioDependentConstraintAttribute, ci::CI{F,S}) where {F <: MOI.SingleVariable, S}
    n = num_scenarios(structure, attr.stage)
    1 <= attr.scenario_index <= n || error("Scenario index $attr.scenario_index not in range 1 to $n.")
    # Do not need to map here
    return MOI.get(scenarioproblems(structure, attr.stage), attr, ci)
end

function MOI.set(structure::HorizontalStructure{2}, attr::Union{MOI.AbstractModelAttribute, MOI.Silent}, value)
    # All subproblems should be updated
    MOI.set(scenarioproblems(structure), attr, value)
end
function MOI.set(structure::HorizontalStructure{2}, attr::MOI.AbstractVariableAttribute,
                 index::MOI.VariableIndex, value)
    # All subproblems should be updated
    MOI.set(scenarioproblems(structure), attr, index, value)
    return nothing
end
function MOI.set(structure::HorizontalStructure{2}, attr::MOI.AbstractConstraintAttribute,
                 ci::MOI.ConstraintIndex, value)
    # All subproblems should be updated
    MOI.set(scenarioproblems(structure), attr, ci, value)
    return nothing
end
function MOI.set(structure::HorizontalStructure{2}, attr::ScenarioDependentModelAttribute, value)
    n = num_scenarios(structure, attr.stage)
    1 <= attr.scenario_index <= n || error("Scenario index $attr.scenario_index not in range 1 to $n.")
    MOI.set(scenarioproblems(subproblem, attr.stage), attr, value)
    return nothing
end
function MOI.set(structure::HorizontalStructure{2}, attr::ScenarioDependentVariableAttribute,
                 index::MOI.VariableIndex, value)
    n = num_scenarios(structure, attr.stage)
    1 <= attr.scenario_index <= n || error("Scenario index $attr.scenario_index not in range 1 to $n.")
    MOI.set(scenarioproblems(structure, attr.stage), attr, index, value)
    return nothing
end
function MOI.set(structure::HorizontalStructure{2}, attr::ScenarioDependentConstraintAttribute,
                 ci::MOI.ConstraintIndex, value)
    n = num_scenarios(structure, attr.stage)
    1 <= attr.scenario_index <= n || error("Scenario index $attr.scenario_index not in range 1 to $n.")
    MOI.set(scenarioproblems(structure, attr.stage), attr, ci, value)
    return nothing
end

function MOI.is_valid(structure::HorizontalStructure{2}, index::MOI.VariableIndex, stage::Integer)
    stage == 1 || error("No scenario index specified.")
    # First-stage decision should be valid in all subproblems
    return all(MOI.is_valid(scenarioproblems(structure), index, scenario_index) for scenario_index in num_scenarios(structure))
end
function MOI.is_valid(structure::HorizontalStructure{2}, ci::MOI.ConstraintIndex, stage::Integer)
    stage == 1 || error("No scenario index specified.")
    # First-stage decision constraint should be valid in all subproblems
    return all(MOI.is_valid(scenarioproblems(structure), ci, scenario_index) for scenario_index in num_scenarios(structure))
end
function MOI.is_valid(structure::HorizontalStructure{N}, ci::MOI.ConstraintIndex, stage::Integer, scenario_index::Integer) where N
    1 <= stage <= N || error("Stage $stage not in range 1 to $N.")
    stage > 1 || error("There are no scenarios in the first stage.")
    n = num_scenarios(structure, stage)
    1 <= scenario_index <= n || error("Scenario index $scenario_index not in range 1 to $n.")
    if !haskey(structure.constraint_map, (ci, scenario_index))
        return false
    end
    mapped_ci = structure.constraint_map[(ci, scenario_index)]
    return MOI.is_valid(scenarioproblems(structure, stage), mapped_ci, scenario_index)
end

function MOI.add_constraint(structure::HorizontalStructure{2}, f::SingleDecision, s::MOI.AbstractSet)
    # Constraints should be added to every subproblem
    for scenario_index in 1:num_scenarios(structure)
        MOI.add_constraint(scenarioproblems(structure), f, s, scenario_index)
    end
    return nothing
end
function MOI.add_constraint(structure::HorizontalStructure{2}, f::SingleDecision, s::MOI.AbstractSet, stage::Integer, scenario_index::Integer)
    stage == 1 && error("There are no scenarios in the first stage.")
    n = num_scenarios(structure, 2)
    1 <= scenario_index <= n || error("Scenario index $scenario_index not in range 1 to $n.")
    new_ci = MOI.add_constraint(scenarioproblems(structure), f, s, scenario_index)
    # Add to map
    ci = CI{SingleDecision,typeof(s)}(f.decision.value)
    structure.constraint_map[(ci, scenario_index)] = new_ci
    return nothing
end

function MOI.delete(structure::HorizontalStructure{2}, index::MOI.VariableIndex, stage::Integer)
    stage == 1 || error("No scenario index specified.")
    # First-stage decision should be removed from every subproblem
    for scenario_index in 1:num_scenarios(structure)
        MOI.delete(scenarioproblems(structure), index, scenario_index)
    end
    return nothing
end
function MOI.delete(structure::HorizontalStructure{2}, indices::Vector{MOI.VariableIndex}, stage::Integer)
    stage == 1 || error("No scenario index specified.")
    # First-stage decision should be removed from every subproblem
    for scenario_index in 1:num_scenarios(structure)
        MOI.delete(scenarioproblems(structure), indices, scenario_index)
    end
    return nothing
end
function MOI.delete(structure::HorizontalStructure{2}, ci::MOI.ConstraintIndex, stage::Integer)
    stage == 1 || error("No scenario index specified.")
    # First-stage decision constraints should be removed from every subproblem
    for scenario_index in 1:num_scenarios(structure)
        MOI.delete(scenarioproblems(structure), ci, scenario_index)
    end
    return nothing
end
function MOI.delete(structure::HorizontalStructure{2}, cis::Vector{<:MOI.ConstraintIndex}, stage::Integer)
    stage == 1 || error("No scenario index specified.")
    # First-stage decision constraints should be removed from every subproblem
    for scenario_index in 1:num_scenarios(structure)
        MOI.delete(scenarioproblems(structure), cis, scenario_index)
    end
    return nothing
end
function MOI.delete(structure::HorizontalStructure{N}, ci::MOI.ConstraintIndex, stage::Integer, scenario_index::Integer) where N
    1 <= stage <= N || error("Stage $stage not in range 1 to $N.")
    stage > 1 || error("There are no scenarios in the first stage.")
    n = num_scenarios(structure, stage)
    1 <= scenario_index <= n || error("Scenario index $scenario_index not in range 1 to $n.")
    mapped_ci = structure.constraint_map[(ci, scenario_index)]
    MOI.delete(scenarioproblems(structure, stage), mapped_ci, scenario_index)
    return nothing
end
function MOI.delete(structure::HorizontalStructure{N}, cis::Vector{<:MOI.ConstraintIndex}, stage::Integer, scenario_index::Integer) where N
    1 <= stage <= N || error("Stage $stage not in range 1 to $N.")
    stage > 1 || error("There are no scenarios in the first stage.")
    n = num_scenarios(structure, stage)
    1 <= scenario_index <= n || error("Scenario index $scenario_index not in range 1 to $n.")
    mapped_cis = map(ci -> structure.constraint_map[(ci, scenario_index)], cis)
    MOI.delete(scenarioproblems(structure, stage), mapped_cis, scenario_index)
    return nothing
end

# JuMP #
# ========================== #
function JuMP.fix(structure::HorizontalStructure{2}, index::MOI.VariableIndex, stage::Integer, val::Number)
    d = decision(structure, index, stage)
    if state(d) == NotTaken
        # Update state
        d.state = Taken
        # Update value
        d.value = val
    else
        # Just update value
        d.value = val
    end
    update_decision_state!(scenarioproblems(structure), index, Taken)
    return nothing
end
function JuMP.fix(structure::HorizontalStructure{N}, index::MOI.VariableIndex, stage::Integer, scenario_index::Integer, val::Number) where N
    1 <= stage <= N || error("Stage $stage not in range 1 to $N.")
    n = num_scenarios(structure, stage)
    1 <= scenario_index <= n || error("Scenario index $scenario_index not in range 1 to $n.")
    fix(scenarioproblems(structure, stage), index, scenario_index, val)
    return nothing
end
function JuMP.unfix(structure::HorizontalStructure{2}, index::MOI.VariableIndex, stage::Integer)
    d = decision(structure, index, stage)
    if state(d) == NotTaken
        # Nothing to do, just return
        return nothing
    end
    # Update state
    d.state = NotTaken
    update_decision_state!(scenarioproblems(structure), index, NotTaken)
    return nothing
end
function JuMP.unfix(structure::HorizontalStructure{N}, index::MOI.VariableIndex, stage::Integer, scenario_index::Integer) where N
    1 <= stage <= N || error("Stage $stage not in range 1 to $N.")
    n = num_scenarios(structure, stage)
    1 <= scenario_index <= n || error("Scenario index $scenario_index not in range 1 to $n.")
    unfix(scenarioproblems(structure, stage), index, scenario_index)
    return nothing
end

function JuMP.objective_function_type(structure::HorizontalStructure)
    error("The horizontal structure is completely decomposed into subproblems. All model attributes are scenario dependent.")
end

function JuMP.objective_function(structure::HorizontalStructure, FunType::Type{<:AbstractJuMPScalar})
    error("The horizontal structure is completely decomposed into subproblems. All model attributes are scenario dependent.")
end

function JuMP.objective_function(structure::HorizontalStructure, proxy::JuMP.Model, stage::Integer, FunType::Type{<:AbstractJuMPScalar})
    return objective_function(proxy, FunType)
end

function JuMP._moi_optimizer_index(structure::HorizontalStructure, ci::CI{F,S}, scenario_index::Integer) where {F,S}
    if haskey(structure.constraint_map, (ci, scenario_index))
        ci = CI{F,S}(structure.constraint_map[(ci, scenario_index)].value)
        return JuMP._moi_optimizer_index(scenarioproblems(structure), ci, scenario_index)
    end
    return JuMP._moi_optimizer_index(scenarioproblems(structure), ci, scenario_index)
end

function JuMP.set_objective_coefficient(structure::HorizontalStructure{2}, index::VI, var_stage::Integer, stage::Integer, coeff::Real)
    if var_stage == 1
        if stage == 1
            # Modification should be applied in every subproblem
            for scenario_index in 1:num_scenarios(structure)
                set_objective_coefficient(scenarioproblems(structure), index, scenario_index, coeff)
            end
        else
            error("The horizontal structure is completely decomposed into subproblems. Can only modify first-stage part of objective for first-stage.")
        end
    else
        error("Decision is scenario dependent, consider `set_objective_coefficient(sp, dvar, stage, scenario_index, coeff)`.")
    end
    return nothing
end
function JuMP.set_objective_coefficient(structure::HorizontalStructure{N}, index::VI, var_stage::Integer, stage::Integer, scenario_index::Integer, coeff::Real) where N
    1 <= stage <= N || error("Stage $stage not in range 1 to $N.")
    stage > 1 || error("There are no scenarios in the first stage.")
    n = num_scenarios(structure, stage)
    1 <= scenario_index <= n || error("Scenario index $scenario_index not in range 1 to $n.")
    set_objective_coefficient(scenarioproblems(structure, stage), index, scenario_index, coeff)
    return nothing
end

function JuMP.set_normalized_coefficient(structure::HorizontalStructure,
                                         ci::CI{F,S},
                                         index::VI,
                                         value) where {T, F <: Union{AffineDecisionFunction{T}, QuadraticDecisionFunction{T}}, S}
    # Modification should be applied in every subproblem
    for scenario_index in 1:num_scenarios(structure)
        set_normalized_coefficient(scenarioproblems(structure), ci, index, scenario_index, value)
    end
    return nothing
end
function JuMP.set_normalized_coefficient(structure::HorizontalStructure{N},
                                         ci::CI{F,S},
                                         index::VI,
                                         stage::Integer,
                                         scenario_index::Integer,
                                         value) where {N, T, F <: Union{AffineDecisionFunction{T}, QuadraticDecisionFunction{T}}, S}
    1 <= stage <= N || error("Stage $stage not in range 1 to $N.")
    stage > 1 || error("There are no scenarios in the first stage.")
    n = num_scenarios(structure, stage)
    1 <= scenario_index <= n || error("Scenario index $scenario_index not in range 1 to $n.")
    mapped_ci = structure.constraint_map[(ci, scenario_index)]
    set_normalized_coefficient(scenarioproblems(structure, stage), mapped_ci, index, scenario_index, value)
    return nothing
end

function JuMP.normalized_coefficient(structure::HorizontalStructure{N},
                                     ci::CI{F,S},
                                     index::VI,
                                     stage::Integer,
                                     scenario_index::Integer) where {N, T, F <: Union{AffineDecisionFunction{T}, QuadraticDecisionFunction{T}}, S}
    1 <= stage <= N || error("Stage $stage not in range 1 to $N.")
    stage > 1 || error("There are no scenarios in the first stage.")
    n = num_scenarios(structure, stage)
    1 <= scenario_index <= n || error("Scenario index $scenario_index not in range 1 to $n.")
    mapped_ci = structure.constraint_map[(ci, scenario_index)]
    return normalized_coefficient(scenarioproblems(structure, stage), mapped_ci, index, scenario_index)
end

function JuMP.set_normalized_rhs(structure::HorizontalStructure,
                                 ci::CI{F,S},
                                 value) where {T,
                                               F <: Union{AffineDecisionFunction{T}, QuadraticDecisionFunction{T}},
                                               S <: MOIU.ScalarLinearSet{T}}
    # Modification should be applied in every subproblem
    for scenario_index in 1:num_scenarios(structure)
        set_normalized_rhs(scenarioproblems(structure), ci, scenario_index, value)
    end
    return nothing
end
function JuMP.set_normalized_rhs(structure::HorizontalStructure{N},
                                 ci::CI{F,S},
                                 stage::Integer,
                                 scenario_index::Integer,
                                 value) where {N,
                                               T,
                                               F <: Union{AffineDecisionFunction{T}, QuadraticDecisionFunction{T}},
                                               S <: MOIU.ScalarLinearSet{T}}
    1 <= stage <= N || error("Stage $stage not in range 1 to $N.")
    stage > 1 || error("There are no scenarios in the first stage.")
    n = num_scenarios(structure, stage)
    1 <= scenario_index <= n || error("Scenario index $scenario_index not in range 1 to $n.")
    mapped_ci = structure.constraint_map[(ci, scenario_index)]
    set_normalized_rhs(scenarioproblems(structure, stage), mapped_ci, scenario_index, value)
    return nothing
end

function DecisionRef(proxy::JuMP.Model, structure::HorizontalStructure, index::VI)
    return DecisionRef(proxy, index)
end

function JuMP.jump_function(structure::HorizontalStructure{N},
                            proxy::JuMP.Model,
                            stage::Integer,
                            f::MOI.AbstractFunction) where N
    1 <= stage <= N || error("Stage $stage not in range 1 to $N.")
    return JuMP.jump_function(proxy, f)
end

# Getters #
# ========================== #
function structure_name(structure::HorizontalStructure)
    return "Horizontal"
end

function decision(structure::HorizontalStructure{N}, index::MOI.VariableIndex, stage::Integer) where N
    stage == 1 || error("No scenario index specified.")
    return decision(structure.decisions[stage], index)
end

# Setters #
# ========================== #
function update_known_decisions!(structure::HorizontalStructure{2})
    # Modification should be applied in every subproblem
    for scenario_index in 1:num_scenarios(structure)
        update_known_decisions!(scenarioproblems(structure), scenario_index)
    end
    return nothing
end

function untake_decisions!(structure::HorizontalStructure{2,1,NTuple{1,SP}}) where SP <: ScenarioProblems
    if untake_decisions!(structure.decisions[1])
        update_decisions!(scenarioproblems(structure), DecisionsStateChange())
    end
    return nothing
end
function untake_decisions!(structure::HorizontalStructure{2,1,NTuple{1,SP}}) where SP <: DistributedScenarioProblems
    sp = scenarioproblems(structure)
    @sync begin
        for (i,w) in enumerate(workers())
            @async remotecall_fetch(
                w, sp[w-1], sp.decisions[w-1]) do sp, d
                    if untake_decisions!(fetch(d))
                        update_decisions!(fetch(sp), DecisionsStateChange())
                    end
                end
        end
    end
    return nothing
end


text:
"""
    x = xvec(λ,N)

Create `x` values with periodicity for box specified by length `λ`, using `N` points.
"""
xvec(L,N) = LinRange(-L/2,L/2,N+1)[2:end] |> collect

"""
    k = kvec(L,N)

Create `k` values with correct periodicity for box specified by length `λ` for number of points `N`.
"""
kvec(L,N) = fftfreq(N)*N*2*π/L

"""
    X = xvecs(L,N)

Create a tuple containing the spatial coordinate array for each spatial dimension.
"""
function xvecs(L,N)
    X = []
    for (λ,ν) in zip(L,N)
        x = xvec(λ,ν)
        push!(X,x)
    end
    return X |> Tuple
end

"""
    K = kvecs(L,N)

Create a tuple containing the spatial coordinate array for each spatial dimension.
"""
function kvecs(L,N)
    K = []
    for (λ,ν) in zip(L,N)
        k = kvec(λ,ν)
        push!(K,k)
    end
    return K |> Tuple
end

"""
    k² = k2(K)

Create the kinetic energy array `k²` on the `k`-grid defined by the tuple `K`.
"""
function k2(K)
    kind = Iterators.product(K...)
    return map(k-> sum(abs2.(k)),kind)
end

"""
    X,K,dX,dK = xk_arrays(L,N)

Create all `x` and `k` arrays for box specified by tuples `L=(Lx,...)` and `N=(Nx,...)`.
For convenience, differentials `dX`, `dK` are also reaturned. `L` and `N` must be tuples of equal length.
"""
function xk_arrays(L,N)
    @assert length(L) == length(N)
    X = xvecs(L,N)
    K = kvecs(L,N)
    dX = Float64[]; dK = Float64[]
    for j ∈ eachindex(X)
        x = X[j]; k = K[j]
        dx = x[2]-x[1]; dk = k[2]-k[1]
        push!(dX,dx)
        push!(dK,dk)
    end
    dX = dX |> Tuple
    dK = dK |> Tuple
    return X,K,dX,dK
end

"""
    Dx,Dk = dfft(x,k)

Measures that make `fft`, `ifft` 2-norm preserving.
Correct measures for mapping between `x`- and `k`-space.
"""
function dfft(x,k)
    dx = x[2]-x[1]; dk = k[2]-k[1]
    Dx = dx/sqrt(2*pi)
    Dk = length(k)*dk/sqrt(2*pi)
    return Dx, Dk
end

"""
    DX,DK = fft_differentials(X,K)

Evalutes tuple of measures that make `fft`, `ifft` 2-norm preserving for each
`x` or `k` dimension.
"""
function fft_differentials(X,K)
    M = length(X)
    DX = zeros(M); DK = zeros(M)
    for i ∈ eachindex(X)
        DX[i],DK[i] = dfft(X[i],K[i])
    end
    return DX,DK
end


text:
# include("../src/LarSurf.jl")

using LarSurf
import SparseArrays.spzeros
import SparseArrays.dropzeros!
using Plasm, SparseArrays
using LinearAlgebraicRepresentation
Lar = LinearAlgebraicRepresentation

using Pio3d

threshold = 4000
pth = Pio3d.datasets_join_path("medical/orig/sample-data/nrn4.pklz")
datap = Pio3d.read3d(pth)

data3d = datap["data3d"]
segmentation = data3d .> threshold
# data_size = LarSurf.size_as_array(size(data3d))
#
# segmentation = zeros(int8, 5, 6, 7)
# segmentation[2:5,2:5,2:6] .= 1
# plasm.view(plasm.numbering(.6)((v,[vv, ev, filteredfv])))

filteredfv, flin, v, model = LarSurf.get_surface_grid(segmentation)
(vv, ev, fv, cv) = model
plasm.view((v,[vv, ev, filteredfv]))


text:
@testset "html" begin
    html = "foo"
    block = PlutoStaticHTML.code_block(html)
    @test contains(block, "<b>foo</b>")

    notebook = Notebook([
        Cell("x = 1 + 1"),
        Cell("using Images: load"),
        Cell("PKGDIR = \"$PKGDIR\""),
        Cell("""im_file(ext) = joinpath(PKGDIR, "test", "im", "im.\$ext")"""),
        Cell("""load(im_file("png"))""")
    ])
    html = notebook2html!(notebook)
    lines = split(html, '\n')

    @test contains(lines[1], "1 + 1")
    @test contains(lines[2], "2")

    @test contains(lines[end-1], """<img src=\"data:image/png;base64,""")

    notebook = Notebook([
        Cell("md\"This is **markdown**\"")
    ])
    html = notebook2html!(notebook)
    lines = split(html, '\n')
    @test contains(lines[2], "")

    notebook = Notebook([
        Cell("""("pluto", "tree", "object")"""),
        Cell("""["pluto", "tree", "object"]"""),
        Cell("""[1, (2, (3, 4))]""")
    ])
    html = notebook2html!(notebook)
    lines = split(html, '\n')
    @test contains(lines[2], "(\"pluto\", \"tree\", \"object\")")
    @test contains(lines[2], "<pre")
    @test contains(lines[5], "[\"pluto\", \"tree\", \"object\"]")

    @test contains(lines[8], "[1, (2, (3, 4))]")

    notebook = Notebook([
        Cell("struct A end"),
        Cell("""
            struct B
                x::Int
                a::A
            end
            """
            ),
        Cell("B(1, A())")
    ])
    html = notebook2html!(notebook)
    lines = split(html, '\n')
    @test contains(lines[end-1], "B(1, A())")

    notebook = Notebook([
        Cell("md\"my text\"")
    ])
    html = notebook2html!(notebook; hide_md_code=true)
    lines = split(html, '\n')
    @test lines[1] == ""

    html = notebook2html!(notebook; hide_md_code=false)
    lines = split(html, '\n')
    @test lines[1] != ""

    html = notebook2html!(notebook; hide_md_code=false, hide_code=true)
    lines = split(html, '\n')
    @test lines[1] == ""
end

@testset "from_file" begin
    mktempdir() do dir
        file = joinpath(dir, "tmp.jl")
        content = pluto_notebook_content("x = 1 + 2")
        write(file, content)
        html = notebook2html(file)
        @test contains(html, "3")
    end
end

@testset "append_cell" begin
    notebook = Notebook([
        Cell("a = 600 + 1"),
    ])
    c1 = Cell("b = 600 + 2")
    c2 = Cell("c = 600 + 3")
    PlutoStaticHTML._append_cell!(notebook, [c1, c2])
    c3 = Cell("d = 600 + 4")
    html = notebook2html!(notebook; append_cells=[c3])
    for i in 1:4
        @test contains(html, "60$i")
    end
end


text:
@testset "Constructors of Basic Fairness Calc. Metrics" begin
    ft = job_fairtensor()
    # check all constructors
    # These exend the struct from MLJ Base https://github.com/ashryaagr/MLJFair.jl/blob/1d0093232ff215ea8a7e8521b0612162f70a92c3/src/measures/calcmetrics.jl#L18
    m = TruePositive()
    @test m(ft) == truepositive(ft)
    m = TrueNegative()
    @test m(ft) == truenegative(ft)
    m = FalsePositive()
    @test m(ft) == falsepositive(ft)
    m = FalseNegative()
    @test m(ft) == falsenegative(ft)
    m = TruePositiveRate()
    @test m(ft) == tpr(ft) == truepositive_rate(ft)
    m = TrueNegativeRate()
    @test m(ft) == tnr(ft) == truenegative_rate(ft)
    m = FalsePositiveRate()
    @test m(ft) == fpr(ft) == falsepositive_rate(ft)
    m = FalseNegativeRate()
    @test m(ft) == fnr(ft) == falsenegative_rate(ft)
    m = FalseDiscoveryRate()
    @test m(ft) == fdr(ft) == falsediscovery_rate(ft)
    m = Precision()
    @test m(ft) == positive_predictive_value(ft)
    m = NPV()
    @test m(ft) == npv(ft)
    # check synonyms
    m = TPR()
    @test m(ft) == tpr(ft)
    m = TNR()
    @test m(ft) == tnr(ft)
    m = FPR()
    @test m(ft) == fpr(ft) == fallout(ft)
    m = FNR()
    @test m(ft) == fnr(ft) == miss_rate(ft)
    m = FDR()
    @test m(ft) == fdr(ft)
end

@testset "Values for Basic Fairness Calc. Metrics" begin
    ft = job_fairtensor()
    @test true_positive(ft) == 2
    @test truenegative(ft) == 1
    @test falsepositive(ft) == 3
    @test falsenegative(ft) == 4
    @test truepositive_rate(ft) ≈ 1/3
    @test truenegative_rate(ft) ≈ 0.25
    @test falsenegative_rate(ft) ≈ 2/3
    @test falsepositive_rate(ft) ≈ 0.75
    @test falsediscovery_rate(ft) ≈ 0.4
    @test positive_predictive_value(ft) ≈ 0.6
    @test negative_predictive_value(ft) ≈ 0.2
end

@testset "Group Specific Calc. Metrics" begin
    ft = job_fairtensor()
    @test MLJFair._ftIdx(ft, "Education") == 2
    @test_throws ArgumentError MLJFair._ftIdx(ft, "ABCDE")
    @test true_positive(ft; grp=ft.labels[1]) == 2
    @test truenegative(ft; grp=ft.labels[2]) == 1
    @test falsepositive(ft; grp=ft.labels[3]) == 1
    @test falsenegative(ft; grp=ft.labels[1]) == 2
    @test truepositive_rate(ft; grp=ft.labels[1]) ≈ 0.5
    @test truenegative_rate(ft; grp=ft.labels[2]) ≈ 1/3
    @test falsepositive_rate(ft; grp=ft.labels[2]) ≈ 2/3
    @test isapprox(falsenegative_rate(ft; grp=ft.labels[2]), 0.0; atol=1e-10, rtol=0)
    @test isapprox(falsediscovery_rate(ft; grp=ft.labels[3]), 0.0; atol=1e-10, rtol=0)
    @test positive_predictive_value(ft; grp=ft.labels[3]) ≈ 1.0
    @test isapprox(negative_predictive_value(ft; grp=ft.labels[3]) , 0.0; atol=1e-10, rtol=0)
end

@testset "Disparity" begin
    ft = job_fairtensor()
    M = [true_positive_rate, false_positive_rate, ppv]
    @test_throws ArgumentError disparity(M, ft)
    d = disparity(M, ft; refGrp="Education")
    @test names(d) == ["labels", "true_positive_rate_disparity", "false_positive_rate_disparity",
                        "positive_predictive_value_disparity"]
    @test size(d)==(3, 4)
end


text:
abstract type AbstractIBPoissonRHS{TU,TF} end

struct PoissonRHS{TU}
    w::TU
end

struct IBPoissonRHS{TU,TF} <: AbstractIBPoissonRHS{TU,TF}
    w::TU
    ψb::TF
end

struct ConstrainedIBPoissonRHS{T,TU,TF} <: AbstractIBPoissonRHS{TU,TF}
    w::TU
    ψb::TF
    fconstraintRHS::Vector{T}
end

struct UnsteadyRegularizedIBPoissonRHS{TU,TF} <: AbstractIBPoissonRHS{TU,TF}
    w::TU
    ψb::TF
    f̃lim_vec::Vector{f̃Limits}
    Γw_vec::Vector{Float64}
end

function _scaletoindexspace!(rhs::PoissonRHS, Δx::Real)
    rhs.w .*= Δx
end

function _scaletoindexspace!(rhs::IBPoissonRHS, Δx::Real)
    rhs.w .*= Δx
    rhs.ψb ./= Δx
end

function _scaletoindexspace!(rhs::ConstrainedIBPoissonRHS, Δx::Real)
    rhs.w .*= Δx
    rhs.ψb ./= Δx
    rhs.fconstraintRHS ./= Δx
end

function _scaletoindexspace!(rhs::UnsteadyRegularizedIBPoissonRHS, Δx::Real)
    rhs.w .*= Δx
    rhs.ψb ./= Δx
    rhs.f̃lim_vec ./= Δx  # Use same scaling for σ as for f
    rhs.Γw_vec ./= Δx
end


text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

struct BatchV2alpha1Api <: SwaggerApi
    client::Swagger.Client
end

"""

create a CronJob
Param: namespace::String (required)
Param: body::IoK8sApiBatchV2alpha1CronJob (required)
Param: pretty::String
Param: dryRun::String
Param: fieldManager::String
Return: IoK8sApiBatchV2alpha1CronJob
"""
function createBatchV2alpha1NamespacedCronJob(_api::BatchV2alpha1Api, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "POST", IoK8sApiBatchV2alpha1CronJob, "/apis/batch/v2alpha1/namespaces/{namespace}/cronjobs", ["BearerToken"], body)
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "dryRun", dryRun)  # type String
    Swagger.set_param(_ctx.query, "fieldManager", fieldManager)  # type String
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

delete collection of CronJob
Param: namespace::String (required)
Param: pretty::String
Param: __continue__::String
Param: fieldSelector::String
Param: labelSelector::String
Param: limit::Int32
Param: resourceVersion::String
Param: timeoutSeconds::Int32
Param: watch::Bool
Return: IoK8sApimachineryPkgApisMetaV1Status
"""
function deleteBatchV2alpha1CollectionNamespacedCronJob(_api::BatchV2alpha1Api, namespace::String; pretty=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "DELETE", IoK8sApimachineryPkgApisMetaV1Status, "/apis/batch/v2alpha1/namespaces/{namespace}/cronjobs", ["BearerToken"])
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "continue", __continue__)  # type String
    Swagger.set_param(_ctx.query, "fieldSelector", fieldSelector)  # type String
    Swagger.set_param(_ctx.query, "labelSelector", labelSelector)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "resourceVersion", resourceVersion)  # type String
    Swagger.set_param(_ctx.query, "timeoutSeconds", timeoutSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "watch", watch)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

delete a CronJob
Param: name::String (required)
Param: namespace::String (required)
Param: pretty::String
Param: body::IoK8sApimachineryPkgApisMetaV1DeleteOptions
Param: dryRun::String
Param: gracePeriodSeconds::Int32
Param: orphanDependents::Bool
Param: propagationPolicy::String
Return: IoK8sApimachineryPkgApisMetaV1Status
"""
function deleteBatchV2alpha1NamespacedCronJob(_api::BatchV2alpha1Api, name::String, namespace::String; pretty=nothing, body=nothing, dryRun=nothing, gracePeriodSeconds=nothing, orphanDependents=nothing, propagationPolicy=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "DELETE", IoK8sApimachineryPkgApisMetaV1Status, "/apis/batch/v2alpha1/namespaces/{namespace}/cronjobs/{name}", ["BearerToken"], body)
    Swagger.set_param(_ctx.path, "name", name)  # type String
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "dryRun", dryRun)  # type String
    Swagger.set_param(_ctx.query, "gracePeriodSeconds", gracePeriodSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "orphanDependents", orphanDependents)  # type Bool
    Swagger.set_param(_ctx.query, "propagationPolicy", propagationPolicy)  # type String
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

get available resources
Return: IoK8sApimachineryPkgApisMetaV1APIResourceList
"""
function getBatchV2alpha1APIResources(_api::BatchV2alpha1Api; _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApimachineryPkgApisMetaV1APIResourceList, "/apis/batch/v2alpha1/", ["BearerToken"])
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

list or watch objects of kind CronJob
Param: __continue__::String
Param: fieldSelector::String
Param: labelSelector::String
Param: limit::Int32
Param: pretty::String
Param: resourceVersion::String
Param: timeoutSeconds::Int32
Param: watch::Bool
Return: IoK8sApiBatchV2alpha1CronJobList
"""
function listBatchV2alpha1CronJobForAllNamespaces(_api::BatchV2alpha1Api; __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApiBatchV2alpha1CronJobList, "/apis/batch/v2alpha1/cronjobs", ["BearerToken"])
    Swagger.set_param(_ctx.query, "continue", __continue__)  # type String
    Swagger.set_param(_ctx.query, "fieldSelector", fieldSelector)  # type String
    Swagger.set_param(_ctx.query, "labelSelector", labelSelector)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "resourceVersion", resourceVersion)  # type String
    Swagger.set_param(_ctx.query, "timeoutSeconds", timeoutSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "watch", watch)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf", "application/json;stream=watch", "application/vnd.kubernetes.protobuf;stream=watch"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

list or watch objects of kind CronJob
Param: namespace::String (required)
Param: pretty::String
Param: __continue__::String
Param: fieldSelector::String
Param: labelSelector::String
Param: limit::Int32
Param: resourceVersion::String
Param: timeoutSeconds::Int32
Param: watch::Bool
Return: IoK8sApiBatchV2alpha1CronJobList
"""
function listBatchV2alpha1NamespacedCronJob(_api::BatchV2alpha1Api, namespace::String; pretty=nothing, __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApiBatchV2alpha1CronJobList, "/apis/batch/v2alpha1/namespaces/{namespace}/cronjobs", ["BearerToken"])
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "continue", __continue__)  # type String
    Swagger.set_param(_ctx.query, "fieldSelector", fieldSelector)  # type String
    Swagger.set_param(_ctx.query, "labelSelector", labelSelector)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "resourceVersion", resourceVersion)  # type String
    Swagger.set_param(_ctx.query, "timeoutSeconds", timeoutSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "watch", watch)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf", "application/json;stream=watch", "application/vnd.kubernetes.protobuf;stream=watch"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

partially update the specified CronJob
Param: name::String (required)
Param: namespace::String (required)
Param: body::IoK8sApimachineryPkgApisMetaV1Patch (required)
Param: pretty::String
Param: dryRun::String
Param: fieldManager::String
Param: force::Bool
Return: IoK8sApiBatchV2alpha1CronJob
"""
function patchBatchV2alpha1NamespacedCronJob(_api::BatchV2alpha1Api, name::String, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, force=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "PATCH", IoK8sApiBatchV2alpha1CronJob, "/apis/batch/v2alpha1/namespaces/{namespace}/cronjobs/{name}", ["BearerToken"], body)
    Swagger.set_param(_ctx.path, "name", name)  # type String
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "dryRun", dryRun)  # type String
    Swagger.set_param(_ctx.query, "fieldManager", fieldManager)  # type String
    Swagger.set_param(_ctx.query, "force", force)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json-patch+json", "application/merge-patch+json", "application/strategic-merge-patch+json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

partially update status of the specified CronJob
Param: name::String (required)
Param: namespace::String (required)
Param: body::IoK8sApimachineryPkgApisMetaV1Patch (required)
Param: pretty::String
Param: dryRun::String
Param: fieldManager::String
Param: force::Bool
Return: IoK8sApiBatchV2alpha1CronJob
"""
function patchBatchV2alpha1NamespacedCronJobStatus(_api::BatchV2alpha1Api, name::String, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, force=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "PATCH", IoK8sApiBatchV2alpha1CronJob, "/apis/batch/v2alpha1/namespaces/{namespace}/cronjobs/{name}/status", ["BearerToken"], body)
    Swagger.set_param(_ctx.path, "name", name)  # type String
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "dryRun", dryRun)  # type String
    Swagger.set_param(_ctx.query, "fieldManager", fieldManager)  # type String
    Swagger.set_param(_ctx.query, "force", force)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json-patch+json", "application/merge-patch+json", "application/strategic-merge-patch+json"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

read the specified CronJob
Param: name::String (required)
Param: namespace::String (required)
Param: pretty::String
Param: exact::Bool
Param: __export__::Bool
Return: IoK8sApiBatchV2alpha1CronJob
"""
function readBatchV2alpha1NamespacedCronJob(_api::BatchV2alpha1Api, name::String, namespace::String; pretty=nothing, exact=nothing, __export__=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApiBatchV2alpha1CronJob, "/apis/batch/v2alpha1/namespaces/{namespace}/cronjobs/{name}", ["BearerToken"])
    Swagger.set_param(_ctx.path, "name", name)  # type String
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "exact", exact)  # type Bool
    Swagger.set_param(_ctx.query, "export", __export__)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

read status of the specified CronJob
Param: name::String (required)
Param: namespace::String (required)
Param: pretty::String
Return: IoK8sApiBatchV2alpha1CronJob
"""
function readBatchV2alpha1NamespacedCronJobStatus(_api::BatchV2alpha1Api, name::String, namespace::String; pretty=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApiBatchV2alpha1CronJob, "/apis/batch/v2alpha1/namespaces/{namespace}/cronjobs/{name}/status", ["BearerToken"])
    Swagger.set_param(_ctx.path, "name", name)  # type String
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

replace the specified CronJob
Param: name::String (required)
Param: namespace::String (required)
Param: body::IoK8sApiBatchV2alpha1CronJob (required)
Param: pretty::String
Param: dryRun::String
Param: fieldManager::String
Return: IoK8sApiBatchV2alpha1CronJob
"""
function replaceBatchV2alpha1NamespacedCronJob(_api::BatchV2alpha1Api, name::String, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "PUT", IoK8sApiBatchV2alpha1CronJob, "/apis/batch/v2alpha1/namespaces/{namespace}/cronjobs/{name}", ["BearerToken"], body)
    Swagger.set_param(_ctx.path, "name", name)  # type String
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "dryRun", dryRun)  # type String
    Swagger.set_param(_ctx.query, "fieldManager", fieldManager)  # type String
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

replace status of the specified CronJob
Param: name::String (required)
Param: namespace::String (required)
Param: body::IoK8sApiBatchV2alpha1CronJob (required)
Param: pretty::String
Param: dryRun::String
Param: fieldManager::String
Return: IoK8sApiBatchV2alpha1CronJob
"""
function replaceBatchV2alpha1NamespacedCronJobStatus(_api::BatchV2alpha1Api, name::String, namespace::String, body; pretty=nothing, dryRun=nothing, fieldManager=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "PUT", IoK8sApiBatchV2alpha1CronJob, "/apis/batch/v2alpha1/namespaces/{namespace}/cronjobs/{name}/status", ["BearerToken"], body)
    Swagger.set_param(_ctx.path, "name", name)  # type String
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "dryRun", dryRun)  # type String
    Swagger.set_param(_ctx.query, "fieldManager", fieldManager)  # type String
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

watch individual changes to a list of CronJob. deprecated: use the 'watch' parameter with a list operation instead.
Param: __continue__::String
Param: fieldSelector::String
Param: labelSelector::String
Param: limit::Int32
Param: pretty::String
Param: resourceVersion::String
Param: timeoutSeconds::Int32
Param: watch::Bool
Return: IoK8sApimachineryPkgApisMetaV1WatchEvent
"""
function watchBatchV2alpha1CronJobListForAllNamespaces(_api::BatchV2alpha1Api; __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApimachineryPkgApisMetaV1WatchEvent, "/apis/batch/v2alpha1/watch/cronjobs", ["BearerToken"])
    Swagger.set_param(_ctx.query, "continue", __continue__)  # type String
    Swagger.set_param(_ctx.query, "fieldSelector", fieldSelector)  # type String
    Swagger.set_param(_ctx.query, "labelSelector", labelSelector)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "resourceVersion", resourceVersion)  # type String
    Swagger.set_param(_ctx.query, "timeoutSeconds", timeoutSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "watch", watch)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf", "application/json;stream=watch", "application/vnd.kubernetes.protobuf;stream=watch"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

watch changes to an object of kind CronJob. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.
Param: name::String (required)
Param: namespace::String (required)
Param: __continue__::String
Param: fieldSelector::String
Param: labelSelector::String
Param: limit::Int32
Param: pretty::String
Param: resourceVersion::String
Param: timeoutSeconds::Int32
Param: watch::Bool
Return: IoK8sApimachineryPkgApisMetaV1WatchEvent
"""
function watchBatchV2alpha1NamespacedCronJob(_api::BatchV2alpha1Api, name::String, namespace::String; __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApimachineryPkgApisMetaV1WatchEvent, "/apis/batch/v2alpha1/watch/namespaces/{namespace}/cronjobs/{name}", ["BearerToken"])
    Swagger.set_param(_ctx.path, "name", name)  # type String
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "continue", __continue__)  # type String
    Swagger.set_param(_ctx.query, "fieldSelector", fieldSelector)  # type String
    Swagger.set_param(_ctx.query, "labelSelector", labelSelector)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "resourceVersion", resourceVersion)  # type String
    Swagger.set_param(_ctx.query, "timeoutSeconds", timeoutSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "watch", watch)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf", "application/json;stream=watch", "application/vnd.kubernetes.protobuf;stream=watch"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    Swagger.exec(_ctx)
end

"""

watch individual changes to a list of CronJob. deprecated: use the 'watch' parameter with a list operation instead.
Param: namespace::String (required)
Param: __continue__::String
Param: fieldSelector::String
Param: labelSelector::String
Param: limit::Int32
Param: pretty::String
Param: resourceVersion::String
Param: timeoutSeconds::Int32
Param: watch::Bool
Return: IoK8sApimachineryPkgApisMetaV1WatchEvent
"""
function watchBatchV2alpha1NamespacedCronJobList(_api::BatchV2alpha1Api, namespace::String; __continue__=nothing, fieldSelector=nothing, labelSelector=nothing, limit=nothing, pretty=nothing, resourceVersion=nothing, timeoutSeconds=nothing, watch=nothing, _mediaType=nothing)
    _ctx = Swagger.Ctx(_api.client, "GET", IoK8sApimachineryPkgApisMetaV1WatchEvent, "/apis/batch/v2alpha1/watch/namespaces/{namespace}/cronjobs", ["BearerToken"])
    Swagger.set_param(_ctx.path, "namespace", namespace)  # type String
    Swagger.set_param(_ctx.query, "continue", __continue__)  # type String
    Swagger.set_param(_ctx.query, "fieldSelector", fieldSelector)  # type String
    Swagger.set_param(_ctx.query, "labelSelector", labelSelector)  # type String
    Swagger.set_param(_ctx.query, "limit", limit)  # type Int32
    Swagger.set_param(_ctx.query, "pretty", pretty)  # type String
    Swagger.set_param(_ctx.query, "resourceVersion", resourceVersion)  # type String
    Swagger.set_param(_ctx.query, "timeoutSeconds", timeoutSeconds)  # type Int32
    Swagger.set_param(_ctx.query, "watch", watch)  # type Bool
    Swagger.set_header_accept(_ctx, ["application/json", "application/yaml", "application/vnd.kubernetes.protobuf", "application/json;stream=watch", "application/vnd.kubernetes.protobuf;stream=watch"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["*/*"] : [_mediaType])
    Swagger.exec(_ctx)
end

export createBatchV2alpha1NamespacedCronJob, deleteBatchV2alpha1CollectionNamespacedCronJob, deleteBatchV2alpha1NamespacedCronJob, getBatchV2alpha1APIResources, listBatchV2alpha1CronJobForAllNamespaces, listBatchV2alpha1NamespacedCronJob, patchBatchV2alpha1NamespacedCronJob, patchBatchV2alpha1NamespacedCronJobStatus, readBatchV2alpha1NamespacedCronJob, readBatchV2alpha1NamespacedCronJobStatus, replaceBatchV2alpha1NamespacedCronJob, replaceBatchV2alpha1NamespacedCronJobStatus, watchBatchV2alpha1CronJobListForAllNamespaces, watchBatchV2alpha1NamespacedCronJob, watchBatchV2alpha1NamespacedCronJobList


text:
#=
This file is auto-generated. Do not edit.
=#

mutable struct TechRenewable <: TechnicalParams
    rating::Float64  # Thermal limited MVA Power Output of the unit. <= Capacity
    primemover::PrimeMovers  # PrimeMover Technology according to EIA 923
    reactivepowerlimits::Union{Nothing, Min_Max}
    powerfactor::Float64
    internal::InfrastructureSystemsInternal
end

function TechRenewable(rating, primemover, reactivepowerlimits, powerfactor, )
    TechRenewable(rating, primemover, reactivepowerlimits, powerfactor, InfrastructureSystemsInternal())
end

function TechRenewable(; rating, primemover, reactivepowerlimits, powerfactor, )
    TechRenewable(rating, primemover, reactivepowerlimits, powerfactor, )
end

# Constructor for demo purposes; non-functional.

function TechRenewable(::Nothing)
    TechRenewable(;
        rating=0.0,
        primemover=OT::PrimeMovers,
        reactivepowerlimits=nothing,
        powerfactor=1.0,
    )
end

"""Get TechRenewable rating."""
get_rating(value::TechRenewable) = value.rating
"""Get TechRenewable primemover."""
get_primemover(value::TechRenewable) = value.primemover
"""Get TechRenewable reactivepowerlimits."""
get_reactivepowerlimits(value::TechRenewable) = value.reactivepowerlimits
"""Get TechRenewable powerfactor."""
get_powerfactor(value::TechRenewable) = value.powerfactor
"""Get TechRenewable internal."""
get_internal(value::TechRenewable) = value.internal