@testset "population initialization" begin
    for clonalmutations in [0,100]
        pop1 = SomaticEvolution.initialize_population(100, clonalmutations=clonalmutations)
        pop2 = SomaticEvolution.initialize_population(clonalmutations=clonalmutations)
        for pop in (pop1, pop2)
            @test length(pop) == 1
            @test pop[1].Nvec[1] == 1
            @test length(pop[1].Nvec) == length(pop[1].tvec) == 1
            @test pop[1].tvec[1] == 0.0
            @test length(pop[1].cells) == 1
            @test length(pop[1].cells[1].mutations) == clonalmutations
            @test pop[1].cells[1].clonetype == 1
            @test length(pop[1].subclones) == 0
            @test pop[1].id == 1
        end
    end
end

@testset "module initialization" begin
    @testset "branching" begin
        for clonalmutations in [0,100]
            module1 = SomaticEvolution.initializesim_branching(100, clonalmutations=clonalmutations)
            module2 = SomaticEvolution.initializesim_branching(clonalmutations=clonalmutations)
            for initmodule in (module1, module2)
                @test initmodule.Nvec[1] == 1
                @test length(initmodule.Nvec) == length(initmodule.tvec) == 1
                @test initmodule.tvec[1] == 0.0
                @test length(initmodule.cells) == 1
                @test length(initmodule.cells[1].mutations) == clonalmutations
                @test initmodule.cells[1].clonetype == 1
                @test length(initmodule.subclones) == 0
                @test initmodule.id == 1
            end
        end

        @testset "moran" begin
            for clonalmutations in [0,100]
                N = 100
                initmodule = SomaticEvolution.initializesim_moran(N, clonalmutations=clonalmutations)
                @test initmodule.Nvec[1] == N
                @test length(initmodule.Nvec) == length(initmodule.tvec) == 1
                @test initmodule.tvec[1] == 0.0
                @test length(initmodule.cells) == N
                allmuts = [cell.mutations for cell in initmodule.cells]
                @test all(x -> x == allmuts[1], allmuts)
                @test length(initmodule.cells[1].mutations) == clonalmutations
                @test initmodule.cells[1].clonetype == 1
                @test length(initmodule.subclones) == 0
                @test initmodule.id == 1
            end
        end

        @testset "sampled" begin
            subclones = SomaticEvolution.CloneTracker[]
            cells = Cell[]
            push!(cells, Cell([1,2,3,4], 1))
            push!(cells, Cell([1,2,3], 1))
            id = 1
            parentid = 0
            inittime = 5
            initmodule = SomaticEvolution.initializesim_from_cells(cells, subclones, id,
                parentid, inittime=inittime)
            @test initmodule.Nvec[1] == length(cells)
            @test length(initmodule.Nvec) == length(initmodule.tvec) == 1
            @test initmodule.tvec[1] == inittime
            @test length(initmodule.cells) == length(cells)
            @test initmodule.cells[1].mutations == [1,2,3,4]
            @test initmodule.cells[1].clonetype == initmodule.cells[2].clonetype == 1
            @test length(initmodule.subclones) == length(subclones)
            @test initmodule.id == id
        end
    end
end


function update!(dQ, Q, rka, rkb, dt, slow_δ, slow_dQ, slow_scaling)
  @inbounds @loop for i = (1:length(Q);
                           (blockIdx().x - 1) * blockDim().x + threadIdx().x)
    if slow_δ !== nothing
      dQ[i] += slow_δ * slow_dQ[i]
    end
    Q[i] += rkb * dt * dQ[i]
    dQ[i] *= rka
    if slow_scaling !== nothing
      slow_dQ[i] *= slow_scaling
    end
  end
end


#default implementation
@impl begin
    struct ScoreGetLogScore end
    function get_log_score(sf::Score{I}, i::I)::AbstractFloat where {I}
        return log(get_score(sf, i))
    end
end


# MATLAB

# specific to Julia, that is, related to the output file format (language).

#=
F: read the file containing constants
I:
O:
BA
=#
function m_include_constants_from_literature(src_file_name, pad_string)
  # create src_buffer -
  src_buffer = ""
  # path to distrubtion -
  path_to_src_file = src_file_name
  open(path_to_src_file,"r") do src_file
    for line in eachline(src_file)
      src_buffer *= pad_string*"$line"
    end
  end
  return src_buffer
end

#=
F: generate kinetic buffer
I:
O: return monod affinity constant symbol, mRNA species array,
  protein species array, W_string_array, disassociation_const_string_array.
BA
=#
function m_build_kinetics_buffer(all_species_dict::Dict{String, Int},
  all_rnx_list::Array, all_txtl_dict::Dict, sys2user::Dict)
  kinetics = "function [rnx_rate_vector, TX_rate_vector, TL_rate_vector] = Kinetics(X, data_dictionary)"
            #  "\n\n\t% load all species dictionary" *
            #  "\n\tall_species_dict = data_dictionary(\'all_species_dict\');  % String-->Int"
  # for signaling
  kinetics *= "\n\n\t% generate kinetics for signaling, assume saturation kinetics"
  totalRnxNo = length(all_rnx_list)
  kinetics *= "\n\t% load Monod affinity constants" *
              "\n\tMonodK = data_dictionary(\'MonodAffinityConstantDict\');  % String-->Float"
  MonodAffinityConstant_String_Array = Array{String,1}()  # collect monod affinity constant symbol
  kinetics *= "\n\t% load reaction kinetic constants" *
              "\n\tkcat = data_dictionary(\'kcat_signaling\');  % in order of rnx" *
              "\n\n\t% write reaction rate equations" *
              "\n\trnx_rate_vector = zeros($totalRnxNo, 1);"
  for (index, rnx) in enumerate(all_rnx_list)  # go thru every rnx
    tmp_rnx_rate = "kcat($index)"
    if isdefined(rnx, :catalysts)  # enzyme
      for token in rnx.catalysts
        tmp_rnx_rate *= "*X($(all_species_dict[token.oriBioName]))"
      end
    end
    if isdefined(rnx,:reactants)  # substrate
      for token in rnx.reactants
        tmp_rnx_rate *= "*X($(all_species_dict[token.oriBioName]))/(MonodK(\'MonodK~rnx$(index)~$(token.oriBioName)\')+X($(all_species_dict[token.oriBioName])))"
        push!(MonodAffinityConstant_String_Array, "MonodK~rnx$(index)~$(token.oriBioName)")
      end
    end
    kinetics *= "\n\trnx_rate_vector($index) = "*tmp_rnx_rate * ";"
  end

  # for TXTL
  W_string_array = Array{String,1}()  # W: weight of protein action on transcription
  disassociation_const_string_array = Array{String,1}()  # disassociation constant in tranfer function
  kinetics *= "\n\n\t% generate kinetics & control terms for TXTL;" *
              "\n\t% load data from data dictionary;" *
              "\n\tW_value_dict = data_dictionary(\'W_value_dict\');  % String-->Float" *
              "\n\tcoop = data_dictionary(\'cooperativity\');" *
              "\n\tdisassociation_constant_dict = data_dictionary(\'transferFunctionDisassociationConstantDict\');" *
              "\n\tbackground_control_term = data_dictionary(\'backgroundGeneExpressionControlTermDict\');" *
              "\n\tkcatTX = data_dictionary(\'kcatTranscription\');" *
              "\n\tRNAPconc = data_dictionary(\'RNAPConcentration\');" *
              "\n\tgeneConc = data_dictionary(\'avgGeneConcentration\');" *
              "\n\tsaturationTX = data_dictionary(\'transcriptionSaturationConstant\');" *
              "\n\tkcatTL = data_dictionary(\'kcatTranslation\');" *
              "\n\tRIBOconc = data_dictionary(\'RIBOConcentration\');" *
              "\n\tsaturationTL = data_dictionary(\'translationSaturationConstant\');"

  totalTXTLNo = length(all_txtl_dict)
  kinetics *= "\n\n\t% write control terms, transcription rate equations, and translation rate equations" *
             "\n\tTX_control_term = containers.Map();  % control term" *
             "\n\tTX_rate_vector = containers.Map();  % transcription rate" *
             "\n\tTL_rate_vector = containers.Map();  % translation rate"
  for (key, txtl) in all_txtl_dict # go thru every txtl
    tmp_protein_string = replace(key, sys2user["MRNA"] => sys2user["PROTEIN"], count=1)
    kinetics *= "\n\t% $key and $tmp_protein_string"
    up_factors_array = Array{String,1}()  # collection of upregulation factors name: targetedmRNA_factor(s)
    if !isempty(txtl.activationProtein)  # upregulation
      for token_array in txtl.activationProtein
        tmp_up = ""
        tmp_up_name = key*"_"
        for token in token_array
          tmp_W = "W~$(token.oriBioName)~$(key)"
          tmp_up *= "W_value_dict(\'$(tmp_W)\') * X($(all_species_dict[token.oriBioName]))^coop/((disassociation_constant_dict(\'KD~$(token.oriBioName)\'))^coop+X($(all_species_dict[token.oriBioName]))^coop)*"
          push!(W_string_array, tmp_W)
          push!(disassociation_const_string_array, "KD~$(token.oriBioName)")
          tmp_up_name *= token.oriBioName*"_"
        end
        tmp_up = chop(tmp_up)
        tmp_up_name = chop(tmp_up_name)
        push!(up_factors_array, tmp_up_name)
        kinetics *= "\n\t$(tmp_up_name) = $(tmp_up);"
      end
    end
    down_factors_array = Array{String,1}()  # collection of downregulation factors name: targetedmRNA_factor(s)
    if !isempty(txtl.inhibitionProtein)  # downregulation
      for token_array in txtl.inhibitionProtein
        tmp_down = ""
        tmp_down_name = key*"_"
        for token in token_array
          tmp_W = "W~$(token.oriBioName)~$(key)"
          tmp_down *= "W_value_dict(\'$(tmp_W)\') * X($(all_species_dict[token.oriBioName]))^coop/((disassociation_constant_dict(\'KD~$(token.oriBioName)\'))^coop+X($(all_species_dict[token.oriBioName]))^coop)*"
          push!(W_string_array, tmp_W)
          push!(disassociation_const_string_array, "KD~$(token.oriBioName)")
          tmp_down_name *= token.oriBioName*"_"
        end
        tmp_down = chop(tmp_down)
        tmp_down_name = chop(tmp_down_name)
        push!(down_factors_array, tmp_down_name)
        kinetics *= "\n\t$(tmp_down_name) = $(tmp_down);"
      end
    end
    # combine to form up and down, then merge up and down to form control term
    if !isempty(up_factors_array) && !isempty(down_factors_array)
      up_action = ""
      for up in up_factors_array
        up_action *= up*" +"
      end
      kinetics *= "\n\tup_action_on_$key = $(chop(up_action));"
      down_action = ""
      for down in down_factors_array
        down_action *= down*" +"
      end
      kinetics *= "\n\tdown_action_on_$key = $(chop(down_action));" *
                  "\n\tTX_control_term(\'$key\') = (background_control_term(\'$key\') + up_action_on_$key)/(1 + background_control_term(\'$key\') + up_action_on_$key + down_action_on_$key);"
    elseif !isempty(up_factors_array)
      up_action = ""
      for up in up_factors_array
        up_action *= up*" +"
      end
      kinetics *= "\n\tup_action_on_$key = $(chop(up_action));" *
                  "\n\tTX_control_term(\'$key\') = (background_control_term(\'$key\') + up_action_on_$key)/(1 + background_control_term(\'$key\') + up_action_on_$key);"
    elseif !isempty(down_factors_array)
      down_action = ""
      for down in down_factors_array
        down_action *= down*" +"
      end
      kinetics *= "\n\tdown_action_on_$key = $(chop(down_action));" *
                  "\n\tTX_control_term(\'$key\') = background_control_term(\'$key\')/(1 + background_control_term(\'$key\') + down_action_on_$key);"
    else
      # seems impossible, a TXTL dict is constructed b/c something acts on the key
    end
    kinetics *= "\n\tTX_rate_vector(\'$key\') = TX_control_term(\'$key\') * kcatTX * RNAPconc * geneConc / (saturationTX + geneConc);" *
                "\n\tTL_rate_vector(\'$tmp_protein_string\') = kcatTL * RIBOconc * X($(all_species_dict[key])) / (saturationTL + X($(all_species_dict[key])));"
  end

  kinetics *= "\n\nend"
  return (kinetics, MonodAffinityConstant_String_Array, W_string_array, disassociation_const_string_array)
end

function m_build_data_dictionary_buffer(host_type::AbstractString, all_species_array::Array,
  all_species2index_dict::Dict,
  rnx_species_array::Array, all_rnx_list::Array, all_txtl_dict::Dict,
  Monod_affinity_constant_array::Array, W_string_array::Array,
  disassociation_const_array::Array, mRNA_species_array::Array, protein_species_array::Array)
  # data dictionary
  buffer = "function dataDictionary = DataDictionary()"

  # stoichiometry
  buffer *= "\n\n\t% load stoichiometry"
  buffer *= "\n\tstoichiometric_matrix = load(\'stoichiometry.dat\');"
  No_species = length(all_species_array)
  # initial concentration
  buffer *= "\n\n\t% initial species concentration" *
    "\n\tinitial_condition = [\n\t\t"
  buffer *= join(["0.001, ...  % $i $(all_species_array[i])" for i = 1:(No_species - 1)], "\n\t\t")
  buffer *= "\n\t\t0.001  ...  % $No_species $(all_species_array[No_species])"
  buffer *= "\n\t];"
  # all species dictionary
  buffer *= "\n\n\t% all species dictionary"
  buffer *= "\n\tall_species_dict = containers.Map();  % String --> Int"
  #all_species_dict = Dict{String, Int64}()  # create all species dict: string --> int64
  for (st, index) in all_species2index_dict
    buffer *= "\n\tall_species_dict(\'$st\') = $index;"
  end
  # all species dictionary_reversed
  buffer *= "\n\n\t% all species dictionary_reversed"
  buffer *= "\n\tkey = keys(all_species_dict);" *
            "\n\tval = values(all_species_dict);" *
            "\n\tall_species_reversed_dict = containers.Map(val, key);  % Int --> String"

  # all rnx species array
  buffer *= "\n\n\t% all rnx species array"
  buffer *= "\n\trnx_species_array = {..." *
    join(["\n\t\t\'$st\'" for st in rnx_species_array], ", ...") *
    "  ...\n\t};"
  # all mRNA species array
  buffer *= "\n\n\t% all mRNA species array"
  buffer *= "\n\tmRNA_species_array = {..." *
    join(["\n\t\t\'$st\'" for st in mRNA_species_array], ", ...") *
    "  ...\n\t};"
  # all protein species array
  buffer *= "\n\n\t% all protein species array"
  buffer *= "\n\tprotein_species_array = {..." *
    join(["\n\t\t\'$st\'" for st in protein_species_array], ", ...") *
    "  ...\n\t};"
  # signaling reaction kinetic constants, reaction name: reactant(s)<rnx type:catalyst(s)>product(s)
  buffer *= "\n\n\t% signaling reaction kinetic constants, reaction name: reactant(s)<rnx type:catalyst(s)>product(s)"
  buffer *= "\n\tkcat_signaling = 1e-3*ones(1, $(length(all_rnx_list)));  % kcat[#reaction]: reaction name"
  for (id, rnx) in enumerate(all_rnx_list)
    buffer*= "\n\tkcat_signaling($id) = 1.0;  % kcat: $(rnx.rnxName)"
  end
  # Monod affinity constant in signaling reaction, nomenclature: MonodK_#reaction_Substrate
  buffer *= "\n\n\t% Monod affinity constant in signaling reaction, nomenclature: MonodK_#reaction_Substrate"
  buffer *= "\n\tMonodAffinityConstantDict = containers.Map();"
  for MK in Monod_affinity_constant_array
    # buffer *= "\n\t$MK = 1.0"
    # buffer *= "\n\tMonodAffinityConstant_dict(\'$MK\') = $MK"
    buffer *= "\n\tMonodAffinityConstantDict(\'$MK\') = 1.0;"
  end
  # W_value in transcription control term, nomenclature: W_targetmRNA_actor
  buffer *= "\n\n\n\t% W_value in transcription control term, nomenclature: W_targetmRNA_actor"
  buffer *= "\n\tW_value_dict = containers.Map();"
  for WS in W_string_array
    buffer *= "\n\tW_value_dict(\'$WS\') = 1.0;"
  end
  # disassociation constants in transfer function, nomenclature: KD_speciesName
  buffer *= "\n\n\t% disassociation constants in transfer function, nomenclature: KD_speciesName"
  buffer *= "\n\ttransferFunctionDisassociationConstantDict = containers.Map();"
  for DC in disassociation_const_array
    buffer *= "\n\ttransferFunctionDisassociationConstantDict(\'$DC\') = 1.0;"
  end
  # transcription specific correction factor
  buffer *= "\n\n\t% transcription specific correction factor"
  buffer *= "\n\ttranscriptionSpecificCorrectionFactorDict = containers.Map();"
  for mrna in mRNA_species_array
    buffer *= "\n\ttranscriptionSpecificCorrectionFactorDict(\'$mrna\') = 1.0;  % $mrna"
  end
  # background gene expression control term
  buffer *= "\n\n\t% background gene expression control term"
  buffer *= "\n\tbackgroundGeneExpressionControlTermDict = containers.Map();"
  for mrna in mRNA_species_array
    buffer *= "\n\tbackgroundGeneExpressionControlTermDict(\'$mrna\') = 0.1;  % $mrna"
  end
  # translation specific correction factor
  buffer *= "\n\n\t% translation specific correction factor"
  buffer *= "\n\ttranslationSpecificCorrectionFactor = containers.Map();"
  for pro in protein_species_array
    buffer *= "\n\ttranslationSpecificCorrectionFactor(\'$pro\') = 1.0;  % $pro"
  end
  # cooperativity number in transfer function
  buffer *= "\n\n\t% cooperativity number in transfer function"
  buffer *= "\n\tcooperativity = 1;"
  #buffer *= "\n\n\tbackground_mRNA_synthesis_rate_vector = 0.01*ones($length_TXTL)"
  # load txtl constants buffer
  buffer *= "\n"
  path_head = Base.@__DIR__
  if host_type == "bacteria"
    buffer *= replace(replace(m_include_constants_from_literature(joinpath(path_head,
        "txtl_constants_ecoli.jl"),"\n\t"), "#" => "%"), r"(  | \t|\t\t)% " => "  ; % ")
  else
    buffer *= replace(m_include_constants_from_literature(joinpath(path_head,
        "txtl_constants_hl60.jl"),"\n"), "#" => "%")
  end

  #---------------------------------
  # put all stuff in a Dictionary
  buffer *= "\n\n\t%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"
  buffer *= "\n\t% put all stuff in a Dictionary"
  buffer *= "\n\tdataDictionary = containers.Map();"
  buffer *= "\n\tdataDictionary(\'stoichiometric_matrix\') = stoichiometric_matrix;"
  buffer *= "\n\tdataDictionary(\'all_species_dict\') = all_species_dict;"
  buffer *= "\n\tdataDictionary(\'all_species_reversed_dict\') = all_species_reversed_dict;"
  buffer *= "\n\tdataDictionary(\'rnx_species_array\') = rnx_species_array;"
  buffer *= "\n\tdataDictionary(\'mRNA_species_array\') = mRNA_species_array;"
  buffer *= "\n\tdataDictionary(\'protein_species_array\') = protein_species_array;"
  buffer *= "\n\tdataDictionary(\'initial_condition\') = initial_condition;"

  buffer *= "\n\tdataDictionary(\'kcat_signaling\') = kcat_signaling;"
  buffer *= "\n\tdataDictionary(\'MonodAffinityConstantDict\') = MonodAffinityConstantDict;"
  buffer *= "\n\tdataDictionary(\'W_value_dict\') = W_value_dict;"
  buffer *= "\n\tdataDictionary(\'transferFunctionDisassociationConstantDict\') = transferFunctionDisassociationConstantDict;"
  buffer *= "\n\tdataDictionary(\'transcriptionSpecificCorrectionFactorDict\') = transcriptionSpecificCorrectionFactorDict;"
  buffer *= "\n\tdataDictionary(\'backgroundGeneExpressionControlTermDict\') = backgroundGeneExpressionControlTermDict;"
  buffer *= "\n\tdataDictionary(\'translationSpecificCorrectionFactor\') = translationSpecificCorrectionFactor;"
  buffer *= "\n\tdataDictionary(\'cooperativity\') = cooperativity;"
  buffer *= "\n\tdataDictionary(\'RNAPConcentration\') = rnapII_concentration;"
  buffer *= "\n\tdataDictionary(\'RIBOConcentration\') = ribosome_concentration;"
  buffer *= "\n\tdataDictionary(\'degradationConstantmRNA\') = degradation_constant_mRNA;"
  buffer *= "\n\tdataDictionary(\'degradationConstantProtein\') = degradation_constant_protein;"
  buffer *= "\n\tdataDictionary(\'kcatTranscription\') = kcat_transcription;"
  buffer *= "\n\tdataDictionary(\'kcatTranslation\') = kcat_translation;"
  buffer *= "\n\tdataDictionary(\'avgGeneConcentration\') = avg_gene_concentration;"
  buffer *= "\n\tdataDictionary(\'transcriptionSaturationConstant\') = saturation_transcription;"
  buffer *= "\n\tdataDictionary(\'translationSaturationConstant\') = saturation_translation;"
  buffer *= "\n\tdataDictionary(\'specificGrowthRate\') = maximum_specific_growth_rate - death_rate_constant;"

  # buffer *= "\n\n\treturn dataDictionary"
  buffer *= "\n\nend"

  return buffer
end

function m_build_simulation_buffer(NoExtracellularSpecies::Int64)
  # buffer = build_copyright_header_buffer()
  buffer = "\n% set up ODE, get the derivatives
function dydt = Balances(t,y,dataDictionary)
  % correct for negatives
  y(find(y < 0)) = 0.0;

  % load data from data dictionary
  rnx_species = dataDictionary(\'rnx_species_array\');
  mRNA_species = dataDictionary(\'mRNA_species_array\');
  protein_species = dataDictionary(\'protein_species_array\');
  stoichiometry = dataDictionary(\'stoichiometric_matrix\');
  all_species_dict = dataDictionary(\'all_species_dict\');  % String --> number
  specific_growth_rate = dataDictionary(\'specificGrowthRate\');
  transcription_correction = dataDictionary(\'transcriptionSpecificCorrectionFactorDict\');
  translation_correction = dataDictionary(\'translationSpecificCorrectionFactor\');
  degradation_constant_mRNA = dataDictionary(\'degradationConstantmRNA\');
  degradation_constant_protein = dataDictionary(\'degradationConstantProtein\');

  % kinetics
  [rnx_rate, transcription_rate, translation_rate] = Kinetics(y, dataDictionary);

  % calculate the derivatives
  dydt = zeros(1, length(all_species_dict));

  % signaling reaction network
  X2 = zeros(1, (length(rnx_species)-$(NoExtracellularSpecies)));  % species inside the cell
  for i = $(NoExtracellularSpecies+1):length(rnx_species)  % get X2 value from y
    X2(i - $(NoExtracellularSpecies)) = y(all_species_dict(char(rnx_species(i))));
  end
  dX1 = stoichiometry(1:$(NoExtracellularSpecies), :) * rnx_rate;  % extracellular metabolites
  dX2 = stoichiometry($(NoExtracellularSpecies)+1:end, :)*rnx_rate - transpose(specific_growth_rate*X2);  % INTRA
  dX = [dX1; dX2];
  for i = 1:length(rnx_species)  % return derivatives back to dydt
    dydt(all_species_dict(char(rnx_species(i)))) = dX(i);
  end
  dydt(1) = specific_growth_rate * y(1);  % for Biomass

  % TXTL network
  for i = 1:length(mRNA_species)
  % seems easy to add additional terms to account for the signaling effect
    mRNA_id = all_species_dict(char(mRNA_species(i)));
    p_id = all_species_dict(char(protein_species(i)));
    dydt(mRNA_id) = transcription_rate(char(mRNA_species(i))) - (specific_growth_rate + transcription_correction(char(mRNA_species(i)))*degradation_constant_mRNA) * y(mRNA_id);
    dydt(p_id) = translation_rate(char(protein_species(i))) - (specific_growth_rate + translation_correction(char(protein_species(i)))*degradation_constant_protein) * y(p_id);
  end
  dydt = dydt';
end"
  return buffer
end

function m_build_solveODEBalances_buffer(all_species_array::Array,
  all_species_dict::Dict{String, Int}, mRNA_species::Array, protein_species::Array)

  buffer = "function [t, y] = SolveBalances(TStart, TStop, TStep, dataDictionary)" *
    "\n\tt = TStart:TStep:TStop;"
  buffer *= "\n\tall_species_reversed_dict = dataDictionary(\'all_species_reversed_dict\');"
  # initial conditions
  buffer *= "\n\t% initial species concentration"
  buffer *= "\n\ty0 = dataDictionary(\'initial_condition\');"
  # run simulation
  buffer *= "\n\n\t[t, y] = ode23s(@(t,y) Balances(t,y,dataDictionary), t, y0);"
  # plotting
  buffer *= "\n\n\t% plotting results" *
    "\n\tsubplt_col = ceil(length(y0)/4);" *
    "\n\tfor i = 1:length(y0)" *
    "\n\t\tsubplot(4, subplt_col, i)" *
    "\n\t\tplot(t, y(:, i))" *
    "\n\t\ttitle(char(all_species_reversed_dict(i)), 'Interpreter', 'none')" *
    "\n\tend"
  buffer *= "\nend"

  return buffer
end

# FBA data dictionary generation
function m_generate_FBA_data_dictionary(all_rnx_list::Array,
  rnx_species_array::Array, extra_species_num::Int)
  secrete_id = Set{Int64}()  # For initialize the coefficient array
  # data dictionary
  buffer = "function data_dictionary = DataDictionary(time_start,time_stop,time_step)"
  # stoichiometry
  buffer *= "\n\n\t% load stoichiometry"
  buffer *= "\n\tstoichiometric_matrix = load(\'stoichiometry.dat\');"
  # Setup default flux bounds array
  buffer *= "\n\t% Setup default flux bounds array"
  buffer *= "\n\tdefault_bounds_array = ["
  for (id, rnx) in enumerate(all_rnx_list)
    buffer *= "\n\t\t0 100.0; % $(id) $(rnx.rnxName)"
  end
  buffer *= "\n\t];"
  # Setup default species bounds array
  buffer *= "\n\n\t% Setup default species bounds array"
  buffer *= "\n\tspecies_bounds_array = ["
  for (id, sp) in enumerate(rnx_species_array)
    if id > extra_species_num
      buffer *= "\n\t\t0.0 0.0;  % $(id) $(sp)"
    else
      buffer *= "\n\t\t-1.0 1.0; % $(id) $(sp)"
    end
  end
  buffer *= "\n\t];"
  # Setup the objective coefficient array
  buffer *= "\n\n\t% Setup the objective coefficient array"
  buffer *= "\n\tobjective_coefficient_array = ["
  for (id, rnx) in enumerate(all_rnx_list)
    buffer *= "\n\t\t0.0, ... % $(id) $(rnx.rnxName)"
    if rnx.rnxType == "secrete"
        push!(secrete_id, id)
    end
  end
  buffer *= "\n\t];"
  # List of reation strings - used to write flux report
  buffer *= "\n\n\t% List of reation strings - used to write flux report"
  buffer *= "\n\tlist_of_reaction_strings = {..."
  for rnx in all_rnx_list
    buffer *= "\n\t\t\'$(rnx.rnxName)\', ..."
  end
  buffer *= "\n\t};"
  # List of metabolite strings - used to write flux report
  buffer *= "\n\n\t% List of metabolite strings - used to write flux report"
  buffer *= "\n\tlist_of_metabolite_symbols = [..."
  for sp in rnx_species_array
    buffer *= "\n\t\t\'$(sp)\'..."
  end
  buffer *= "\n\t];"
  # return block -
  buffer *= "\n\n"
  buffer *= "\t% =============================== DO NOT EDIT BELOW THIS LINE ============================== #\n"
  buffer *= "\tdata_dictionary = containers.Map();\n"
  buffer *= "\tdata_dictionary(\'stoichiometric_matrix\') = stoichiometric_matrix;\n"
  buffer *= "\tdata_dictionary(\'objective_coefficient_array\') = objective_coefficient_array;\n"
  buffer *= "\tdata_dictionary(\'default_flux_bounds_array\') = default_bounds_array;\n"
  buffer *= "\tdata_dictionary(\'species_bounds_array\') = species_bounds_array;\n"
  buffer *= "\tdata_dictionary(\'list_of_reaction_strings\') = list_of_reaction_strings;\n"
  buffer *= "\tdata_dictionary(\'list_of_metabolite_symbols\') = list_of_metabolite_symbols;\n"
  buffer *= "\tdata_dictionary(\'extra_species_num\') = $(extra_species_num);\n"
  buffer *= "\t% =============================== DO NOT EDIT ABOVE THIS LINE ============================== #\n"
  buffer *= "end\n"

  # maximize_product_dictionary
  buffer2 = "% maximize_product_dictionary"
  buffer2 *= "\nfunction dataDict = maximizeProductDictionary(time_start,time_stop,time_step)"
  # load the data dictionary -
  buffer2 *= "\n\t% load the data dictionary -"
  buffer2 *= "\n\tdataDict = DataDictionary(time_start,time_stop,time_step);"
  # Modify the data dictionary -
  buffer2 *= "\n\t% Modify the data dictionary -"
  buffer2 *= "\n\tobjective_coefficient_array = dataDict(\'objective_coefficient_array\');"
  secrete_id = sort(collect(secrete_id))
  for id in secrete_id
      buffer2 *= "\n\tobjective_coefficient_array($(id)) = -1;"
  end
  buffer2 *= "\n\tdataDict(\'objective_coefficient_array\') = objective_coefficient_array;"
  buffer2 *= "\nend"

  return buffer, buffer2
end


mutable struct QRS{T} where T
    P::T
    Q::T
    S::T
    T::T
    form
    noiseLvl::Float32
    validity::Float32
    params::Tuple{Any}
    actions::Tuple{Any}
end
function changeQ(QRS)
end

function changeS(QRS)
end

function newQRS(QRS)
end


module DICOMUtils

using DICOM
using LinearAlgebra

include("./load.jl")
include("./orientation.jl")
include("./sort.jl")

export
    # Export load.jl functions
    load_dcm_array,
    get_pixel_size,

    # Export orientation.jl functions
    get_affine,
    io_orientation,
    axcodes2ornt,
    ornt2axcodes,
    ornt_transform,
    apply_orientation,
    inv_ornt_aff,

    # Export sort.jl functions
    sortbytag_copy,
    sortbytag_move

end


unique_frame_name(frame::CartesianFrame3D) =
    Symbol("$(frame)_(#$(frame.id))")

function setgeometry!(vis::Visualizer, mechanism::Mechanism, source::AbstractGeometrySource=Skeleton(false, true))
    elements = visual_elements(mechanism, source)
    setgeometry!(vis, mechanism, elements)
end

function setgeometry!(vis::Visualizer, mechanism::Mechanism, elements::AbstractVector{<:VisualElement})
    batch(vis) do v
        delete!(v)
        addgeometry!(vis, mechanism, elements)
    end
    nothing
end

apply_scaling(geometry::HyperSphere, scale::Vec) = HyperEllipsoid(center(geometry), scale)
apply_scaling(geometry, scale) = error("Geometry $geometry has a non-unit scale: $scale, but I don't know how to transform it into a scaled geometry automatically.")

"""
The remote tree viewer interface has no notion of scale
(only a quaternion and translation), so we have to do some
monkey business to push the scaling into the geometry itself.

Currently, this only works for converting spheres into ellipsoids
and assumes the scaling is positive.
"""
function remove_scaling(geometry, transform)
    H = [transform_deriv(transform, Vec(0., 0, 0)) transform(Vec(0., 0, 0));
    Vec(0, 0, 0, 1)']
    scale = Vec(norm(H[1:3, 1]), norm(H[1:3, 2]), norm(H[1:3, 3]))
    if norm(scale - Vec(1, 1, 1)) > 1e-3
        H[:,1] ./= scale[1]
        H[:,2] ./= scale[2]
        H[:,3] ./= scale[3]
        tform = AffineMap(H[1:3, 1:3], transform(Vec(0., 0, 0)))
        return apply_scaling(geometry, scale), tform
    else
        return geometry, transform
    end
end

function addgeometry!(vis::Visualizer, mechanism::Mechanism, elements::AbstractVector{<:VisualElement})
    frame_to_elements = Dict{CartesianFrame3D, Vector{VisualElement}}()
    for element in elements
        push!(get!(() -> VisualElement[], frame_to_elements, element.frame), element)
    end
    body_names = Set{Symbol}()
    batch(vis) do v
        for body in bodies(mechanism)
            body_name = Symbol(body)
            if body_name in body_names
                error("Duplicate body name in mechanism")
            end
            push!(body_names, body_name)
            bodyvis = v[body_name]
            found_geometry = false
            for transform in rbd.frame_definitions(body)
                frame = transform.from
                framename = unique_frame_name(frame)
                if haskey(frame_to_elements, frame)
                    found_geometry = true
                    for element in frame_to_elements[frame]
                        geometry, transform = remove_scaling(element.geometry, element.transform)
                        geomdata = GeometryData(geometry, element.color, transform)
                        addgeometry!(bodyvis[framename], geomdata)
                    end
                    settransform!(bodyvis[framename], rbd.frame_definition(body, frame))
                end
            end
            if !found_geometry
                setgeometry!(bodyvis, [])
            end
        end
    end
    nothing
end

function addgeometry!(vis::Visualizer, mechanism::Mechanism, frame::CartesianFrame3D, geometry::Union{AbstractGeometry, AbstractMesh})
    addgeometry!(vis, mechanism, [VisualElement(frame, geometry, RGBA{Float32}(1, 0, 0, 0.5), IdentityTransformation())])
end

function addgeometry!(vis::Visualizer, mechanism::Mechanism, frame::CartesianFrame3D; scale=1.0)
    addgeometry!(vis, mechanism, frame, Triad(scale, false))
end

function addgeometry!(vis::Visualizer, mechanism::Mechanism, point::Point3D; radius=0.03)
    addgeometry!(vis, mechanism, point.frame, HyperSphere(Point{3, Float64}(point.v), radius))
end

function addgeometry!(vis::Visualizer, mechanism::Mechanism, frame::CartesianFrame3D, geomdata::GeometryData)
    addgeometry!(vis, mechanism, [VisualElement(frame, geomdata.geometry, convert(RGBA{Float32}, geomdata.color), geomdata.transform)])
end

settransform!(vis::Visualizer, tform::rbd.Transform3D) = settransform!(vis, convert(AffineMap, tform))

function settransform!(vis::Visualizer, state::MechanismState)
    batch(vis) do v
        for body in bodies(state.mechanism)
            body_name = Symbol(body)
            if body_name in keys(v.core.tree[v.path].children)
                settransform!(v[body_name], transform_to_root(state, body))
            end
        end
    end
end

"""
Construct a DrakeVisualizer.Visualizer for the given mechanism by constructing
simple geometries just from the structure of the kinematic tree. If
`show_intertias` is true, then also construct equivalent inertial ellipsoids
for every link.
"""
function Visualizer(mechanism::Mechanism, prefix=[:robot1];
                    show_inertias::Bool=false, randomize_colors::Bool=true)
    source = Skeleton(inertias=show_inertias, randomize_colors=randomize_colors)
    Visualizer(mechanism, source, prefix)
end

function Visualizer(mechanism::Mechanism, elements::AbstractVector{<:VisualElement},
                    prefix=[:robot1])
    vis = Visualizer()[prefix]
    setgeometry!(vis, mechanism, elements)
    vis
end

function Visualizer(mechanism::Mechanism, source::AbstractGeometrySource, prefix=[:robot1])
    elements = visual_elements(mechanism, source)
    vis = Visualizer()[prefix]
    setgeometry!(vis, mechanism, elements)
    vis
end

convert(::Type{AffineMap}, T::Transform3D) =
    AffineMap(rotation(T), translation(T))

inspect!(state::MechanismState, vis::Visualizer) = manipulate!(state) do state
    settransform!(vis, state)
end

function inspect!(state::MechanismState;
        show_inertias::Bool=false, randomize_colors::Bool=true)
    vis = Visualizer()[:robot1]
    elements = visual_elements(state.mechanism, Skeleton(show_inertias, randomize_colors))
    setgeometry!(vis, state.mechanism, elements)
    inspect!(state, vis)
end

inspect(mechanism::Mechanism, args...; kwargs...) =
    inspect!(MechanismState{Float64}(mechanism), args...; kwargs...)


immutable StaticBitSet
    chunks::NTuple{4, UInt64}
end

@inline _div64(l) = l >>> 6
@inline _mod64(l) = l & 63
@inline get_chunks_id(i::Integer) = _div64(Int(i)-1)+1, _mod64(Int(i)-1)

@inline function Base.in(x::UInt8, b::StaticBitSet)
    i1, i2 = get_chunks_id(x+1)
    (b.chunks[i1] & (UInt64(1) << i2)) != 0
end

function emit_level(leaf::IOTreeLeaf, args...)
    :(return $(leaf.value))
end

function emit_level(tree::Union{IOTreeRoot, IOTreeNode}, emasks, mask_idx, byte_reader, level)
    exprs = Expr(:block)
    b = gensym()
    push!(exprs.args, :($b = $(byte_reader(level))))
    cs = children(tree)
    accum_mask = copy(zeros_mask)
    for (i,c) in enumerate(cs)
        accum_mask .|= c.mask
        sm = StaticBitSet(tuple(c.mask.mask.chunks...))
        if i < length(cs)
          idx = mask_idx[sm]
          push!(exprs.args, quote
              if $b in $emasks[$idx]
                  $(emit_level(c, emasks, mask_idx, byte_reader, level+1))
              end
          end)
        else
          push!(exprs.args, emit_level(c, emasks, mask_idx, byte_reader, level+1))
        end
    end
    # Make sure the masks where exhaustive
    @assert accum_mask == ones_mask
    exprs
end

function generate_decoder_for_tree(tree, byte_reader)
    masks = Set{StaticBitSet}()
    for node in PreOrderDFS(tree)
        (node == tree) && continue
        push!(masks, StaticBitSet(tuple(node.mask.mask.chunks...)))
    end
    masks = tuple(collect(masks)...)
    mask_idx = Dict(x=>i for (i,x) in enumerate(masks))
    ret = Expr(:block)
    emasks = gensym()
    push!(ret.args, :($emasks = $masks))
    push!(ret.args, emit_level(tree, emasks, mask_idx, byte_reader, 1))
    ret
end


using Parameters, Compat

@with_kw mutable struct lsoda_common_t
    yh::Ptr{Ptr{Cdouble}}= C_NULL
    wm::Ptr{Ptr{Cdouble}}= C_NULL
    ewt::Ptr{Cdouble}= C_NULL
    savf::Ptr{Cdouble}= C_NULL
    acor::Ptr{Cdouble}= C_NULL
    ipvt::Ptr{Cint}= C_NULL
    memory::Ptr{Nothing}= C_NULL
    h::Cdouble = 0.
    hu::Cdouble = 0.
    rc::Cdouble = 0.
    tn::Cdouble = 0.
    tsw::Cdouble = 0.
    pdnorm::Cdouble = 0.
    crate::Cdouble = 0.
    el::NTuple{14,Cdouble} = ntuple(x->Cdouble(0), 14)
    elco::NTuple{13,NTuple{14,Cdouble}} = ntuple(x->ntuple(x->Cdouble(0), 14),13)
    tesco::NTuple{13,NTuple{4,Cdouble}} = ntuple(x->ntuple(x->Cdouble(0), 4),13)
    hold::Cdouble = 0.
    rmax::Cdouble = 0.
    pdest::Cdouble = 0.
    pdlast::Cdouble = 0.
    ialth::Cint = 0
    ipup::Cint = 0
    nslp::Cint = 0
    icount::Cint = 0
    irflag::Cint = 0
    imxer::Cint = 0
    illin::Cint = 0
    nhnil::Cint = 0
    nslast::Cint = 0
    jcur::Cint = 0
    meth::Cint = 0
    mused::Cint = 0
    nq::Cint = 0
    nst::Cint = 0
    ncf::Cint = 0
    nfe::Cint = 0
    nje::Cint = 0
    nqu::Cint = 0
    miter::Cint = 0
end

@with_kw mutable struct lsoda_opt_t
	  ixpr::Cint = 0
    mxstep::Cint = 0
    mxhnil::Cint = 0
    mxordn::Cint = 0
    mxords::Cint = 0
    tcrit::Cdouble = 0.
    h0::Cdouble = 0.
    hmax::Cdouble = 0.
    hmin::Cdouble = 0.
    hmxi::Cdouble = 0.
    itask::Cint = 0
    rtol::Ptr{Cdouble} = C_NULL
    atol::Ptr{Cdouble} = C_NULL
end

const _lsoda_f = Ptr{Nothing}

@with_kw mutable struct lsoda_context_t
    function_::_lsoda_f = C_NULL
    data::Ptr{Nothing} = C_NULL ##
    neq::Cint = 0
    state::Cint = 0
    error::Cstring = C_NULL
    common::Ptr{lsoda_common_t} = C_NULL
    opt::Ptr{lsoda_opt_t} = C_NULL
end

const lsoda_context_t_ptr = Ptr{lsoda_context_t}

mutable struct UserFunctionAndData
    func::Function
    data::Any
    neq::Cint
    UserFunctionAndData(func::Function, data::Any, neq::Cint) = new(func, data, neq)
end

# UserFunctionAndData(func::Function) = func
# UserFunctionAndData(func::Function, data::Nothing) = func
# UserFunctionAndData(func::Function, data::Nothing, neq::Cint) = func

function lsoda_prepare(ctx::lsoda_context_t,opt::lsoda_opt_t)
  return ccall((:lsoda_prepare,liblsoda),Cint,
    (Ptr{lsoda_context_t},Ptr{lsoda_opt_t}),
    Ref(ctx),Ref(opt))
end

function lsoda(ctx::lsoda_context_t,y::Vector,t::Vector{Float64},tout)
  return ccall((:lsoda,liblsoda),Cint,
    (Ptr{lsoda_context_t},Ptr{Cdouble},Ptr{Cdouble},Cdouble),
    Ref(ctx),y,t,tout[1])
end

function lsoda_reset(ctx::lsoda_context_t)
	ccall((:lsoda_reset,liblsoda),Nothing,(Ptr{lsoda_context_t},),Ref(ctx))
end

# written to wrap lsoda_free from C library but never used in practise as
# lsoda_context_t variables are handled on Julia's side
function lsoda_free(ctx::lsoda_context_t)
		ccall((:lsoda_free,liblsoda),Nothing,(Ref{lsoda_context_t},),Ref(ctx))
    nothing
end

function lsoda_free(ctx::Ref{lsoda_context_t})
		ccall((:lsoda_free,liblsoda),Nothing,(Ref{lsoda_context_t},),(ctx))
    nothing
end


module ErlangTerm

export serialize, deserialize

const NEW_FLOAT = UInt8(70)
const SMALL_INTEGER = UInt8(97)
const INTEGER = UInt8(98)
const ATOM = UInt8(100)
const SMALL_TUPLE = UInt8(104)
const LARGE_TUPLE = UInt8(105)
const NIL = UInt8(106)
const STRING = UInt8(107)
const LIST = UInt8(108)
const BINARY = UInt8(109)
const SMALL_BIG = UInt8(110)
const LARGE_BIG = UInt8(111)
const MAP = UInt8(116)
const ATOM_UTF8 = UInt8(118)
const SMALL_ATOM_UTF8 = UInt8(119)

const VERSION = UInt8(131)

"""
    deserialize(io::IO)
    deserialize(binary::Array{UInt8})

Deserialize a Julia value encoded in the
[Erlang external term format](http://erlang.org/doc/apps/erts/erl_ext_dist.html)
from a stream or an array of bytes.
"""
deserialize

"""
    serialize(io::IO, value)
    serialize(value)

Serialize a Julia value to the
[Erlang external term format](http://erlang.org/doc/apps/erts/erl_ext_dist.html).
"""
serialize

function deserialize(io::IO)
    version = read(io, UInt8)
    version != VERSION && throw(ArgumentError("Unknown external term format version: $(Int(version))."))

    tag = read(io, UInt8)
    deserialize(io, Val(tag))
end

deserialize(binary::Array{UInt8}) = deserialize(IOBuffer(binary))

function serialize(io::IO, data)
    write(io, VERSION)
    _serialize(io, data)
end

serialize(data) = take!(serialize(IOBuffer(), data))

function _serialize(io, val::Integer)
    if val > 0 && val <= typemax(UInt8)
        write(io, SMALL_INTEGER)
        write(io, UInt8(val))
    elseif abs(val) <= typemax(Int32)
        write(io, INTEGER)
        write(io, hton(Int32(val)))
    else
        arr = digits(UInt8, abs(val), base=0x100)
        n = length(arr)
        if n < 0x100
            write(io, SMALL_BIG)
            write(io, UInt8(n))
        else
            write(io, LARGE_BIG)
            write(io, hton(UInt32(n)))
        end
        write(io, UInt8(sign(val) == 1 ? 0 : 1))
        write(io, arr)
    end
    io
end

function deserialize(io, ::Val{SMALL_INTEGER})
    Int(read(io, UInt8))
end

function deserialize(io, ::Val{INTEGER})
    ntoh(read(io, Int32))
end

function _serialize(io, val::Float64)
    write(io, NEW_FLOAT)
    write(io, hton(val))
    io
end

function deserialize(io, ::Val{NEW_FLOAT})
    ntoh(read(io, Float64))
end

function _serialize(io, val::Symbol)
    str = Array{UInt8}(string(val))
    n = length(str)
    if n > typemax(UInt16)
        throw(ArgumentError("Cannot serialize symbols with more than $(typemax(UInt16)) characters."))
    elseif n > typemax(UInt8)
        write(io, ATOM_UTF8)
        write(io, hton(UInt16(n)))
    else
        write(io, SMALL_ATOM_UTF8)
        write(io, hton(UInt8(n)))
    end
    write(io, str)
    io
end

function deserialize(io, ::Val{ATOM})
    n = Int(ntoh(read(io, UInt16)))
    Symbol(String(read(io, n)))
end

function deserialize(io, ::Val{ATOM_UTF8})
    n = Int(ntoh(read(io, UInt16)))
    Symbol(String(read(io, n)))
end

function deserialize(io, ::Val{SMALL_ATOM_UTF8})
    n = Int(ntoh(read(io, UInt8)))
    Symbol(String(read(io, n)))
end

function _serialize(io, val::String)
    write(io, BINARY)
    str = Array{UInt8}(val)
    n = length(str)
    n > typemax(UInt32) && throw(ArgumentError("Strings longer than $(typemax(UInt32)) cannot be serialized."))
    write(io, hton(UInt32(n)))
    write(io, str)
    io
end

function deserialize(io, ::Val{BINARY})
    n = Int(ntoh(read(io, UInt32)))
    String(read(io, n))
end

function deserialize(io, ::Val{SMALL_BIG})
    n = Int(read(io, UInt8))
    sign = Int32(read(io, UInt8) == 0 ? 1 : -1)
    arr = Array{UInt8,1}(read(io, n))
    _bignum(n, sign, arr)
end

function deserialize(io, ::Val{LARGE_BIG})
    n = Int(ntoh(read(io, UInt32)))
    sign = Int32(read(io, UInt8) == 0 ? 1 : -1)
    arr = Array{UInt8,1}(read(io, n))
    _bignum(n, sign, arr)
end

function _bignum(n, sign, arr)
    itype = n < 4 ? Int32 :
            n < 8 ? Int64 :
            n < 16 ? Int128 : BigInt
    sign * dig2int(itype, arr)
end

dig2int(arr) = sum(((i, x),) -> x * 256^(i - 1), pairs(arr))
dig2int(::Type{Int32}, arr) = Int32(dig2int(arr))
dig2int(::Type{Int64}, arr) = sum(((i, x),) -> x * Int64(256)^(i - 1), pairs(arr))
dig2int(::Type{Int128}, arr) = sum(((i, x),) -> x * Int128(256)^(i - 1), pairs(arr))
dig2int(::Type{BigInt}, arr) = sum(((i, x),) -> x * BigInt(256)^(i - 1), pairs(arr))

function _serialize(io, array::AbstractArray)
    if isempty(array)
        write(io, NIL)
        return io
    end

    write(io, LIST)
    n = length(array)
    n > typemax(UInt32) && throw(ArgumentError("Arrays longer than $(typemax(UInt32)) cannot be serialized."))
    write(io, hton(UInt32(n)))
    for el in array
        _serialize(io, el)
    end
    write(io, NIL)
    io
end

function deserialize(io, ::Val{NIL})
    []
end

function deserialize(io, ::Val{LIST})
    n = Int(ntoh(read(io, UInt32)))
    i = 0
    array = []
    while i < n
        tag = read(io, UInt8)
        push!(array, deserialize(io, Val(tag)))
        i += 1
    end
    tail = read(io, UInt8)
    tail != NIL && throw(ArgumentError("Improper lists are not supported."))
    array
end

function _serialize(io, dict::Dict)
    write(io, MAP)
    n = length(dict)
    n > typemax(UInt32) && throw(ArgumentError("Dicts with more than $(typemax(UInt32)) pairs cannot be serialized."))
    write(io, hton(UInt32(n)))
    for (key, value) in dict
        _serialize(io, key)
        _serialize(io, value)
    end
    io
end

function deserialize(io, ::Val{MAP})
    n = Int(ntoh(read(io, UInt32)))
    i = 0
    dict = Dict()
    while i < n
        keytag = read(io, UInt8)
        key = deserialize(io, Val(keytag))
        valuetag = read(io, UInt8)
        value = deserialize(io, Val(valuetag))
        push!(dict, key=>value)
        i += 1
    end
    dict
end

function _serialize(io, val::Tuple)
    n = length(val)
    if n > typemax(UInt32)
        throw(ArgumentError("Tuples longer than $(typemax(UInt32)) cannot be serialized."))
    elseif n > typemax(UInt8)
        write(io, LARGE_TUPLE)
        write(io, hton(UInt32(n)))
    else
        write(io, SMALL_TUPLE)
        write(io, hton(UInt8(n)))
    end
    for el in val
        _serialize(io, el)
    end
    io
end

function deserialize(io, ::Val{SMALL_TUPLE})
    n = Int(ntoh(read(io, UInt8)))
    i = 0
    array = []
    while i < n
        tag = read(io, UInt8)
        push!(array, deserialize(io, Val(tag)))
        i += 1
    end
    Tuple(array)
end

function deserialize(io, ::Val{LARGE_TUPLE})
    n = Int(ntoh(read(io, UInt32)))
    i = 0
    array = []
    while i < n
        tag = read(io, UInt8)
        push!(array, deserialize(io, Val(tag)))
        i += 1
    end
    Tuple(array)
end

function deserialize(io, ::Val{STRING})
    n = Int(ntoh(read(io, UInt16)))
    i = 0
    array = []
    while i < n
        push!(array, ntoh(read(io, UInt8)))
        i += 1
    end
    array
end

function _serialize(io, ::Nothing)
    _serialize(io, :nil)
    io
end

function _serialize(io, unknown)
    _serialize(io, repr(unknown))
end

end # module


__precompile__()
"""
SU_mod

Implements seismic un*x (SU)  format
and I/O of SU files

"""
module SU_mod

export SU_hdr, SU_trc, SUPutTrace, SUGetTrace

mutable struct SU_hdr
        tracl::Int32                                       #  1-4
        tracr::Int32                                       #  5-8
        fldr::Int32                                        #  9-12
        tracf::Int32                                       # 13-16
        ep::Int32                                          # 17-20
        cdp::Int32                                         # 21-24
        cdpt::Int32                                        # 25-28
        trid::Int16                                        # 29-30
        nvs::Int16                                         # 31-32
        nhs::Int16                                         # 33-34
        duse::Int16                                        # 35-36
        offset::Int32                                      # 37-40
        gelev::Int32                                       # 41-44
        selev::Int32                                       # 45-48
        sdepth::Int32                                      # 49-52
        gdel::Int32                                        # 53-56
        sdel::Int32                                        # 57-60
        swdep::Int32                                       # 61-64
        gwdep::Int32                                       # 65-68
        scalel::Int16                                      # 69-70
        scalco::Int16                                      # 71-72
        sx::Int32                                          # 73-76
        sy::Int32                                          # 77-80
        gx::Int32                                          # 81-84
        gy::Int32                                          # 85-88
        counit::Int16                                      # 89-90
        wevel::Int16                                       # 91-92
        swevel::Int16                                      # 93-94
        sut::Int16                                         # 95-96
        gut::Int16                                         # 97-98
        sstat::Int16                                       # 99-100
        gstat::Int16                                       #101-102
        tstat::Int16                                       #103-104
        laga::Int16                                        #105-106
        lagb::Int16                                        #107-108
        delrt::Int16                                       #109-110
        muts::Int16                                        #111-112
        mute::Int16                                        #113-114
        ns::Int16                                          #115-116
        dt::Int16                                          #117-118
        gain::Int16                                        #119-120
        igc::Int16                                         #121-122
        igi::Int16                                         #123-124
        corr::Int16                                        #125-126
        sfs::Int16                                         #127-128
        sfe::Int16                                         #129-130
        slen::Int16                                        #131-132
        styp::Int16                                        #133-134
        stas::Int16                                        #135-136
        stae::Int16                                        #137-138
        tatyp::Int16                                       #139-140
        afilf::Int16                                       #141-142
        afils::Int16                                       #143-144
        nofilf::Int16                                      #145-146
        nofils::Int16                                      #147-148
        lcf::Int16                                         #149-150
        hcf::Int16                                         #151-152
        lcs::Int16                                         #153-154
        hcs::Int16                                         #155-156
        year::Int16                                        #157-158
        day::Int16                                         #159-160
        hour::Int16                                        #161-162
        minute::Int16                                      #163-164
        sec::Int16                                         #165-166
        timbas::Int16                                      #167-168
        trwf::Int16                                        #169-170
        grnors::Int16                                      #171-172
        grnofr::Int16                                      #173-174
        grnlof::Int16                                      #175-176
        gaps::Int16                                        #177-178
        otrav::Int16                                       #179-180
        d1::Float32                                        #181-184
        f1::Float32                                        #185-188
        d2::Float32                                        #189-192
        f2::Float32                                        #193-196
        ungpow::Float32                                    #197-200
        unscale::Float32                                   #201-204
        ntr::Int32                                         #205-208
        mark::Int16                                        #209-210
        shortpad::Int16                                    #211-212
        unassigned::Array{Int16,1}
        #
        function SU_hdr(
                tracl=Int32(0),                                       #  1-4
                tracr=Int32(0),                                       #  5-8
                fldr=Int32(0),                                        #  9-12
                tracf=Int32(0),                                       # 13-16
                ep=Int32(0),                                          # 17-20
                cdp=Int32(0),                                         # 21-24
                cdpt=Int32(0),                                        # 25-28
                trid=Int16(0),                                        # 29-30
                nvs=Int16(0),                                         # 31-32
                nhs=Int16(0),                                         # 33-34
                duse=Int16(0),                                        # 35-36
                offset=Int32(0),                                      # 37-40
                gelev=Int32(0),                                       # 41-44
                selev=Int32(0),                                       # 45-48
                sdepth=Int32(0),                                      # 49-52
                gdel=Int32(0),                                        # 53-56
                sdel=Int32(0),                                        # 57-60
                swdep=Int32(0),                                       # 61-64
                gwdep=Int32(0),                                       # 65-68
                scalel=Int16(0),                                      # 69-70
                scalco=Int16(0),                                      # 71-72
                sx=Int32(0),                                          # 73-76
                sy=Int32(0),                                          # 77-80
                gx=Int32(0),                                          # 81-84
                gy=Int32(0),                                          # 85-88
                counit=Int16(0),                                      # 89-90
                wevel=Int16(0),                                       # 91-92
                swevel=Int16(0),                                      # 93-94
                sut=Int16(0),                                         # 95-96
                gut=Int16(0),                                         # 97-98
                sstat=Int16(0),                                       # 99-100
                gstat=Int16(0),                                       #101-102
                tstat=Int16(0),                                       #103-104
                laga=Int16(0),                                        #105-106
                lagb=Int16(0),                                        #107-108
                delrt=Int16(0),                                       #109-110
                muts=Int16(0),                                        #111-112
                mute=Int16(0),                                        #113-114
                ns=Int16(0),                                          #115-116
                dt=Int16(0),                                          #117-118
                gain=Int16(0),                                        #119-120
                igc=Int16(0),                                         #121-122
                igi=Int16(0),                                         #123-124
                corr=Int16(0),                                        #125-126
                sfs=Int16(0),                                         #127-128
                sfe=Int16(0),                                         #129-130
                slen=Int16(0),                                        #131-132
                styp=Int16(0),                                        #133-134
                stas=Int16(0),                                        #135-136
                stae=Int16(0),                                        #137-138
                tatyp=Int16(0),                                       #139-140
                afilf=Int16(0),                                       #141-142
                afils=Int16(0),                                       #143-144
                nofilf=Int16(0),                                      #145-146
                nofils=Int16(0),                                      #147-148
                lcf=Int16(0),                                         #149-150
                hcf=Int16(0),                                         #151-152
                lcs=Int16(0),                                         #153-154
                hcs=Int16(0),                                         #155-156
                year=Int16(0),                                        #157-158
                day=Int16(0),                                         #159-160
                hour=Int16(0),                                        #161-162
                minute=Int16(0),                                      #163-164
                sec=Int16(0),                                         #165-166
                timbas=Int16(0),                                      #167-168
                trwf=Int16(0),                                        #169-170
                grnors=Int16(0),                                      #171-172
                grnofr=Int16(0),                                      #173-174
                grnlof=Int16(0),                                      #175-176
                gaps=Int16(0),                                        #177-178
                otrav=Int16(0),                                       #179-180
                d1=Float32(0.0),                                      #181-184
                f1=Float32(0.0),                                      #185-188
                d2=Float32(0.0),                                      #189-192
                f2=Float32(0.0),                                      #193-196
                ungpow=Float32(0.0),                                  #197-200
                unscale=Float32(0.0),                                 #201-204
                ntr=Int32(0),                                         #205-208
                mark=Int16(0),                                        #209-210
                shortpad=Int16(0),                                    #211-212
                unassigned=zeros(Int16,14))                           #213-240
                new(
                    tracl,                                       #  1-4
                    tracr,                                       #  5-8
                    fldr,                                        #  9-12
                    tracf,                                       # 13-16
                    ep,                                          # 17-20
                    cdp,                                         # 21-24
                    cdpt,                                        # 25-28
                    trid,                                        # 29-30
                    nvs,                                         # 31-32
                    nhs,                                         # 33-34
                    duse,                                        # 35-36
                    offset,                                      # 37-40
                    gelev,                                       # 41-44
                    selev,                                       # 45-48
                    sdepth,                                      # 49-52
                    gdel,                                        # 53-56
                    sdel,                                        # 57-60
                    swdep,                                       # 61-64
                    gwdep,                                       # 65-68
                    scalel,                                      # 69-70
                    scalco,                                      # 71-72
                    sx,                                          # 73-76
                    sy,                                          # 77-80
                    gx,                                          # 81-84
                    gy,                                          # 85-88
                    counit,                                      # 89-90
                    wevel,                                       # 91-92
                    swevel,                                      # 93-94
                    sut,                                         # 95-96
                    gut,                                         # 97-98
                    sstat,                                       # 99-100
                    gstat,                                       #101-102
                    tstat,                                       #103-104
                    laga,                                        #105-106
                    lagb,                                        #107-108
                    delrt,                                       #109-110
                    muts,                                        #111-112
                    mute,                                        #113-114
                    ns,                                          #115-116
                    dt,                                          #117-118
                    gain,                                        #119-120
                    igc,                                         #121-122
                    igi,                                         #123-124
                    corr,                                        #125-126
                    sfs,                                         #127-128
                    sfe,                                         #129-130
                    slen,                                        #131-132
                    styp,                                        #133-134
                    stas,                                        #135-136
                    stae,                                        #137-138
                    tatyp,                                       #139-140
                    afilf,                                       #141-142
                    afils,                                       #143-144
                    nofilf,                                      #145-146
                    nofils,                                      #147-148
                    lcf,                                         #149-150
                    hcf,                                         #151-152
                    lcs,                                         #153-154
                    hcs,                                         #155-156
                    year,                                        #157-158
                    day,                                         #159-160
                    hour,                                        #161-162
                    minute,                                      #163-164
                    sec,                                         #165-166
                    timbas,                                      #167-168
                    trwf,                                        #169-170
                    grnors,                                      #171-172
                    grnofr,                                      #173-174
                    grnlof,                                      #175-176
                    gaps,                                        #177-178
                    otrav,                                       #179-180
                    d1,                                          #181-184
                    f1,                                          #185-188
                    d2,                                          #189-192
                    f2,                                          #193-196
                    ungpow,                                      #197-200
                    unscale,                                     #201-204
                    ntr,                                         #205-208
                    mark,                                        #209-210
                    shortpad,                                    #211-212
                    unassigned)
        end
end

mutable struct SU_trc
        hdr::SU_hdr
        dat::Array{Float32,1}
        function SU_trc(n::Int64)
                hdr=SU_hdr();
                dat=zeros(Float32,n)
                new(hdr,dat)
        end

end

"""
SUPutTrace(io::IOStream,trc::SU_trc)

Write a SU trace to a file

### Signature
* io  : file stream reference
* trc : seismic trace (header + data)
"""
function SUPutTrace(io::IOStream,trc::SU_trc)
        @assert iswritable(io)
        write(io,Int32(trc.hdr.tracl))
        write(io,Int32(trc.hdr.tracr))
        write(io,Int32(trc.hdr.fldr))
        write(io,Int32(trc.hdr.tracf))
        write(io,Int32(trc.hdr.ep))
        write(io,Int32(trc.hdr.cdp))
        write(io,Int32(trc.hdr.cdpt))
        write(io,Int16(trc.hdr.trid))
        write(io,Int16(trc.hdr.nvs))
        write(io,Int16(trc.hdr.nhs))
        write(io,Int16(trc.hdr.duse))
        write(io,Int32(trc.hdr.offset))
        write(io,Int32(trc.hdr.gelev))
        write(io,Int32(trc.hdr.selev))
        write(io,Int32(trc.hdr.sdepth))
        write(io,Int32(trc.hdr.gdel))
        write(io,Int32(trc.hdr.sdel))
        write(io,Int32(trc.hdr.swdep))
        write(io,Int32(trc.hdr.gwdep))
        write(io,Int16(trc.hdr.scalel))
        write(io,Int16(trc.hdr.scalco))
        write(io,Int32(trc.hdr.sx))
        write(io,Int32(trc.hdr.sy))
        write(io,Int32(trc.hdr.gx))
        write(io,Int32(trc.hdr.gy))
        write(io,Int16(trc.hdr.counit))
        write(io,Int16(trc.hdr.wevel))
        write(io,Int16(trc.hdr.swevel))
        write(io,Int16(trc.hdr.sut))
        write(io,Int16(trc.hdr.gut))
        write(io,Int16(trc.hdr.sstat))
        write(io,Int16(trc.hdr.gstat))
        write(io,Int16(trc.hdr.tstat))
        write(io,Int16(trc.hdr.laga))
        write(io,Int16(trc.hdr.lagb))
        write(io,Int16(trc.hdr.delrt))
        write(io,Int16(trc.hdr.muts))
        write(io,Int16(trc.hdr.mute))
        write(io,Int16(trc.hdr.ns))
        write(io,Int16(trc.hdr.dt))
        write(io,Int16(trc.hdr.gain))
        write(io,Int16(trc.hdr.igc))
        write(io,Int16(trc.hdr.igi))
        write(io,Int16(trc.hdr.corr))
        write(io,Int16(trc.hdr.sfs))
        write(io,Int16(trc.hdr.sfe))
        write(io,Int16(trc.hdr.slen))
        write(io,Int16(trc.hdr.styp))
        write(io,Int16(trc.hdr.stas))
        write(io,Int16(trc.hdr.stae))
        write(io,Int16(trc.hdr.tatyp))
        write(io,Int16(trc.hdr.afilf))
        write(io,Int16(trc.hdr.afils))
        write(io,Int16(trc.hdr.nofilf))
        write(io,Int16(trc.hdr.nofils))
        write(io,Int16(trc.hdr.lcf))
        write(io,Int16(trc.hdr.hcf))
        write(io,Int16(trc.hdr.lcs))
        write(io,Int16(trc.hdr.hcs))
        write(io,Int16(trc.hdr.year))
        write(io,Int16(trc.hdr.day))
        write(io,Int16(trc.hdr.hour))
        write(io,Int16(trc.hdr.minute))
        write(io,Int16(trc.hdr.sec))
        write(io,Int16(trc.hdr.timbas))
        write(io,Int16(trc.hdr.trwf))
        write(io,Int16(trc.hdr.grnors))
        write(io,Int16(trc.hdr.grnofr))
        write(io,Int16(trc.hdr.grnlof))
        write(io,Int16(trc.hdr.gaps))
        write(io,Int16(trc.hdr.otrav))
        write(io,Float32(trc.hdr.d1))
        write(io,Float32(trc.hdr.f1))
        write(io,Float32(trc.hdr.d2))
        write(io,Float32(trc.hdr.f2))
        write(io,Float32(trc.hdr.ungpow))
        write(io,Float32(trc.hdr.unscale))
        write(io,Int32(trc.hdr.ntr))
        write(io,Int16(trc.hdr.mark))
        write(io,Int16(trc.hdr.shortpad))
        @inbounds for i=1:14
                write(io,Int16(trc.hdr.unassigned[i]))
        end
        @inbounds for is=1:trc.hdr.ns
                write(io,Float32(trc.dat[is]))
        end
end # SUPutTrace

"""
SUPutTrace(io::IOStream,trc::SU_trc)

Read a SU trace from a file

### Signature
* io  : file stream reference
* trc : seismic trace (header + data)

"""
function SUGetTrace(io::IOStream,trc::SU_trc)
        @assert isreadable(io)
        trc.hdr.tracl=read(io,Int32)
        trc.hdr.tracr=read(io,Int32)
        trc.hdr.fldr=read(io,Int32)
        trc.hdr.tracf=read(io,Int32)
        trc.hdr.ep=read(io,Int32)
        trc.hdr.cdp=read(io,Int32)
        trc.hdr.cdpt=read(io,Int32)
        trc.hdr.trid=read(io,Int16)
        trc.hdr.nvs=read(io,Int16)
        trc.hdr.nhs=read(io,Int16)
        trc.hdr.duse=read(io,Int16)
        trc.hdr.offset=read(io,Int32)
        trc.hdr.gelev=read(io,Int32)
        trc.hdr.selev=read(io,Int32)
        trc.hdr.sdepth=read(io,Int32)
        trc.hdr.gdel=read(io,Int32)
        trc.hdr.sdel=read(io,Int32)
        trc.hdr.swdep=read(io,Int32)
        trc.hdr.gwdep=read(io,Int32)
        trc.hdr.scalel=read(io,Int16)
        trc.hdr.scalco=read(io,Int16)
        trc.hdr.sx=read(io,Int32)
        trc.hdr.sy=read(io,Int32)
        trc.hdr.gx=read(io,Int32)
        trc.hdr.gy=read(io,Int32)
        trc.hdr.counit=read(io,Int16)
        trc.hdr.wevel=read(io,Int16)
        trc.hdr.swevel=read(io,Int16)
        trc.hdr.sut=read(io,Int16)
        trc.hdr.gut=read(io,Int16)
        trc.hdr.sstat=read(io,Int16)
        trc.hdr.gstat=read(io,Int16)
        trc.hdr.tstat=read(io,Int16)
        trc.hdr.laga=read(io,Int16)
        trc.hdr.lagb=read(io,Int16)
        trc.hdr.delrt=read(io,Int16)
        trc.hdr.muts=read(io,Int16)
        trc.hdr.mute=read(io,Int16)
        trc.hdr.ns=read(io,Int16)
        trc.hdr.dt=read(io,Int16)
        trc.hdr.gain=read(io,Int16)
        trc.hdr.igc=read(io,Int16)
        trc.hdr.igi=read(io,Int16)
        trc.hdr.corr=read(io,Int16)
        trc.hdr.sfs=read(io,Int16)
        trc.hdr.sfe=read(io,Int16)
        trc.hdr.slen=read(io,Int16)
        trc.hdr.styp=read(io,Int16)
        trc.hdr.stas=read(io,Int16)
        trc.hdr.stae=read(io,Int16)
        trc.hdr.tatyp=read(io,Int16)
        trc.hdr.afilf=read(io,Int16)
        trc.hdr.afils=read(io,Int16)
        trc.hdr.nofilf=read(io,Int16)
        trc.hdr.nofils=read(io,Int16)
        trc.hdr.lcf=read(io,Int16)
        trc.hdr.hcf=read(io,Int16)
        trc.hdr.lcs=read(io,Int16)
        trc.hdr.hcs=read(io,Int16)
        trc.hdr.year=read(io,Int16)
        trc.hdr.day=read(io,Int16)
        trc.hdr.hour=read(io,Int16)
        trc.hdr.minute=read(io,Int16)
        trc.hdr.sec=read(io,Int16)
        trc.hdr.timbas=read(io,Int16)
        trc.hdr.trwf=read(io,Int16)
        trc.hdr.grnors=read(io,Int16)
        trc.hdr.grnofr=read(io,Int16)
        trc.hdr.grnlof=read(io,Int16)
        trc.hdr.gaps=read(io,Int16)
        trc.hdr.otrav=read(io,Int16)
        trc.hdr.d1=read(io,Float32)
        trc.hdr.f1=read(io,Float32)
        trc.hdr.d2=read(io,Float32)
        trc.hdr.f2=read(io,Float32)
        trc.hdr.ungpow=read(io,Float32)
        trc.hdr.unscale=read(io,Float32)
        trc.hdr.ntr=read(io,Int32)
        trc.hdr.mark=read(io,Int16)
        trc.hdr.shortpad=read(io,Int16)
        trc.hdr.unassigned=zeros(Int16,14)
        for i=1:14
                trc.hdr.unassigned[i]=read(io,Int16)
        end

        if size(trc.dat,1) == undef
                trc.dat=zeros(trc.hdr.ns)
        end
        @inbounds for is=1:trc.hdr.ns
                trc.dat[is]=read(io,Float32)
        end
        nothing
end # SUGetTrace
end # module SU_mod


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

"""
    struct PartitionedSampling <: AbstractSamplingAlgorithm

*Experimental feature, not part of stable public API.*

A sampling algorithm that partitions parameter space into multiple subspaces and
samples/integrates them independently ([Caldwell et al.](https://arxiv.org/abs/2008.03098)).

Constructors:

* ```$(FUNCTIONNAME)(; fields...)```

Fields:

$(TYPEDFIELDS)
"""
@with_kw struct PartitionedSampling{
    TR<:AbstractDensityTransformTarget,
    S<:AbstractSamplingAlgorithm,
    E<:AbstractSamplingAlgorithm,
    I<:IntegrationAlgorithm,
    P<:SpacePartitioningAlgorithm
} <: AbstractSamplingAlgorithm
    trafo::TR = PriorToUniform()
    npartitions::Integer = 10
    sampler::S = MCMCSampling()
    exploration_sampler::E = MCMCSampling(nchains=30)
    partitioner::P = KDTreePartitioning()
    integrator::I = AHMIntegration()
end

export PartitionedSampling

function bat_sample_impl(rng::AbstractRNG, target::PosteriorDensity, algorithm::PartitionedSampling)
    posterior = target

    @info "Generating Exploration Samples"
    exploration_samples = bat_sample(posterior, algorithm.exploration_sampler).result

    @info "Constructing Partition Tree"
    partition_tree, cost_values = partition_space(exploration_samples, algorithm.nsubspaces, algorithm.partitioner)
    # Convert 'partition_tree' structure into a set of truncated posteriors:
    posteriors_array = convert_to_posterior(posterior, partition_tree, extend_bounds = algorithm.partitioner.extend_bounds)

    @info "Sampling Subspaces"
    iterator_subspaces = [
        [subspace_ind, posteriors_array[subspace_ind],
        n_samples,
        algorithm.sampler,
        algorithm.integrator,
        algorithm.sampling_kwargs] for subspace_ind in Base.OneTo(n_subspaces)]
    samples_subspaces = pmap(inp -> sample_subspace(inp...), iterator_subspaces)

    @info "Combining Samples"
    # Save indices from different subspaces:
    samples_subspaces[1].info.samples_ind[1] = 1:length(samples_subspaces[1].samples)
    for subspace in samples_subspaces[2:end]
        start_ind, stop_ind = length(samples_subspaces[1].samples)+1, length(samples_subspaces[1].samples)+length(subspace.samples)
        subspace.info.samples_ind[1] = start_ind:stop_ind

        append!(samples_subspaces[1].samples, subspace.samples)
        append!(samples_subspaces[1].info, subspace.info)
    end

    return (result=samples_subspaces[1].samples,
                info = samples_subspaces[1].info,
                exp_samples = exploration_samples,
                part_tree = partition_tree,
                cost_values = cost_values
            )
end

function sample_subspace(
    space_id::Integer,
    posterior::PosteriorDensity,
    n::Integer,
    sampling_algorithm::A,
    integration_algorithm::I,
    sampling_kwargs::N
) where {N<:NamedTuple, A<:AbstractSamplingAlgorithm, I<:IntegrationAlgorithm}

    @info "Sampling subspace #$space_id"
    sampling_wc_start = Dates.Time(Dates.now())
    sampling_cpu_time = CPUTime.@CPUelapsed begin
        samples_subspace = bat_sample(posterior, sampling_algorithm).result
    end
    sampling_wc_stop = Dates.Time(Dates.now())

    integration_wc_start = Dates.Time(Dates.now())
    integration_cpu_time = CPUTime.@CPUelapsed begin
        integras_subspace = bat_integrate(samples_subspace, integration_algorithm).result
    end
    integration_wc_stop = Dates.Time(Dates.now())

    samples_subspace_reweighted = DensitySampleVector(
        (
            samples_subspace.v,
            samples_subspace.logd,
            integras_subspace.val .* samples_subspace.weight ./ sum(samples_subspace.weight),
            samples_subspace.info,
            samples_subspace.aux
        )
    )

    info_subspace = TypedTables.Table(
            density_integral = [integras_subspace],
            sampling_cpu_time = [sampling_cpu_time],
            integration_cpu_time = [integration_cpu_time],
            sampling_wc = [Dates.value(sampling_wc_start):Dates.value(sampling_wc_stop)],
            integration_wc = [Dates.value(integration_wc_start):Dates.value(integration_wc_stop)],
            worker_id = [Distributed.myid()],
            n_threads = [Threads.nthreads()],
            samples_ind = [0:0],
            sum_weights = [sum(samples_subspace.weight)],
        )

    return (samples = samples_subspace_reweighted, info = info_subspace)
end

function convert_to_posterior(posterior::PosteriorDensity, partition_tree::SpacePartTree; extend_bounds::Bool=true)

    if extend_bounds
        # Exploration samples might not always cover properly tails of the distribution.
        # We will extend boudnaries of the partition tree with original bounds which are:
        lo_bounds = getprior(posterior).bounds.vol.lo
        hi_bounds = getprior(posterior).bounds.vol.hi
        extend_tree_bounds!(partition_tree, lo_bounds, hi_bounds)
    end

    #Get flattened rectangular parameter bounds from tree
    subspaces_rect_bounds = get_tree_par_bounds(partition_tree)

    n_params = size(subspaces_rect_bounds[1])[1]

    posterior_array = map(
        x -> begin
            bounds = HyperRectBounds(x[:,1], x[:,2])
            prior_dist = BAT.truncate_density(getprior(posterior), bounds)
            PosteriorDensity(getlikelihood(posterior), prior_dist)
        end,
    subspaces_rect_bounds)

    return posterior_array
end


using Printf
using ShaperOriginDesignLib
using Unitful
using UnitfulUS
using Test

@testset "ShaperOriginDesignLib.jl" begin
    @test svgval(1.5) == 1.5
    @test svgval(4.5u"inch") == 4.5
    d = pathd(
        ["M", 0, 0],
        ["h", 3u"cm"],
        ["v", 4u"cm"],
        ["z"]
    )
    @test d == @sprintf("M 0 0 h %3f v %3f z",
                        3 / 2.54, 4 / 2.54)
    @test shaper_style_string(:on_line_cut) ==
        "fill: none; stroke: gray; stroke-width: 0.01; opacity: 1.0;"
end

@testset "viewport_attributes" begin
    left, top, right, bottom = (0.0u"inch", 0.0u"cm", 5.0u"inch", 2.54u"cm")
    attrs = viewport_attributes(left, top, right, bottom, u"inch")
    @test attrs.width == "5.0in"
    @test attrs.height == "1.0in"
    @test attrs.viewBox == "0.0 0.0 5.0 1.0"
end