text:
using DataStructures, DataArrays , DataFrames, StatsFuns, GLM , Distributions, MixedModels, StatsBase, StatLib, JSON, NamedArrays

const cfgDefaults=OrderedDict( :P2_Competitor => true
                        ,:pvalue_lvl => 0.20  #pvalue_lvl = 0.20
                        ,:excludedBreaks => String[]    #["estimated_hh_income","hh_age","number_of_children_in_living_un","person_1_gender"]
                        ,:excludedLevels => ["none"]
                        ,:excludedKeys => String[]
                        ,:exposed_flag_var => :exposed_flag
                        ,:sigLevel => "0.2"
                        ,:random_demos => [:estimated_hh_income,:hh_age,:number_of_children_in_living_Un,:person_1_gender]
                        ,:random_campaigns => []
                        ,:dropvars => [:exposed_flag,:dma,:dma_code]
                        ,:scoring_vars => [:Prd_1_Net_Pr_PRE,:Prd_1_Net_Pr_POS,:Buyer_Pos_P0,:Buyer_Pre_P0]
                        ,:occ_y_var => :Trps_POS_P1
                        ,:occ_logvar => :Trps_PRE_P1
                        ,:dolocc_y_var => :Dol_per_Trip_POS_P1
                        ,:dolocc_logvar => :Dol_per_Trip_PRE_P1
                        ,:pen_y_var => :Buyer_Pos_P1
                        ,:pen_logvar => :Buyer_Pre_P1
                        ,:TotalModelsOnly=>false
                       )

"""
# ************ CUSTOMIZE *********************
#jennie7_979
cfgDefaults[:random_demos] = [:estimated_hh_income,:hh_age,:number_of_children_in_living_Un,:person_1_gender]
cfgDefaults[:random_campaigns] = Symbol[:creative,:placement,:publisher]
cfgDefaults[:exposed_flag_var] = :exposed_flag_new
root="/mapr/mapr04p/analytics0001/analytic_users/jmdl/jennie7_979"

# Dict{Any,Any}(Pair{Any,Any}(:random_demos,Symbol[:estimated_hh_income,:hh_age,:number_of_children_in_living_Un,:person_1_gender]),Pair{Any,Any}(:random_campaigns,Symbol[:creative,:placement,:publisher]),Pair{Any,Any}(:root,"/mapr/mapr04p/analytics0001/analytic_users/jmdl/jennie7_979"))
"""

root = !isdefined(:root) ? pwd() : root
cfgDefaults = mergeDict(dict_Sym(read_cfg(root)),cfgDefaults)

# ************ END CUSTOMIZE *********************

# TEst speed improvements
#bigStr(fname::String) = open(fname) do f  return readstring(f) end
#s = bigStr(root*"/orig.csv")
#function loadDF()
#    open(root*"/orig.csv") do f
#       s = readstring(f)
#    end
#    readtable(IOBuffer(s))
#end
#dfdt = loadDF()

cd(root)
function loadDF()
    #cd("/media/u01/analytics/scoring/Healthy/modeling5/")
    #df_data = readtable("csv_final_healthychoice1_I3.csv",header=true); #lowercase(df_data)
    #df_h = readtable("Headers_healthy_choice3.csv",header=false); #lowercase(df_data)
    #names!(df_data, convert(Array{Symbol}, df_h[:x1]) )
    df_data = readtable(root*"/orig.csv",header=false);
    df_h = readtable(root*"/origHead.csv",header=false);
    names!(df_data, convert(Array{Symbol}, df_h[:x1]) )
end
df_in=loadDF()

function Pre_out(df_in::DataFrame)
     df_cat_pre = df_in[df_in[:Buyer_Pre_P0] .==1 , [:Prd_0_Net_Pr_PRE,:experian_id]]
     df_cat_pos = df_in[(df_in[:Buyer_Pre_P0] .==0) & (df_in[:Buyer_Pos_P0] .==1) , [:experian_id]]
     median_df = median(df_cat_pre[:Prd_0_Net_Pr_PRE])
     df_cat_pre[:Prd_0_Net_Pr_PRE_med1] = abs(df_cat_pre[:Prd_0_Net_Pr_PRE]-median_df)
     MAD=median(df_cat_pre[:Prd_0_Net_Pr_PRE_med1])
     df_cat_pre[:Prd_0_Net_Pr_PRE_med2] = (0.6745*(df_cat_pre[:Prd_0_Net_Pr_PRE]-median_df))/MAD
     df_cat_pre_zsc = df_cat_pre[abs(df_cat_pre[:Prd_0_Net_Pr_PRE_med2]) .< 3.5,:]
     df_cat_pre_zsc_1 = df_cat_pre_zsc[:,[:experian_id]]
     df_cat_pre_zsc_f = vcat(df_cat_pos,df_cat_pre_zsc_1)
     df_in_pout =  join(df_in, df_cat_pre_zsc_f, on =  :experian_id , kind = :inner);
end
df_in = Pre_out(df_in);

function isValid(df_data::DataFrame,cfg::OrderedDict)
    function checkValid(iarr::Array{Symbol})  length(setdiff(iarr, names(df_data))) > 0 ? false : true end
    !checkValid(cfg[:all_mandatory_vars]) ? error("ERROR: Not all mandatory_vars in dataset ") : println("VALID : mandatory_vars")
    !checkValid(cfg[:scoring_vars]) ? error("ERROR: Not all scoring_vars in dataset ") : println("VALID : scoring_vars")
end

function getCFG(df_in::DataFrame)
    cfg=StatLib.loadCFG(cfgDefaults, pwd()*"/app.cfg")
    #cfg[:exposed_flag_var] = :exposed_flag_new                  # Go In app.cfg
    #cfg[:random_campaigns] = [:Publisher_Fct1,:Targeting_Fct1]  # Go In app.cfg
    cfg[:allrandoms] = vcat(cfg[:random_demos],cfg[:random_campaigns])
    #cfg[:ProScore] = grep1("MODEL",names(df_in))
    ps = filter(x->contains(string(x), "MODEL"), names(df_in)) # Set the ProScore variable in the dataset   #push!(cfg[:all_mandatory_vars], cfg[:ProScore])
    cfg[:ProScore] = length(ps) > 0 ? ps[1] : :MISSING_MODEL_VARIABLE_IN_DATA
    cfg[:num_products] = length( grep("Buyer_Pre_P",names(df_in)))-1  # get number of products in the data
    #rework vars after loading cfg
    cfg[:random_campaigns] = intersect(cfg[:random_campaigns],names(df_in))

    cfg[:occ_logvar_colname] = Symbol("LOG_"*string(cfg[:occ_logvar]))
    cfg[:dolocc_logvar_colname] = Symbol("LOG_"*string(cfg[:dolocc_logvar]))
    cfg[:pen_logvar_colname] = Symbol("LOG_"*string(cfg[:pen_logvar]))

    cfg[:all_mandatory_vars] = vcat( [:experian_id,
                                      cfg[:pen_y_var],cfg[:pen_logvar],
                                      cfg[:occ_y_var], cfg[:occ_logvar],
                                      cfg[:dolocc_y_var], cfg[:dolocc_logvar]
                                     ]
                                     , cfg[:random_demos]
                                   )
    return cfg
end
cfg=getCFG(df_in)

isValid(df_in,cfg)

function reworkCFG!(df_in::DataFrame,cfg::OrderedDict)
    xflags=[cfg[:exposed_flag_var],:exposed_flag,:Nonbuyer_Pre_P1]  # :Nonbuyer_Pre_P1 no longer used
    features = setdiff(names(df_in),xflags)
    if :state in names(df_in)
        cfg[:xVarsDemos]=vcat([:experian_id, :banner, :Prd_0_Qty_PRE],features[findfirst(features, :state):findfirst(features, :Mosaic)])   # exclude demos between....
    else
        cfg[:xVarsDemos]=Symbol[]
    end
    cfg[:xVarsDemos]=setdiff(cfg[:xVarsDemos],[:person_1_gender,:number_of_children_in_living_Un]) # exclude person, # from non-used demos
    cfg[:xVarsPost] = grep(["POS","Pos","Buyer_Pre_","Nonbuyer_Pre_"],features)   #exclude POST variables
    cfg[:iVarsPREPOS] = grep(["PRE_POS","Pre_Pos"],features)
    features=setdiff(features, setdiff(  vcat(cfg[:xVarsDemos],cfg[:xVarsPost]) ,cfg[:iVarsPREPOS])  )
    cfg[:xVarsP0] =  setdiff(grep("0", features ),[cfg[:ProScore]] ) #exclude category variables(exclude P0's)
    features=setdiff(features,cfg[:xVarsP0])
    cfg[:xVarsReports] = grep(["Perc_","Pr_per_"],features)  #exclude Reporting vars
    features=setdiff(features,cfg[:xVarsReports])
    cfg[:xvars] = vcat(xflags,cfg[:xVarsDemos], cfg[:xVarsPost],cfg[:xVarsP0], cfg[:xVarsReports],cfg[:dropvars])
    features=setdiff(features,cfg[:dropvars])
    cfg[:ivars] = vcat(cfg[:iVarsPREPOS],cfg[:all_mandatory_vars],cfg[:scoring_vars])
    cfg[:ALL_vars_to_exclude] = setdiff(cfg[:xvars], cfg[:ivars])
    df_in[:group] = df_in[cfg[:exposed_flag_var]]
    df_in[:panid] = df_in[:experian_id]
    features = setdiff(unique(vcat(features,cfg[:iVarsPREPOS],cfg[:all_mandatory_vars],cfg[:scoring_vars],[:group,:panid])  ) ,[:experian_id] )
    cfg[:negativevars] = grep(vec(hcat([["P"*string(i),string(i)*"_"] for i=3:cfg[:num_products]]...)),features)   # get variables that need to have negative sign
    cfg[:positivevars] = grep(["P1", "1_","MODEL"], features)    # get variables that need to have positive sign
    if cfg[:P2_Competitor] == true
        cfg[:negativevars] = unique(vcat(cfg[:negativevars],grep(["P2","2_"],features)))
    else
        cfg[:positivevars] = unique(vcat(cfg[:positivevars],grep(["P2","2_"],features)))
    end
    return df_in[features]
end

dfd = reworkCFG!(df_in,cfg)

#######################################
#--------- Data Manipulation ---------#
#######################################

function data_Prep(dfd::DataFrame, cfg::OrderedDict)
    dfd[:isO]=false
    dfd[dfd[:number_of_children_in_living_Un].>=4,:number_of_children_in_living_Un] = 4  # aggregate #of children for 4+ L_114
    if typeof(dfd[:group]) in [DataArray{String,1}]
        dfd[ findin(dfd[:group],["//N","\\N"]), :group] = "0"
        dfd[DataArrays.isna(dfd[:group]), :group]="0"
        dfd[:group] = [parse(Int64,s) for s = dfd[:group]]
    else
        dfd[ DataArrays.isnan(dfd[:group]), :group] = 0
    end
    vars = DataFrame(names=names(dfd),eltypes=eltypes(dfd))
    #for c in setdiff(y,cfg[:allrandoms]) # set variables as.numeric and replace NA's with zero
    for c in setdiff(vars[findin(vars[:eltypes],[String]),:names],cfg[:allrandoms])
        print("Convert String: String->Numeric: ",c)
        try dfd[c] = map( x -> DataArrays.isna.(x) ?  NaN : convert(Float64, x)  , dfd[c]) catch e println("  (failed)") end  #NA to NaN
        try dfd[c] = convert(Array{Float64}, dfd[c]) catch e end   # To Float64
    end
    vars = DataFrame(names=names(dfd),eltypes=eltypes(dfd))
    for c in setdiff(vars[findin(vars[:eltypes],[Float64]),:names],cfg[:allrandoms])  # replace NaN's with zero for numeric variables
        println("Replace Float64 NaN (0.0): ",c)
        dfd[ DataArrays.isnan(dfd[c]), c] = 0.0
    end
    for c in  setdiff(vars[findin(vars[:eltypes],[Int64]),:names],cfg[:allrandoms])
        println("Replace Int64 NaN (0) : ",c)
        dfd[ DataArrays.isnan(dfd[c]), c] = 0
    end
    # NOTE : Do QCs here
    dfd[dfd[:person_1_gender].=="U",:isO] = true   # remove HHs with no gender info
    #dfd[dfd[:person_1_gender].=="U",:whyO] = "person_1_gender=U; remove HHs with no gender info"
    dfd[findin(dfd[:estimated_hh_income],["U","L"]),:estimated_hh_income]="L" # aggregate U and L levels of hh income

    for r in cfg[:random_campaigns]    # check and drop exposed HHs with no publisher info or non-exposed HHs with publisher info
        dfd[findin(dfd[r],["\\N","NULL","0","NONE"])  ,r] ="none"
        #dfd[ (dfd[:isO].==false) & (dfd[r].!="none") & (dfd[:group].==0) ,:whyO] = "non exposed HHs with publisher info"
        dfd[ (dfd[:isO].==false) & (dfd[r].!="none") & (dfd[:group].==0) ,:isO] = true
        #println(r," non exposed HHs with publisher info : ",nrow(dfd[dfd[:whyO].=="non exposed HHs with publisher info",:] )   )
        #dfd[ (dfd[:isO].==false) & (dfd[r].=="none") & (dfd[:group].!=0) ,:whyO] = "exposed HHs with no publisher info"
        dfd[ (dfd[:isO].==false) & (dfd[r].=="none") & (dfd[:group].!=0) ,:isO] = true
        #println(r," exposed HHs with no publisher info : ",nrow(dfd[dfd[:whyO].=="exposed HHs with no publisher info",:] )   )
    end
    # segments for outliers detection
    dfd[:data_NB_NE_B] = false
    dfd[ (dfd[:Buyer_Pre_P1].==0 ) & (dfd[:group].==0 ) & (dfd[:Buyer_Pos_P1].==1 ) ,:data_NB_NE_B] = true
    dfd[:data_B_E_NB] = false
    dfd[ (dfd[:Buyer_Pre_P1].==1 ) & (dfd[:group].==0 ) & (dfd[:Buyer_Pos_P1].==0 ) ,:data_B_E_NB] = true
    dfd[:pen_reduction] = false
    dfd[ (dfd[:Buyer_Pre_P1].==1) & (dfd[:group].==0) & (dfd[:Buyer_Pos_P1].==0 )  ,:pen_reduction] = true
    dfd[:occ_reduction] = false
    dfd[ (dfd[:group].==0) & (dfd[:Buyer_Pos_P1].==1) & (dfd[:Trps_POS_P1].< dfd[:Trps_PRE_P1] ) ,:occ_reduction] = true
    dfd[:dolocc_reduction] = false
    dfd[  (dfd[:group].==0) & (dfd[:Buyer_Pos_P1].==1) & (dfd[:Dol_per_Trip_POS_P1].< dfd[:Dol_per_Trip_PRE_P1] )  , :dolocc_reduction] = true
    return dfd[dfd[:isO].==false, : ]   #[setdiff(names(dfd),[:isO,:whyO])]
end

dfd = data_Prep(dfd, cfg);

#function Restrict50Buyers()
#    for (k,v) in Dict(r=>countmap(dfd[(dfd[:Buyer_Pos_P1].==1)&(dfd[:group].==1), r]) for r in cfg[:random_campaigns])
#            lvls=String[]
#            for (kl,vl) in v
#                if vl < 50
#                    println("$k :: $kl to Other")
#                    dfd[dfd[k].==kl,k] = "Other"
#                end
#                #if vl > 50 push!(lvls,kl) end
#            end
#            #toNone = setdiff(levels(dfd[k]),lvls)
#            #println("\n\n\n$k       ::::          ",toNone)
#    end
#end
#Restrict50Buyers()

function Restrict50Buyers()
    for (k,v) in Dict(r=>countmap(dfd[(dfd[:Buyer_Pos_P1].==1), r]) for r in cfg[:random_campaigns])
            lvls=String[]
            for (kl,vl) in v
                if vl < 25
                    println("$k :: $kl to Other")
                    dfd[dfd[k].==kl,k] = "Other"
                end
                #if vl > 50 push!(lvls,kl) end
            end
            #toNone = setdiff(levels(dfd[k]),lvls)
            #println("\n\n\n$k       ::::          ",toNone)
    end
end
Restrict50Buyers()

function MatchMe(dfd::DataFrame,cfg::OrderedDict)
    df=dfd[dfd[:isO].==false,:]
    df_exp     = df[df[:group].==1,:]
    df_unexp   = df[df[:group].==0,:]
    df_exp_dim   = nrow(df_exp)
    df_unexp_dim = nrow(df_unexp)
    new_unexp_dim = df_unexp_dim*(df_unexp_dim>2000000 ? 0.3 : df_unexp_dim>1000000 ? 0.4 : df_unexp_dim>750000 ? 0.6 : 0.7)
    if length(string(cfg[:ProScore])) == 0
        df_unexp_1 =  df[(df[:group].==0)&(df[:Buyer_Pre_P1].==1),:]
        df_unexp_0 =  df[(df[:group].==0)&(df[:Buyer_Pre_P1].==0),:]

        df_exp_1_dim = nrow(df[(df[:group].==1)&(df[:Buyer_Pre_P1].==1),:])
        df_exp_0_dim = nrow(df[(df[:group].==1)&(df[:Buyer_Pre_P1].==0),:])
        df_unexp_1_dim = nrow(df_unexp_1)
        df_unexp_0_dim =  nrow(df_unexp_0)
        dim_sample0 = round(Int64, (new_unexp_dim-df_exp_dim  ) / (1+(df_exp_1_dim / df_exp_0_dim)) )
        dim_sample1 = round(Int64, new_unexp_dim - df_exp_dim - dim_sample0)

        new_df_unexp_1 = df_unexp_1[sample(1:size(df_unexp_1,1), dim_sample1 ),:]
        new_df_unexp_0 = df_unexp_0[sample(1:size(df_unexp_0,1), dim_sample0 ),:]
        dfd_sample  = vcat(df_exp,new_df_unexp_1,new_df_unexp_0)
    elseif length(string(cfg[:ProScore])) > 0
        sample_control_data=similar(df_unexp, 0)
        for (key, value) in countmap(df_exp[cfg[:ProScore]])
            sample_dim=round(Int64,new_unexp_dim*(value/df_exp_dim))
            temp_data = df_unexp[df_unexp[cfg[:ProScore]].==key,:]
            if sample_dim > size(temp_data,1) sample_dim = size(temp_data,1) end  #??
            samp_data = temp_data[sample(1:size(temp_data,1), sample_dim, replace=false),:]
            sample_control_data = vcat(sample_control_data,    samp_data   )
        end

        sample_data = vcat(sample_control_data,df_exp)
        sample_df_unexp = sample_data[sample_data[:group].==0,:]
        sample_df_exp   = sample_data[sample_data[:group].==1,:]
        sample_df_unexp_1 = sample_data[(sample_data[:group].==0)&(sample_data[:Buyer_Pre_P1].==1),:]
        sample_df_unexp_0 = sample_data[(sample_data[:group].==0)&(sample_data[:Buyer_Pre_P1].==0),:]
        sample_df_exp_1 =  sample_data[(sample_data[:group].==1)&(sample_data[:Buyer_Pre_P1].==1) ,:]
        sample_df_exp_0 =  sample_data[(sample_data[:group].==1)&(sample_data[:Buyer_Pre_P1].==0) ,:]
        sample_df_unexp_1_dim = nrow(sample_df_unexp_1)
        sample_df_unexp_0_dim = nrow(sample_df_unexp_0)
        sample_df_exp_1_dim = nrow(sample_df_exp_1)
        sample_df_exp_0_dim = nrow(sample_df_exp_0)
        dim_sampleA = round(Int64,(sample_df_exp_1_dim/sample_df_exp_0_dim)*sample_df_unexp_0_dim)
        dim_sampleB = round(Int64,(sample_df_exp_0_dim/sample_df_exp_1_dim)*sample_df_unexp_1_dim)

        if sample_df_unexp_1_dim/sample_df_unexp_0_dim > sample_df_exp_1_dim/sample_df_exp_0_dim
            new_df_unexp_1 = sample_df_unexp_1[sample(1:sample_df_unexp_1_dim,dim_sampleA , replace=false),:]
            dfd_sample = vcat(sample_df_exp,new_df_unexp_1,sample_df_unexp_0)
        else
            new_df_unexp_0 = sample_df_unexp_0[sample(1:sample_df_unexp_0_dim, dim_sampleB, replace=false ),:]
            dfd_sample = vcat(sample_df_exp,sample_df_unexp_1,new_df_unexp_0)
        end
    end
    rows2remove = setdiff(dfd[dfd[:isO].==false, :panid],dfd_sample[:panid])
    #dfd[findin(dfd[:panid],rows2remove),:whyO]="NoMatch"
    dfd[findin(dfd[:panid],rows2remove),:isO]=true

#    #Matching - pre-post
"""
    dfp=join(by(dfd[(dfd[:isO].==false),:],[:group,:Buyer_Pos_P1], df->DataFrame(cnt=size(df,1))) ,
             by(dfd[(dfd[:isO].==false),:],:group,df->DataFrame(tot=size(df,1)))
             , on=:group
            )
    dfp[:prop] = dfp[:cnt]./dfp[:tot]
    if dfp[(dfp[:group].==1)&(dfp[:Buyer_Pos_P1].==1),:prop][1] < dfp[(dfp[:group].==0)&(dfp[:Buyer_Pos_P1].==1),:prop][1]

        scnt = Int( round(( dfp[(dfp[:group].==0)&(dfp[:Buyer_Pos_P1].==0),:cnt][1]*dfp[(dfp[:group].==1)&(dfp[:Buyer_Pos_P1].==1),:prop][1] ) / (1-dfp[(dfp[:group].==1)&(dfp[:Buyer_Pos_P1].==1),:prop][1])  ))
        smp = sample(dfd[(dfd[:isO].==false)&(dfd[:Buyer_Pos_P1].==1)&(dfd[:group].==0),:panid], scnt , replace=false)

        oliers = setdiff(dfd[(dfd[:isO].==false)&(dfd[:Buyer_Pos_P1].==1)&(dfd[:group].==0),:panid],smp)

        dfd[findin(dfd[:panid], setdiff(dfd[(dfd[:isO].==false)&(dfd[:Buyer_Pos_P1].==1)&(dfd[:group].==0),:panid],smp)),:isO] =true
   end
"""
        # ---- end matching ------------

    return dfd[dfd[:isO].==false, : ]  #[setdiff(names(dfd),[:isO,:whyO])]
end

dfd = MatchMe(dfd,cfg)

lowercase!(dfd)
cfg=lowercase(cfg)

######################################
#------------MODEL OBJECTS-----------#  [:fea_or_dis_trps_shr_dpp_p1,:fea_or_dis_trps_shr_dpp_p2,:fea_or_dis_trps_shr_dpp_p3,:fea_or_dis_trps_shr_dpp_p4]
######################################

iocc = Dict(:modelName=>:occ, :raneff=>cfg[:random_campaigns], :y_var=>:trps_pos_p1, :logvar=>:LOG_trps_pre_p1, :logvarOrig=>:trps_pre_p1 )
idolocc = Dict(:modelName=>:dolocc, :raneff=>cfg[:random_campaigns], :y_var=>:dol_per_trip_pos_p1, :logvar=>:LOG_dol_per_trip_pre_p1, :logvarOrig=>:dol_per_trip_pre_p1)
ipen = Dict(:modelName=>:pen, :raneff=>cfg[:random_campaigns], :y_var=>:buyer_pos_p1, :logvar=>:LOG_buyer_pre_p1, :logvarOrig=>:buyer_pre_p1 )

custom_vars = [:iso,:whyo,:data_nb_ne_b, :data_b_e_nb ,:pen_reduction,:occ_reduction,:dolocc_reduction]
for m in [iocc,idolocc,ipen] dfd[m[:logvar]]=log(Array(dfd[m[:logvarOrig]]+1)) end

function genExcludeVars!(iocc::Dict,idolocc::Dict,ipen::Dict)
    iocc[:exclude_vars] = vcat( custom_vars,
                                [ :buyer_pos_p1, iocc[:logvarOrig]
                                  ,idolocc[:y_var],idolocc[:logvar],idolocc[:logvarOrig]
                                 ,ipen[:y_var],ipen[:logvar],ipen[:logvarOrig]
                               ]
                              )
    idolocc[:exclude_vars] = vcat( custom_vars,
                                   [ :buyer_pos_p1, idolocc[:logvarOrig]
                                           ,iocc[:y_var],iocc[:logvar],iocc[:logvarOrig]
                                           ,ipen[:y_var],ipen[:logvar],ipen[:logvarOrig]
                                   ]
                                 )

    ipen[:exclude_vars] = vcat( custom_vars,
                                [ ipen[:logvarOrig]
                                  ,iocc[:y_var],iocc[:logvar],iocc[:logvarOrig]
                                  ,idolocc[:y_var],idolocc[:logvar],idolocc[:logvarOrig]
                                ]
                                 )
end
genExcludeVars!(iocc,idolocc,ipen)

function featureSelection(dfd::DataFrame, m::Dict)
    function rmVars(v::DataArray{Any}) rmVars(convert(Array{Symbol},v)) end
    function rmVars(v::Array{Any}) rmVars(convert(Array{Symbol},v)) end
    function rmVars(v::Array{Symbol})
        return setdiff(vars,v)
    end
    vars=setdiff(names(dfd),   vcat(m[:exclude_vars],m[:y_var],:panid,cfg[:random_demos],cfg[:random_campaigns],cfg[:scoring_vars]) )
    upperModName = uppercase( string(  m[:modelName]  )  )
    println( upperModName*" : SingleValue") #SingleValue
    removed_SingleLevelVars=FS_singleLevel(dfd,vars)
    vars = rmVars(removed_SingleLevelVars)
    println(upperModName*" : Singularity : "*string(genF(m[:y_var],vars))) # Singularity
    singularity_x = checksingularity(genF(m[:y_var],vars), dfd)
    vars = rmVars(singularity_x)
    println(upperModName*" : PVals") #PVals
    f=genF(m[:y_var],vars)
    g1 = glm( f, dfd[convert(Array{Symbol},vcat(f.lhs,f.rhs.args[3:end]))]  , m[:dist], m[:lnk] )
    sdf = coefDF(g1,true)
    #g1_x= sdf[sdf[:pval].>0.7,:vars]
    g1_x= sdf[sdf[:pval].>0.7,:parameter]
    #vars = rmVars(g1_x)
    vars = rmVars(convert(Array{Any},g1_x))
    #function chkSigns(m::Dict, vars::Array{Symbol}, dfd::DataFrame, cfg::OrderedDict)  # Pvalue & Signs
    #    vars=unique(vcat(vars,[:group]))
    #    f=genF(m[:y_var],vars)
    #    g = glm( f, dfd[convert(Array{Symbol},vcat(f.lhs,f.rhs.args[3:end]))]  , m[:dist], m[:lnk] )
    #    sdf = coefDF(g1)
    #    neutralvars = setdiff(vars,vcat(cfg[:negativevars],cfg[:positivevars]))
    #    neg=intersect(cfg[:negativevars],sdf[sdf[:coef].<0,:vars])
    #    pos=intersect(cfg[:positivevars],sdf[sdf[:coef].>0,:vars])
    #    varstokeep = intersect(vcat(neutralvars, pos,neg) ,  sdf[sdf[:pval].<cfg[:pvalue_lvl] ,:vars] )
    #    varstokeep =  convert(Array{Symbol},varstokeep)
    #    return g, varstokeep
    #end
    function chkSigns(m::Dict, vars::Array{Symbol}, dfd::DataFrame, cfg::OrderedDict)  # Pvalue & Signs
        vars=unique(vcat(vars,[:group]))
        f=genF(m[:y_var],vars)
        g = glm( f, dfd[convert(Array{Symbol},vcat(f.lhs,f.rhs.args[3:end]))]  , m[:dist], m[:lnk] )
        sdf = coefDF(g1, true)
        neutralvars = setdiff(vars,vcat(cfg[:negativevars],cfg[:positivevars]))
        neg=intersect(cfg[:negativevars],sdf[sdf[:coef].<0,:parameter])
        pos=intersect(cfg[:positivevars],sdf[sdf[:coef].>0,:parameter])
        varstokeep = intersect(vcat(neutralvars, pos,neg) ,  sdf[sdf[:pval].<cfg[:pvalue_lvl] ,:parameter] )
        varstokeep =  convert(Array{Symbol},varstokeep)
        return g, varstokeep
    end

    println(upperModName*" : SIGN Check 1")
    (g3, initialvars) = chkSigns(m, vars, dfd, cfg)
    println(upperModName*" : SIGN Check 2")
    (g4, vars_2) = chkSigns(m, initialvars, dfd, cfg)
    function getCorrVars(m::RegressionModel, dfd::DataFrame, vars_2::Array{Symbol})
        sdf = coefDF(m, true)
        vars_2 = convert(Array{Symbol}, setdiff(vars_2, filter(x-> typeof(dfd[x]) in [PooledDataArray{String,UInt8,1}], vars_2)) )
        rm_lst=Symbol[]
        if (length(vars_2) > 1) & (   length(getColswithType("num", dfd, vars_2 ) ) > 1  )
            stackdf = corDFD(dfd,vars_2)
            stackdf[:variable_pval] = [ sdf[sdf[:parameter].==c,:pval][1]   for c in stackdf[:variable]]
            stackdf[:vars_pval] = [ sdf[sdf[:parameter].==c,:pval][1]   for c in stackdf[:vars]]
            stackdf[:most_Sig] = map((x,y) -> x < y ? "variable" : "vars" ,stackdf[:variable_pval],stackdf[:vars_pval])

            for row in eachrow(stackdf[(stackdf[:value].> 0.8) | (stackdf[:value].<-0.8),:])
                if row[:vars] == "group"
                    push!(rm_lst,row[:variable])
                elseif row[:variable] == "group"
                    push!(rm_lst,row[:vars])
                else
                    row[:most_Sig] == "variable" ? push!(rm_lst,row[:vars]) : push!(rm_lst,row[:variable])
                end
            end
        end
        return rm_lst
    end
    println(upperModName*" : Correlation")
    corrvars_x = getCorrVars(g4, dfd,vars_2  )
    vars_2 = setdiff(vars_2,corrvars_x)
    (g5, finalvars) =  chkSigns(m, convert(Array{Symbol},vars_2), dfd, cfg)

    #VIF is only valid on multiple cols
    if length(finalvars) > 2
        println( upperModName *" : Z & Vif") #Z & Vif
        f=genF(m[:y_var],finalvars)
        g2 = glm( f, dfd[convert(Array{Symbol},vcat(f.lhs,f.rhs.args[3:end]))]  , m[:dist], m[:lnk] )
        sdf=vifDF2(g2)
        z = sdf[abs(sdf[:zval]).<1.96,:parameter]
        v = sdf[ !DataArrays.isna(sdf[:vif])&(sdf[:vif].>15),:parameter]
        g2_x =intersect(z,v)
        finalvars = setdiff(finalvars,g2_x)
        println("vif_vars: ",g2_x)
    end
    finalvars = setdiff(finalvars,[:group])# reorder for group
    finalvars = convert(Array{Symbol},vcat(finalvars,[:group]) )
    return finalvars
end

factor_cols=vcat( [ cfg[:proscore], :group, :panid], cfg[:allrandoms] )
for c in setdiff(factor_cols,[:panid, cfg[:proscore]]) #cfg[:random_campaigns]
    if !( typeof(dfd[c]) in [  DataArray{String,1}  ])
        println("converting to Strings : ", c," of type : ",typeof(dfd[c]))
        dfd[c] = map(x->string(x),dfd[c])
        dfd[c] = convert(Array{String},dfd[c])
    end
end
poolit!(dfd,factor_cols)

iocc[:finalvars] = featureSelection(dfd[(dfd[:iso].==false)&(dfd[:buyer_pos_p1].==1),:], expandM(iocc))
idolocc[:finalvars]   = featureSelection(dfd[(dfd[:iso].==false)&(dfd[:buyer_pos_p1].==1),:], expandM(idolocc))
ipen[:finalvars]  = featureSelection(dfd[(dfd[:iso].==false) ,:], expandM(ipen))

"""
dfcor(dfr::DataFrame) = dfcor(dfr, names(dfr))

function dfcor(dfr::DataFrame, cols::Vector{Symbol})
    nms = Symbol[]
    arr = Float64[]
    for (n, v) in eachcol(dfr)
        if n ∈ cols && isnumeric(v)
            push!(nms, n)
            append!(arr, v)
        end
    end
    result = NamedArray(cor(reshape(arr, (size(dfr, 1), length(nms)))))
    NamedArrays.setnames!(result, string.(nms), 1)
    NamedArrays.setnames!(result, string.(nms), 2)
    result
end

dfcor1(dfr::DataFrame) = dfcor1(dfr, names(dfr))

function dfcor1(dfr::DataFrame, cols::Vector{Symbol})
    m = size(dfr, 1)
    nms = Symbol[]
    arr = ones(m)
    for (n, v) in eachcol(dfr)
        if n ∈ cols && isnumeric(v)
            push!(nms, n)
            append!(arr, v)
        end
    end
    n = length(nms) + 1
    R = full(UpperTriangular(view(qrfact!(reshape(arr, (m, n)))[:R],
                2:n, 2:n)))
    for j in 1:size(R, 2)
        colj = view(R, 1:j, j)
        colj ./= norm(colj)
    end
    Rt = UpperTriangular(R)
    result = NamedArray(Rt'Rt)
    NamedArrays.setnames!(result, string.(nms), 1)
    NamedArrays.setnames!(result, string.(nms), 2)
    result
end
"""
#m=expandM(ipen)
#glm(genF(m[:y_var],m[:finalvars]), dfd[vcat(m[:y_var],m[:finalvars])], m[:dist], m[:lnk])

# --- Removing outlliers - to ensure overall campaign uplift
function getOutliers(r::DataFrame, dfd::DataFrame, m::Dict, segment::Symbol,pct::Int64) # :data_nb_ne_b, :data_b_e_nb, :pen_reduction, :occ_reduction, :dolocc_reduction
    if m[:Buyer_Pos_P1_is1]
        println("pre=1")
        odf = dfd[(dfd[:iso].==false)&(dfd[:buyer_pos_p1].==1)&(dfd[segment].==true),:panid]
    else
        println("pre!=1")
        odf = dfd[(dfd[:iso].==false)&(dfd[segment].==true),:panid]
    end
    pctnum = Integer(round(length(odf) / 100) * pct)
    return sort(r[findin(r[:panid],odf) ,:], cols=[order(:resids, rev = true)] )[1:pctnum,:panid]
end

function genGLM(dfd::DataFrame, m::Dict)
    f=genF(m[:y_var],m[:finalvars])
    println("GLM for ",m[:modelName]," ::: ",f)
    dfd_tmp = m[:modelName] in [:occ, :dolocc] ? dfd[(dfd[:iso].==false)&(dfd[:buyer_pos_p1].==1),:] : dfd[(dfd[:iso].==false),:]
    g = glm(f, dfd_tmp[vcat(m[:y_var],m[:finalvars])], m[:dist], m[:lnk])
    println("GLM Residuals for ",m[:modelName])
    r = DataFrame(panid=dfd_tmp[:panid], resids=StatLib.xResiduals(g))
    return r, g
end

function genlists(dfd::DataFrame, iocc::Dict,idolocc::Dict,ipen::Dict)
    r1, g1 = genGLM( dfd[(dfd[:iso].==false)&(dfd[:buyer_pos_p1].==1),:], expandM(iocc))
    r2, g2 = genGLM( dfd[(dfd[:iso].==false)&(dfd[:buyer_pos_p1].==1),:], expandM(idolocc))
    r3, g3 = genGLM( dfd[(dfd[:iso].==false),:], expandM(ipen))
    rlist = [r1,r2,r3]
    glist = [g1,g2,g3]
    return rlist, glist
end
rlist, glist = genlists(dfd,iocc,idolocc,ipen)

for i in 1:5
    mlist = [expandM(iocc),expandM(idolocc),expandM(ipen)]
    #mliftβ = [ g.sdf[g.sdf[:vars].==:group,:coef][1] for g in glist]
    mliftβ = [ sdf[sdf[:parameter].==:group,:coef][1] for sdf in [coefDF(m, true) for m in glist]]
    minidx = find(x->x==minimum(mliftβ),mliftβ)[1]
    if  mliftβ[minidx] < 0.0
        #mx=mlist[minidx]
        println("WRONG : ",mliftβ," for ",mlist[minidx][:modelName])
        panids = getOutliers(rlist[minidx], dfd, mlist[minidx], :data_nb_ne_b,5)
        dfd[findin(dfd[:panid],panids) ,:iso] = true
        rlist, glist = genlists(dfd,iocc,idolocc,ipen)
    else
        println("All good : ",mliftβ)
        break
    end
end

# ---- SAVE RESULTS -----------
delete!(iocc, :exclude_vars)
delete!(idolocc, :exclude_vars)
delete!(ipen, :exclude_vars)

modelsDict = Dict()
modelsDict[:iocc] = iocc
modelsDict[:idolocc] = idolocc
modelsDict[:ipen] = ipen

modelsDict[:factors] = unpool(dfd)

cols = vcat([:panid, :iso, :group, :buyer_pos_p1],cfg[:random_campaigns])
cols = vcat(cols,iocc[:finalvars],[iocc[:y_var], iocc[:logvar]])
cols = vcat(cols,idolocc[:finalvars],[idolocc[:y_var], idolocc[:logvar]])
cols = unique(vcat(cols,ipen[:finalvars],[ipen[:y_var], ipen[:logvar]]) )
# add required cold for Scoring agregation
cols = unique(vcat(cols,[:buyer_pre_p1, :buyer_pos_p1, :trps_pos_p1,:trps_pre_p1,:dol_per_trip_pre_p1,:dol_per_trip_pos_p1, :prd_1_net_pr_pre, :prd_1_net_pr_pos]))

save_dfd(root, dfd[(dfd[:iso].==false),cols] )
save_modelsDict(root, modelsDict)

max_stars_repo_name: werbac/jlib

max_issues_repo_name: werbac/jlib

max_forks_repo_name: werbac/jlib


text:
#
# Module for requests pooling. Communication works through Client/
# Server modules. This module works in a simple way: first, you
# have to create Pooling data object by calling create() function.
# It returns PoolingData data object, which is used in all functions.
# create() doesn't start pooling. For this, you have to call start()
# function. To stop pooling, call stop().
#
# Events:
#     beforerequest Fired before every request. I used for setting
#                   parameters of request. e.g.: [RpcApi.RPC_GET_REGION,
#                   1, 1, 200, 150].
#     afterresponse Fired after every request. Passes answer object.
#
# Usage:
#     import Pooling
#
#     function onBeforeRequest(pObj::PoolingData)
#         push!(pObj.args, "arg1")
#         push!(pObj.args, "arg2")
#     end
#     function onAfterResponse(pObj::PoolingData, ans::Connection.Answer)
#         println(ans.data)
#     end
#
#     p = Pooling.create(ip"127.0.0.1", 2000)
#     Event.on(p.obs, "beforerequest", onBeforeRequest)
#     Pooling.start(p)
#     ...
#     Pooling.stop(p)
#
# @author DeadbraiN
#
module Pooling
  import Event
  import Connection
  import Client
  import Event

  export create
  export start
  export stop
  export EVENT_BEFORE_REQUEST
  export EVENT_AFTER_REQUEST
  #
  # Name of the events, which are fired before and after each request.
  #
  const EVENT_BEFORE_REQUEST = "before-request"
  const EVENT_AFTER_REQUEST = "after-response"
  #
  # Contains pooling data. Is used in all functions as
  # first parameter.
  #
  type PoolingData
    con::Client.ClientConnection
    obs::Event.Observer
    args::Array{Any, 1}
    resp::Function
    delay::Int

    PoolingData(con::Client.ClientConnection, obs::Event.Observer, args::Array{Any, 1}) = new(con, obs, args)
  end
  #
  # Creates connection with remote host. To start pooliing
  # call start() function.
  # @param host Remote host we are connecting to
  # @param port Remote port we are connecting to
  # @return {PoolingData|false}
  #
  function create(host::Base.IPAddr, port::Int)
    con = Client.create(host, port)
    Client.isOk(con) ? PoolingData(con, Event.create(), Array{Any, 1}()) : false
  end
  #
  # Start pooling process. It makes requests to remote server.
  # @param pd Pooling data object. See create()
  # @param delay Delay between requests
  #
  function start(pd::PoolingData, delay::Int)
    pd.delay = delay
    pd.resp  = (ans::Connection.Answer) -> _onResponse(pd, ans)

    Event.on(pd.con.observer, Client.EVENT_AFTER_REQUEST, pd.resp)
    #
    # Here user should fill pd.args parameter. These arguments
    # will be passed to the remote server with request.
    #
    Event.fire(pd.obs, EVENT_BEFORE_REQUEST, pd)
    Client.request(pd.con, pd.args...)
  end
  #
  # Stops pooling and closes connection.
  # @param pd Pooling data object
  #
  function stop(pd::PoolingData)
    Event.off(pd.con.observer, Client.EVENT_AFTER_REQUEST, pd.resp)
    Client.stop(pd.con)
  end
  #
  # Handler of server answer
  # @param pd remote data for specified server
  # @param ans Answer object with custom data
  #
  function _onResponse(pd::PoolingData, ans::Connection.Answer)
    #
    # Here we pass response answer outside the Pooling module
    #
    Event.fire(pd.obs, EVENT_AFTER_REQUEST, pd, ans)
    sleep(pd.delay)
    #
    # Here user should fill pd.args parameter. These arguments
    # will be passed to the remote server with request.
    #
    Event.fire(pd.obs, EVENT_BEFORE_REQUEST, pd)
    Client.request(pd.con, pd.args...)
  end
end

ext: jl

max_stars_repo_name: tmptrash/jevo

max_issues_repo_name: tmptrash/jevo

max_forks_repo_name: tmptrash/jevo


text:
export record_response_ecasound, measure_response, measurement_info, calibrate_response, compute_ir
export table_measurement
export postprocess_response, postprocess_dir

function record_response_ecasound(fstimulus, fresponse; duration=2s)
    durationsec = ustrip(Float32, u"s", duration)
    cmd = `ecasound -t:$durationsec -f:16,2,44100 -a:1 -i $fstimulus -o alsa,pulse -a:2 -i alsa,pulse -o $fresponse`
    run(pipeline(cmd, devnull))
    readwav(fresponse)
end

function measure_response(stimulus, fstimulus, fresponse; postduration=1s)
    #TODO fix API so stimulus is not always converted....
    xstimulus = SampleArray(stimulus.sig, stimulus.samplerate)
    duration = getduration(xstimulus)
    xresponse = record_response_ecasound(fstimulus, fresponse; duration=(duration + postduration))
    xresponse
end

function measurement_info(xresponse; c=344u"m/s")
    duration = uconvert(u"s", getduration(xresponse))
    tdiff_samples = DSP.finddelay(xresponse[:, 2:2], xresponse[:, 1:1]);
    tdiff = uconvert(u"ms", tdiff_samples/rateHz(xresponse))
    tdiff_distance = uconvert(u"mm", c * tdiff)
    return (duration=duration, tdiff=tdiff, tdiff_samples=tdiff_samples, tdiff_distance)
end

function calibrate_response(xresponse, calfile="../data/34Q956_cal_0degree.txt")
    # TODO make this more general, e.g., sound card calibration
    Xresponse = rfft(xresponse)
    Xresponse[:, 2:2] .= miccalibrate(Xresponse[:, 2:2], calfile)
    xresponse = irfft(Xresponse)
    xresponse, Xresponse
end

function compute_ir(stimulus, xresponse; noncausal=true)
    # find loopback delay caused by the processing
    # shift signals, keeping only the mic channel
    xstimulus = SampleArray(stimulus.sig, stimulus.samplerate)
    tdiff = DSP.finddelay(xstimulus, xresponse[:, 1:1])
    xresponse = DSP.shiftsignal(xresponse, tdiff)[:, 2:2]

    h = analyze(stimulus, xresponse, noncausal=noncausal)
    H = rfft(h)
    h, H
end

function table_measurement(stimulus, fstimulus, rawdir, tableAPI, degs=0:5:355;
        response_template="response_{deg}.wav",
        rate=44100Hz, duration=5s, postduration=1s, speed=0.1, dampingsleep=2s, c=344m/s)
    tableAPI("speed/$(speed)")
    mkpath(rawdir)
    iter = ProgressBar(degs)
    for deg in iter
        set_description(iter, "$(deg)°")
        tableAPI("rotateto/$(deg)")
        sleep(ustrip(Float32, u"s", dampingsleep)) # wait for damping vibrations
        fresponse = joinpath(rawdir, replace(response_template, "{deg}" => @sprintf("%03d", deg)))
        isfile(fresponse) && error("response file already exists: $(fresponse)!")

        xresponse = measure_response(stimulus, fstimulus, fresponse; postduration=postduration)
        minfo = measurement_info(xresponse; c=c)
        set_postfix(iter, Lag="$(minfo.tdiff_samples) spl")
        # @info "recorded $(nframes(xresponse)) samples ($(minfo.duration)); mic lags the loopback by $(minfo.tdiff_samples) samples ≈ $(minfo.tdiff) ≈ $(minfo.tdiff_distance)"
    end
end

function postprocess_response(infile, outdir, stimulus;
    response_template=r"response_(\d\d\d).wav",
    shift=0, # shift due to imprecise mic positioning
    calfile=nothing,
    ir_template="ir_{deg}.wav", noncausal=true)

    m = match(response_template, basename(infile))
    if !isnothing(m)
        mkpath(outdir)

        deg = parse(Int, m.captures[1])
        if deg < 0
            deg = 360 - deg
        end
        xresponse = readwav(infile)
        @assert nchannels(xresponse) == 2

        # shift the second channel which is the mic recording
        xresponse[:, 2:2] .= DSP.shiftsignal(xresponse[:, 2:2], shift)

        if !isnothing(calfile)
            xresponse, _ = calibrate_response(xresponse, calfile)
        end
        fimpulse = joinpath(outdir, replace(ir_template, "{deg}" => @sprintf("%03d", deg)))
        isfile(fimpulse) && error("impulse file already exists: $(fimpulse)!")

        h, _ = compute_ir(stimulus, xresponse; noncausal=noncausal)
        writewav(h, fimpulse; nbits=16, compression=WAVE_FORMAT_PCM)
        return true
    end
    return false
end

function postprocess_dir(indir, func)
    res = []
    iter = ProgressBar(readdir(indir))
    for f in iter
        set_description(iter, f)
        infile = joinpath(indir, f)
        if isfile(infile)
            push!(res, func(infile))
        end
    end
    @info "processed $(sum(res)) files"
end


text:
__precompile__(true)

module ImplicitEquations

using ValidatedNumerics

import ValidatedNumerics: Interval, diam
using RecipesBase
using Compat

include("predicates.jl")
include("intervals.jl")
include("tupper.jl")
include("asciigraphs.jl")
#include("plot.jl")
include("plot_recipe.jl")

export eq, neq, ⩵, ≷, ≶
export screen, I_
export asciigraph

#export GRAPH, OInterval
#export Region, compute, negate_op
#export TRUE, FALSE, MAYBE

end # module


text:
export KuhnPokerEnv

const KUHN_POKER_CARDS = (:J, :Q, :K)
const KUHN_POKER_CARD_COMBINATIONS =
    ((:J, :Q), (:J, :K), (:Q, :J), (:Q, :K), (:K, :J), (:K, :Q))
const KUHN_POKER_ACTIONS = (:pass, :bet)
const KUHN_POKER_STATES = (
    (),
    map(tuple, KUHN_POKER_CARDS)...,
    KUHN_POKER_CARD_COMBINATIONS...,
    (
        (cards..., actions...) for cards in ((), map(tuple, KUHN_POKER_CARDS)...) for
        actions in (
            (),
            (:bet,),
            (:bet, :bet),
            (:bet, :pass),
            (:pass,),
            (:pass, :pass),
            (:pass, :bet),
            (:pass, :bet, :pass),
            (:pass, :bet, :bet),
        )
    )...,
)

"""
![](https://upload.wikimedia.org/wikipedia/commons/a/a9/Kuhn_poker_tree.svg)
"""
const KUHN_POKER_REWARD_TABLE = Dict(
    (:J, :Q, :bet, :bet) => -2,
    (:J, :K, :bet, :bet) => -2,
    (:Q, :J, :bet, :bet) => 2,
    (:Q, :K, :bet, :bet) => -2,
    (:K, :J, :bet, :bet) => 2,
    (:K, :Q, :bet, :bet) => 2,
    (:J, :Q, :bet, :pass) => 1,
    (:J, :K, :bet, :pass) => 1,
    (:Q, :J, :bet, :pass) => 1,
    (:Q, :K, :bet, :pass) => 1,
    (:K, :J, :bet, :pass) => 1,
    (:K, :Q, :bet, :pass) => 1,
    (:J, :Q, :pass, :pass) => -1,
    (:J, :K, :pass, :pass) => -1,
    (:Q, :J, :pass, :pass) => 1,
    (:Q, :K, :pass, :pass) => -1,
    (:K, :J, :pass, :pass) => 1,
    (:K, :Q, :pass, :pass) => 1,
    (:J, :Q, :pass, :bet, :pass) => -1,
    (:J, :K, :pass, :bet, :pass) => -1,
    (:Q, :J, :pass, :bet, :pass) => -1,
    (:Q, :K, :pass, :bet, :pass) => -1,
    (:K, :J, :pass, :bet, :pass) => -1,
    (:K, :Q, :pass, :bet, :pass) => -1,
    (:J, :Q, :pass, :bet, :bet) => -2,
    (:J, :K, :pass, :bet, :bet) => -2,
    (:Q, :J, :pass, :bet, :bet) => 2,
    (:Q, :K, :pass, :bet, :bet) => -2,
    (:K, :J, :pass, :bet, :bet) => 2,
    (:K, :Q, :pass, :bet, :bet) => 2,
)

"""
    KuhnPokerEnv()

See more detailed description [here](https://en.wikipedia.org/wiki/Kuhn_poker).

Here we demonstrate how to write a typical [`ZERO_SUM`](@ref),
[`IMPERFECT_INFORMATION`](@ref) game. The implementation here has a explicit
[`CHANCE_PLAYER`](@ref).

TODO: add public state for [`SPECTOR`](@ref).
Ref: https://arxiv.org/abs/1906.11110
"""
struct KuhnPokerEnv <: AbstractEnv
    cards::Vector{Symbol}
    actions::Vector{Symbol}
end

KuhnPokerEnv() = KuhnPokerEnv(Symbol[], Symbol[])

function RLBase.reset!(env::KuhnPokerEnv)
    empty!(env.cards)
    empty!(env.actions)
end

RLBase.is_terminated(env::KuhnPokerEnv) =
    length(env.actions) == 2 && (env.actions[1] == :bet || env.actions[2] == :pass) ||
    length(env.actions) == 3
RLBase.players(env::KuhnPokerEnv) = (1, 2, CHANCE_PLAYER)

function RLBase.state(env::KuhnPokerEnv, ::InformationSet{Tuple{Vararg{Symbol}}}, p::Int)
    if length(env.cards) >= p
        (env.cards[p], env.actions...)
    else
        ()
    end
end

RLBase.state(env::KuhnPokerEnv, ::InformationSet{Tuple{Vararg{Symbol}}}, ::ChancePlayer) =
    Tuple(env.cards)
RLBase.state_space(env::KuhnPokerEnv, ::InformationSet{Tuple{Vararg{Symbol}}}, p) =
    KUHN_POKER_STATES

RLBase.action_space(env::KuhnPokerEnv, ::Int) = Base.OneTo(length(KUHN_POKER_ACTIONS))
RLBase.action_space(env::KuhnPokerEnv, ::ChancePlayer) =
    Base.OneTo(length(KUHN_POKER_CARDS))

RLBase.legal_action_space(env::KuhnPokerEnv, p::ChancePlayer) =
    [x for x in action_space(env, p) if KUHN_POKER_CARDS[x] ∉ env.cards]

function RLBase.legal_action_space_mask(env::KuhnPokerEnv, p::ChancePlayer)
    m = fill(true, 3)
    m[env.cards] .= false
    m
end

function RLBase.prob(env::KuhnPokerEnv, ::ChancePlayer)
    if length(env.cards) == 0
        fill(1 / 3, 3)
    elseif length(env.cards) == 1
        p = fill(1 / 2, 3)
        i = findfirst(==(env.cards[1]), KUHN_POKER_CARDS)
        p[i] = 0
        p
    else
        @error "it's not chance player's turn!"
    end
end

(env::KuhnPokerEnv)(action::Int, p::Int) = env(KUHN_POKER_ACTIONS[action], p)
(env::KuhnPokerEnv)(action::Int, p::ChancePlayer) = env(KUHN_POKER_CARDS[action], p)
(env::KuhnPokerEnv)(action::Symbol, ::ChancePlayer) = push!(env.cards, action)
(env::KuhnPokerEnv)(action::Symbol, ::Int) = push!(env.actions, action)

RLBase.reward(::KuhnPokerEnv, ::ChancePlayer) = 0

function RLBase.reward(env::KuhnPokerEnv, p)
    if is_terminated(env)
        v = KUHN_POKER_REWARD_TABLE[(env.cards..., env.actions...)]
        p == 1 ? v : -v
    else
        0
    end
end

RLBase.current_player(env::KuhnPokerEnv) =
    if length(env.cards) < 2
        CHANCE_PLAYER
    elseif length(env.actions) == 0
        1
    elseif length(env.actions) == 1
        2
    elseif length(env.actions) == 2
        1
    else
        2  # actually the game is over now
    end

RLBase.NumAgentStyle(::KuhnPokerEnv) = MultiAgent(2)
RLBase.DynamicStyle(::KuhnPokerEnv) = SEQUENTIAL
RLBase.ActionStyle(::KuhnPokerEnv) = MINIMAL_ACTION_SET
RLBase.InformationStyle(::KuhnPokerEnv) = IMPERFECT_INFORMATION
RLBase.StateStyle(::KuhnPokerEnv) = InformationSet{Tuple{Vararg{Symbol}}}()
RLBase.RewardStyle(::KuhnPokerEnv) = TERMINAL_REWARD
RLBase.UtilityStyle(::KuhnPokerEnv) = ZERO_SUM
RLBase.ChanceStyle(::KuhnPokerEnv) = EXPLICIT_STOCHASTIC


text:
export ZabbixServer, get_server

struct ZabbixServer
  templates::Vector{ZabbixTemplate}
  hostgroups::Vector{ZabbixHostgroup}
  hosts::Vector{ZabbixHost}
end

function showres(s::ZabbixServer; offset=0)
  println("ZabbixServer:")
  for t in s.templates
    showres(t; offset=offset+1)
  end
  for h in s.hostgroups
    showres(h; offset=offset+1)
  end
  for h in s.hosts
    showres(h; offset=offset+1)
  end
end

function get_server(z)
  # Templates
  templates = get_templates(z)

  # Host groups
  hostgroups = get_hostgroups(z)

  # Hosts
  hosts = get_hosts(z)
  for host in hosts
    # Items
    items = get_items(z, host)

    # Triggers
    triggers = get_triggers(z, host)

    break
  end

  # Users
  users = get_users(z)
  @showres users
  exit()

  ZabbixServer(
    templates,
    hostgroups,
    hosts,
  )
end


text:
#
# Factors Map Tests
#
# log, fill, abs, etc

@testset "Factors Map" begin

@testset "normalize" begin
    @testset "1" begin
        ϕ = Factor([:A, :B], [1 2; 3 4])
        ϕ2 = normalize(ϕ, p=1)

        @test_approx_eq ϕ2.potential [0.1 0.2; 0.3 0.4]
        @test ϕ.potential == [1 2; 3 4]

        @test_throws ArgumentError normalize(ϕ, :waldo)

        normalize!(ϕ, p=2)
        @test_approx_eq ϕ.potential [1 2; 3 4]./sqrt(30)
    end

    @testset "2" begin
        ϕ = Factor(-30)
        normalize!(ϕ)
        @test ϕ.potential == reshape([-1.0], ())

        ϕ = Factor(:A, [2, 3])
        normalize!(ϕ, p=1)
        @test_approx_eq ϕ.potential [0.4, 0.6]
    end

    @testset "3" begin
        ϕ = Factor(1:24, :X=>3, :Y=>2, :Z=>4)
        ϕ2 = normalize(ϕ, :Y)
        @test_approx_eq sum(ϕ2.potential, 2) fill(1.0, (3, 1, 4))

        ϕ3 = normalize(ϕ, [:X, :Z])
        @test_approx_eq sum(ϕ3.potential, [1, 3]) fill(1.0 ,(1, 2, 1))
    end
end

@testset "map" begin
    @testset "1" begin
        ϕ = Factor(1:16, :X=>4, :Y=>4)
        ϕ2 = map(x -> x+10, ϕ)
        @test ϕ.potential + 10 == ϕ2.potential

        map!(x-> 2*x - 10, ϕ)
        @test ϕ.potential == 2*reshape(1:16, (4,4) ) - 10
    end

    @testset "2" begin
        ϕ = Factor(:X, 1:4)
        map!(x -> x + 2, ϕ)
        @test ϕ.potential == [3, 4, 5, 6]
    end

    @testset "3" begin
        ϕ = Factor(16)
        ϕ2 = map(log10, ϕ)
        @test ϕ.potential == reshape([16], ())
        @test ϕ2.potential == reshape([log10(16)], ())
    end
end

@testset "rand!" begin
    @testset "1" begin
        ϕ = Factor(16, :X=>3, :Y=>4)
        rand!(ϕ)
        @test ϕ.potential != 16
    end

    @testset "2" begin
        ϕ = Factor(3)
        randn!(ϕ)
        @test ϕ.potential != 3
    end
end

@testset "fill!" begin
    @testset "1" begin
        ϕ = Factor(16, :X=>3, :Y=>4)
        fill!(ϕ, 2016)
        @test all(ϕ.potential .== 2016)
    end

    @testset "2" begin
        ϕ = Factor(3)
        fill!(ϕ, 19)
        @test ϕ.potential == reshape([19], ())
    end
end

@testset "log, exp, etc." begin
    @testset "1" begin
        ϕ = Factor(:X=>3, :Y=>4)
        rand!(ϕ)

        @test_approx_eq ϕ.potential exp(log(ϕ)).potential
        @test_approx_eq ϕ.potential log10(exp10(ϕ)).potential
    end

    @testset "2" begin
        ϕ = Factor(58)
        @test_approx_eq log2(ϕ).potential log2(58)
        @test_approx_eq secd(ϕ).potential secd(58)
    end
end

end


text:
# A FileRaft represents all the variables stored inside some file of type T
type FileRaft{T} <: Raft
    path::UTF8String  # Path to the file itself
    group::UTF8String # For HDF5-like sub-groupings within the file
    file::T

    parent::Raft
    children::Dict{UTF8String, Raft}
end

# A DataRaft represents exactly one stored variable inside a FileRaft
type DataRaft{T<:FileRaft} <: Raft
    name::UTF8String
    desc::UTF8String

    parent::T
end


text:
immutable Frenet
    roadind::RoadIndex
    s::Float64 # distance along lane
    t::Float64 # lane offset, positive is to left
    ϕ::Float64 # lane relative heading
end
function Frenet(roadind::RoadIndex, roadway::Roadway; t::Float64=0.0, ϕ::Float64=0.0)
    s = roadway[roadind].s
    ϕ = _mod2pi2(ϕ)
    Frenet(roadind, s, t, ϕ)
end
function Frenet(roadproj::RoadProjection, roadway::Roadway)
    roadind = RoadIndex(roadproj.curveproj.ind, roadproj.tag)
    s = roadway[roadind].s
    t = roadproj.curveproj.t
    ϕ = _mod2pi2(roadproj.curveproj.ϕ)
    Frenet(roadind, s, t, ϕ)
end
Frenet(posG::VecSE2, roadway::Roadway) = Frenet(proj(posG, roadway), roadway)

function get_posG(frenet::Frenet, roadway::Roadway)
    curvept = roadway[frenet.roadind]
    pos = curvept.pos + polar(frenet.t, curvept.pos.θ + π/2)
    VecSE2(pos.x, pos.y, frenet.ϕ + curvept.pos.θ)
end

const NULL_FRENET = Frenet(NULL_ROADINDEX, NaN, NaN, NaN)

Base.show(io::IO, frenet::Frenet) = print(io, "Frenet(", frenet.roadind, @sprintf(", %.3f, %.3f, %.3f)", frenet.s, frenet.t, frenet.ϕ))
function Base.isapprox(a::Frenet, b::Frenet;
    rtol::Real=cbrt(eps(Float64)),
    atol::Real=sqrt(eps(Float64))
    )

    a.roadind.tag == b.roadind.tag &&
    isapprox(a.roadind.ind.t, b.roadind.ind.t, atol=atol, rtol=rtol) &&
    isapprox(a.s, b.s, atol=atol, rtol=rtol) &&
    isapprox(a.t, b.t, atol=atol, rtol=rtol) &&
    isapprox(a.ϕ, b.ϕ, atol=atol, rtol=rtol)
end

immutable VehicleState
    posG::VecSE2 # global
    posF::Frenet # lane-relative frame
    v::Float64

    VehicleState() = new(VecSE2(), NULL_FRENET, NaN)
    VehicleState(posG::VecSE2, v::Float64) = new(posG, NULL_FRENET, v)
    VehicleState(posG::VecSE2, posF::Frenet, v::Float64) = new(posG, posF, v)
    VehicleState(posG::VecSE2, roadway::Roadway, v::Float64) = new(posG, Frenet(posG, roadway), v)
    VehicleState(posF::Frenet, roadway::Roadway, v::Float64) = new(get_posG(posF, roadway), posF, v)
end
Base.show(io::IO, s::VehicleState) = print(io, "VehicleState(", s.posG, ", ", s.posF, ", ", @sprintf("%.3f", s.v), ")")
function Vec.lerp(a::VehicleState, b::VehicleState, t::Float64, roadway::Roadway)
    posG = lerp(a.posG, b.posG, t)
    v = lerp(a.v, b.v, t)
    VehicleState(posG, roadway, v)
end

baremodule AgentClass
    const MOTORCYCLE = 1
    const CAR        = 2
    const TRUCK      = 3
end

immutable VehicleDef
    id::Int
    class::Int # ∈ AgentClass
    length::Float64
    width::Float64
end
function VehicleDef(id::Int;
    class::Int=AgentClass.CAR,
    length::Float64=4.0,
    width::Float64=1.8,
    )

    VehicleDef(id, class, length, width)
end

const NULL_VEHICLEDEF = VehicleDef(0, AgentClass.CAR, NaN, NaN)
Base.show(io::IO, d::VehicleDef) = @printf(io, "VehicleDef(%d, %s, %.3f, %.3f)", d.id, d.class == AgentClass.CAR ? "CAR" : d.class == AgentClass.MOTORCYCLE ? "MOTORCYCLE" : "TRUCK", d.length, d.width)

type Vehicle
    state::VehicleState # position is at the center
    def::VehicleDef

    function Vehicle(
        state::VehicleState=VehicleState(),
        def::VehicleDef=NULL_VEHICLEDEF,
        )
        new(state,def)
    end
end
Base.show(io::IO, v::Vehicle) = print(io, "Vehicle(", v.state, ", ", v.def, ")")
function Base.copy!(veh1::Vehicle, veh2::Vehicle)
    veh1.state = veh2.state
    veh1.def = veh2.def
    veh1
end

get_vel_s(s::VehicleState) = s.v * cos(s.posF.ϕ) # velocity along the lane
get_vel_t(s::VehicleState) = s.v * sin(s.posF.ϕ) # velocity ⟂ to lane

get_center(veh::Vehicle) = veh.state.posG
get_footpoint(veh::Vehicle) = veh.state.posG + polar(veh.state.posF.t, veh.state.posG.θ-veh.state.posF.ϕ-π/2)
get_front_center(veh::Vehicle) = veh.state.posG + polar(veh.def.length/2, veh.state.posG.θ)
get_rear_center(veh::Vehicle) = veh.state.posG - polar(veh.def.length/2, veh.state.posG.θ)

function move_along(vehstate::VehicleState, roadway::Roadway, Δs::Float64;
    ϕ₂::Float64=vehstate.posF.ϕ, t₂::Float64=vehstate.posF.t, v₂::Float64=vehstate.v
    )

    roadind = move_along(vehstate.posF.roadind, roadway, Δs)
    try
        footpoint = roadway[roadind]
    catch
        println(roadind)
    end
    footpoint = roadway[roadind]
    posG = convert(VecE2, footpoint.pos) + polar(t₂, footpoint.pos.θ + π/2)
    posG = VecSE2(posG.x, posG.y, footpoint.pos.θ + ϕ₂)
    VehicleState(posG, roadway, v₂)
end


text:
using Images: mapwindow, Fill

function part1(input)
    grid = foldl(hcat, read_line.(eachline(input)))
    for i = 1:100
        grid = step(grid)
    end
    return sum(grid)
end

function part2(input)
    grid = foldl(hcat, read_line.(eachline(input)))
    grid[[1, end], [1, end]] .= 1
    for i = 1:100
        grid = step(grid)
        grid[[1, end], [1, end]] .= 1
    end
    return sum(grid)
end

read_line(line) = Int.(collect(line) .== '#')

function step(grid)
    return Int.(5 .<= 2 .* mapwindow(sum, grid, (3, 3),
                                     border = Fill(0)) .- grid .<= 7)
end


text:
function KS_solve_Emin_cg( pw::PWGrid, V_ionic, Focc, Nstates::Int;
                           psi0=nothing, Potentials0 = nothing,
                           α_t = 3e-5, NiterMax=1000, verbose=false )

    Ns = pw.Ns
    Npoints = prod(Ns)

    if psi0 == nothing
        psi = rand(ComplexF64,Npoints,Nstates)
        psi = ortho_gram_schmidt(psi)
    else
        psi = copy(psi0)
    end

    if Potentials0 == nothing
        Potentials = PotentialsT( V_ionic, zeros(Npoints), zeros(Npoints) )
        rho = calc_rho( pw, Focc, psi )
        Potentials.Hartree = real( G_to_R( Ns, Poisson_solve(pw, rho) ) )
        Potentials.XC = excVWN( rho ) + rho .* excpVWN( rho )
    else
        Potentials = PotentialsT( Potentials0.Ionic,
                                  Potentials0.Hartree,
                                  Potentials0.XC )
    end

    d      = zeros(ComplexF64, Npoints, Nstates)
    g_old  = zeros(ComplexF64, Npoints, Nstates)
    d_old  = zeros(ComplexF64, Npoints, Nstates)
    Kg     = zeros(ComplexF64, Npoints, Nstates)
    Kg_old = zeros(ComplexF64, Npoints, Nstates)

    yk1 = zeros(ComplexF64, Npoints, Nstates) # for testing Hager-Zhang beta
    v1 = zeros(ComplexF64, Npoints, Nstates)

    β     = 0.0
    Etot_old = 0.0
    Etot     = 0.0
    Energies = EnergiesT( 0.0, 0.0, 0.0, 0.0, 0.0 )

    CONVERGED = 0

    for iter = 1:NiterMax

        g = calc_grad( pw, Potentials, Focc, psi)
        Kg = Kprec(pw,g)
        if iter != 1
            #β = real(sum(conj(g).*Kg))/real(sum(conj(g_old).*Kg_old))
            #β = real(sum(conj(g-g_old).*Kg))/real(sum(conj(g_old).*Kg_old))
            #β = real(sum(conj(g-g_old).*Kg))/real(sum(conj(g-g_old).*d))
            β = real(sum(conj(g).*Kg))/real(sum((g-g_old).*conj(d_old)))

            #yk1 = g - g_old
            #tt = real(tr(d_old'*yk1))
            #v1 = yk1 - 2*d_old*norm(yk1)/tt
            #β = real(tr(v1'*Kg)/tt)

            #@printf("\nβ = %f\n", β)
        end

        if β < 0.0
            println("Encountered β less than 0 setting it to zero")
            β = 0.0
        end
        #
        d = -Kg + β * d_old
        #
        psic = ortho_gram_schmidt(psi + α_t*d)
        rho = calc_rho( pw, Focc, psic )
        Potentials.Hartree = real( G_to_R( Ns, Poisson_solve(pw, rho) ) )
        Potentials.XC = excVWN( rho ) + rho .* excpVWN( rho )
        gt = calc_grad( pw, Potentials, Focc, psic )

        denum = real(sum(conj(g-gt).*d))
        if denum != 0.0
            α = abs( α_t*real(sum(conj(g).*d))/denum )
        else
            α = 0.0
        end

        # Update wavefunction
        psi = psi[:,:] + α*d[:,:]

        # Update potentials
        psi = ortho_gram_schmidt(psi)
        rho = calc_rho( pw, Focc, psi )
        Potentials.Hartree = real( G_to_R( Ns, Poisson_solve(pw, rho) ) )
        Potentials.XC = excVWN( rho ) + rho .* excpVWN( rho )

        Energies = calc_Energies( pw, Potentials, Focc, psi )
        Etot = Energies.Total

        diff = abs(Etot-Etot_old)
        @printf("Emin CG: %8d = %18.10f %18.10f\n", iter, Etot, diff)

        if diff < 1e-6
            CONVERGED = CONVERGED + 1
        else
            CONVERGED = 0
        end

        if CONVERGED >= 2
            @printf("CONVERGENCE ACHIEVED\n")
            break
        end

        g_old = copy(g)
        d_old = copy(d)
        Kg_old = copy(Kg)
        Etot_old = Etot
    end
    return psi, Energies, Potentials
    #
end


text:
"""
    CosineKernel(; metric=Euclidean())

Cosine kernel with respect to the `metric`.

# Definition

For inputs ``x, x'`` and metric ``d(\\cdot, \\cdot)``, the cosine kernel is defined as
```math
k(x, x') = \\cos(\\pi d(x, x')).
```
By default, ``d`` is the Euclidean metric ``d(x, x') = \\|x - x'\\|_2``.
"""
struct CosineKernel{M} <: SimpleKernel
    metric::M
end

CosineKernel(; metric=Euclidean()) = CosineKernel(metric)

kappa(::CosineKernel, d::Real) = cospi(d)

metric(k::CosineKernel) = k.metric

Base.show(io::IO, k::CosineKernel) = print(io, "Cosine Kernel (metric = ", k.metric, ")")


text:
# BEGIN 0.9 deprecations

@deprecate warp(img::AbstractArray, tform::Transformation,       method::MethodType,                      ) warp(img, tform; method=method)
@deprecate warp(img::AbstractArray, tform::Transformation,                             fillvalue::FillType) warp(img, tform; fillvalue=fillvalue)
@deprecate warp(img::AbstractArray, tform::Transformation,       method::MethodType,   fillvalue::FillType) warp(img, tform; method=method, fillvalue=fillvalue)
@deprecate warp(img::AbstractArray, tform::Transformation,       fillvalue::FillType,   method::MethodType) warp(img, tform; method=method, fillvalue=fillvalue)
@deprecate warp(img::AbstractArray, tform::Transformation, inds, method::MethodType,                      ) warp(img, tform, inds; method=method)
@deprecate warp(img::AbstractArray, tform::Transformation, inds,                       fillvalue::FillType) warp(img, tform, inds; fillvalue=fillvalue)
@deprecate warp(img::AbstractArray, tform::Transformation, inds, method::MethodType,   fillvalue::FillType) warp(img, tform, inds; method=method, fillvalue=fillvalue)
@deprecate warp(img::AbstractArray, tform::Transformation, inds, fillvalue::FillType,   method::MethodType) warp(img, tform, inds; method=method, fillvalue=fillvalue)

@deprecate imrotate(img::AbstractArray, θ::Real,       method::MethodType                     )  imrotate(img, θ;       method=method)
@deprecate imrotate(img::AbstractArray, θ::Real,                           fillvalue::FillType)  imrotate(img, θ;       fillvalue=fillvalue)
@deprecate imrotate(img::AbstractArray, θ::Real,       method::MethodType, fillvalue::FillType)  imrotate(img, θ; method=method, fillvalue=fillvalue)
@deprecate imrotate(img::AbstractArray, θ::Real,       fillvalue::FillType, method::MethodType)  imrotate(img, θ; method=method, fillvalue=fillvalue)
@deprecate imrotate(img::AbstractArray, θ::Real, inds, method::MethodType                     )  imrotate(img, θ, inds; method=method)
@deprecate imrotate(img::AbstractArray, θ::Real, inds,                     fillvalue::FillType)  imrotate(img, θ, inds; fillvalue=fillvalue)
@deprecate imrotate(img::AbstractArray, θ::Real, inds, method::MethodType, fillvalue::FillType)  imrotate(img, θ, inds; method=method, fillvalue=fillvalue)
@deprecate imrotate(img::AbstractArray, θ::Real, inds, fillvalue::FillType, method::MethodType)  imrotate(img, θ, inds; method=method, fillvalue=fillvalue)

@deprecate WarpedView(img::AbstractArray, tform::Transformation,       method::MethodType,                      ) WarpedView(img, tform; method=method)
@deprecate WarpedView(img::AbstractArray, tform::Transformation,                             fillvalue::FillType) WarpedView(img, tform; fillvalue=fillvalue)
@deprecate WarpedView(img::AbstractArray, tform::Transformation,       method::MethodType,   fillvalue::FillType) WarpedView(img, tform; method=method, fillvalue=fillvalue)
@deprecate WarpedView(img::AbstractArray, tform::Transformation,       fillvalue::FillType,   method::MethodType) WarpedView(img, tform; method=method, fillvalue=fillvalue)
@deprecate WarpedView(img::AbstractArray, tform::Transformation, inds, method::MethodType,                      ) WarpedView(img, tform, inds; method=method)
@deprecate WarpedView(img::AbstractArray, tform::Transformation, inds,                       fillvalue::FillType) WarpedView(img, tform, inds; fillvalue=fillvalue)
@deprecate WarpedView(img::AbstractArray, tform::Transformation, inds, method::MethodType,   fillvalue::FillType) WarpedView(img, tform, inds; method=method, fillvalue=fillvalue)
@deprecate WarpedView(img::AbstractArray, tform::Transformation, inds, fillvalue::FillType,   method::MethodType) WarpedView(img, tform, inds; method=method, fillvalue=fillvalue)

@deprecate warpedview(args...; kwargs...) WarpedView(args...; kwargs...)

@deprecate invwarpedview(img::AbstractArray, tinv::Transformation,       method::MethodType,                      ) invwarpedview(img, tinv; method=method)
@deprecate invwarpedview(img::AbstractArray, tinv::Transformation,                             fillvalue::FillType) invwarpedview(img, tinv; fillvalue=fillvalue)
@deprecate invwarpedview(img::AbstractArray, tinv::Transformation,       method::MethodType,   fillvalue::FillType) invwarpedview(img, tinv; method=method, fillvalue=fillvalue)
@deprecate invwarpedview(img::AbstractArray, tinv::Transformation,       fillvalue::FillType,   method::MethodType) invwarpedview(img, tinv; method=method, fillvalue=fillvalue)
@deprecate invwarpedview(img::AbstractArray, tinv::Transformation, inds, method::MethodType,                      ) invwarpedview(img, tinv, inds; method=method)
@deprecate invwarpedview(img::AbstractArray, tinv::Transformation, inds,                       fillvalue::FillType) invwarpedview(img, tinv, inds; fillvalue=fillvalue)
@deprecate invwarpedview(img::AbstractArray, tinv::Transformation, inds, method::MethodType,   fillvalue::FillType) invwarpedview(img, tinv, inds; method=method, fillvalue=fillvalue)
@deprecate invwarpedview(img::AbstractArray, tinv::Transformation, inds, fillvalue::FillType,   method::MethodType) invwarpedview(img, tinv, inds; method=method, fillvalue=fillvalue)

# END 0.9 deprecations


text:
#=

Here's how Base works:
1. show(x) = show(stdout, x)
2. show(io, x) is meant for plain text display
3. customize show(io, mime, x) for other mime types.
    show(io, MIME"text/plain", x) calls show(io, x)

4. print(x) = print(stdout, x)
5. print(io, x) falls back to show(io, MIME"text/plain", x), hence to show(io, x)

6. string(x) defaults to print(Buffer, x)

Current design:

There is one context parameter, :depth, which indicates the level of nested structure.

At depth 0:
    - The quantity is shown in "long" form, possibly multi-line
    - If the formatting of the data does not indicate the type, the type and metadata
        (e.g. array size) are explicitly shown at depth 1
    - Field names are shown
    - Constituent values are shown at level 1
For 1 <= depth < structdepth:
    - The quantity is shown in compact form, generally single-line
    - Datatypes are shown as type_name(field_values...)
    - Contained values are shown at depth+1
For depth == maxdepth:
    - Primitive quantities are shown
    - Composite quantities are shown as type{...}(...)

There are currently two different values of maxdepth, one for values and one for types
=#

# TODO:  Continue Implement showing of Arrays
#   - This will not be simple; there is a lot of infrastructure in place.
#   - Note, some deep methods in Base resort to string(T), which loses context
#   - need to fine tune choosing how many rows of matrices to show
#   - figure something clever for ND-s
#
# TODO:  Improve showing of UnionAlls
#   - When possible, abbreviate "A{N} where N<:T" as "A{<:T}"
#       - N must not appears as a bound on any other typevars
#   - Don't show "where N" if N only appears at a depth that is not shown

module ShowNice

export test_show

#import Base: show
export shownice
using Base: print_without_params, show_datatype, io_has_tvar_name, uniontypes, has_typevar, unwrap_unionall, print_without_params, show_circular

function test_show()

t = (2, 'a', 3.1416)

shownice(t)
println()
shownice(typeof(t))
println()

nt = (name = "Mary", age = 20)
shownice(nt)
println()
shownice(typeof(nt))
println()

end

#-------------------------------------------------------------
## Infrastructure

global structdepth = 2          # the maximum depth of nested structure levels to show
global typedepth = 4            # the maximum depth of nested types to show

# Set the maximum number of structural levels to show
function structdepth!(d::Int)
    global structdepth
    if d>=0
        structdepth = d
    else
        error("Depth must be a non-negative integer")
    end
    nothing
end

function typedepth!(d::Int)
    global typedepth
    if d>=0
        typedepth = d
    else
        error("Depth must be a non-negative integer")
    end
    nothing
end

depth(io::IO) = get(io, :depth, 0)

maxdepth(io::IO) =  get(io, :istype, false) ? typedepth : structdepth

function deeper(io::IO)
    depth(io) == 0 ?
        IOContext(io, :depth => get(io, :depth, 0) + 1) :
        IOContext(io, :compact => true, :depth => get(io, :depth, 0) + 1)
end
#--------------------------------------------------------------
## Fallback

shownice(x) = shownice(stdout, x)

# shownice(io, x) = show(io, x)

#-------------------------------------------------------------
## show types

# Somes types (e.g. Union, UnionAll, Tuple) are actually instances of DataType

function shownice(io::IO, @nospecialize t::Type)
    depth(io) == 0 && print(io, "Type ")
    show_type(IOContext(io, :istype => true), t)
end

# Show with surrounding parentheses if type is a UnionAll
function show_protected(io::IO, @nospecialize t)
    if isa(t, UnionAll)
        print(io, '(')
        shownice(io, t)
        print(io, ')')
    else
        shownice(io, t)
    end
end

# function show_type(io::IO, t)
#     @warn "Unmatched method for type"
#     Base.show_type_name(io, t.name)
# end

# Default display of a type:  type_name{parameters}
#show_type(io::IO, @nospecialize t) = show_type_default(io, t)
function show_type(io::IO, @nospecialize t)
    n = length(t.parameters)::Int

    # Print homogeneous tuples with >2 elements compactly as NTuple{N, T}
    if isa(t, Tuple) && n > 2 && all(i -> (t.parameters[1] === i), t.parameters)
        if depth(io) >= maxdepth(io)
            print(io, "NTuple{…}")
        else
            print(io, "NTuple{", n, ',', t.parameters[1], "}")
        end
        return
    end

    # Otherwise, display type name and parameters
    Base.show_type_name(io, t.name)

    n = length(t.parameters)::Int
    n==0 && return

    if depth(io) >= maxdepth(io)
        print(io, "{…}")
    else
        print(io, '{')
        for (i, p) in enumerate(t.parameters)
            show(deeper(io), p)
            i < n && print(io, ',')
        end
        print(io, '}')
    end
end

# OOPS!  NTuple will dispatch to ::Type{<:Tuple}, even though it is a UnionAll.

function show_type(io::IO, ::Type{Base.Bottom})
    print(io, "Union{}")
end

function show_type(io::IO, @nospecialize t::Union)
    print(io, "Union")

    utypes = uniontypes(t)
    if depth(io) >= maxdepth(io)
        isempty(utypes) || print(io, "{…}")
    else
        Base.show_delim_array(deeper(io), utypes, '{', ',', '}', false)
    end
end

function show_type(io::IO, @nospecialize t::UnionAll)
    #@info "show(UnionAll) over $(t.var)"
    if depth(io) >= maxdepth(io)
        ut = unwrap_unionall(t)
        Base.show_type_name(io, ut.name)
        print(io, "{⋰}")
    else
        # Determine unbound type parameters.
        # In a nested UnionAll, IO will contain names of typevars already identified.
        if t.var.name === :_ || io_has_tvar_name(io, t.var.name, t)
            counter = 1
            while true
                newname = Symbol(t.var.name, counter)
                if !io_has_tvar_name(io, newname, t)
                    newtv = TypeVar(newname, t.var.lb, t.var.ub)
                    t = UnionAll(newtv, t{newtv})
                    break
                end
                counter += 1
            end
        end

        # We don't need to worry about printing "type" here.
        # When the UnionAll is fully unwrapped, we will get a "regular" type that will
        # print "type" if appropriate.
        # Show the UnionAll recursively
        show_type(IOContext(io, :unionall_env => t.var), t.body)
        print(io, " where ")
        show(deeper(io), t.var)
    end
    nothing
end

function shownice(io::IO, tv::TypeVar)
    print(io, "TypeVar ")
    show(io, tv)
end
# # TODO:  When _show is renamed to show, this method can be eliminated
# function _show(io::IO, tv::TypeVar)
#     in_env = (:unionall_env => tv) in io
#     function show_bound(io::IO, @nospecialize(b))
#         parens = isa(b,UnionAll) && !print_without_params(b) && depth(io) < typedepth
#         parens && print(io, "(")
#         _show(io, b)        #  the only difference from Base
#         parens && print(io, ")")
#     end
#     lb, ub = tv.lb, tv.ub
#     if !in_env && lb !== Union{}
#         if ub === Any
#             write(io, tv.name)
#             print(io, ">:")
#             show_bound(io, lb)
#         else
#             show_bound(io, lb)
#             print(io, "<:")
#             write(io, tv.name)
#         end
#     else
#         write(io, tv.name)
#     end
#     if !in_env && ub !== Any
#         print(io, "<:")
#         show_bound(io, ub)
#     end
#     nothing
# end

#-----------------------------------------------------------------
## Show values

# Can be eliminated
# _show(io::IO, @nospecialize(x::String)) = show(io, x)

# function show(io::IO, @nospecialize(x))
#     if isstructtype(typeof(x))
#         show_struct(io, x)
#     else
#         show(io, x)
#     end
# end

# Default (e.g. structs)
function shownice(io::IO, @nospecialize x)
    t = typeof(x)
    nf = nfields(x)

    # show primitive types using built-in methods
    if nf ==0
        show(io, x)
        return
     end

    # show structure types
    if depth(io) >= maxdepth(io)
        # show no structure
        shownice(deeper(io), t)
        nf == 0 ? print(io, "()") : print(io, "(…)")
    elseif depth(io) == 0
        # Show nested structure
        shownice(deeper(io), t)
        println(io, ":")
        if nf != 0
            if !show_circular(io, x)
                recur_io = IOContext(deeper(io), Pair{Symbol,Any}(:SHOWN_SET, x),
                                     Pair{Symbol,Any}(:typeinfo, Any))
                for i in 1:nf
                    fname = fieldname(t, i)
                    print(io, "   ", fname, " = ")
                    if !isdefined(x, fname)
                        print(io, undef_ref_str)
                    else
                        shownice(recur_io, getfield(x, i))
                        println(io)
                    end
                end
            end
        end
    else  # depth > 0
        # Show in compact form
        shownice(deeper(io), t)        # show just t.name?
        print(io, '(')
        if nf != 0
            if !show_circular(io, x)
                recur_io = IOContext(deeper(io), Pair{Symbol,Any}(:SHOWN_SET, x),
                                     Pair{Symbol,Any}(:typeinfo, Any))
                for i in 1:nf
                    f = fieldname(t, i)
                    if !isdefined(x, f)
                        print(io, undef_ref_str)
                    else
                        shownice(recur_io, getfield(x, i))
                    end
                    if i < nf
                        print(io, ", ")
                    end
                end
            end
        end
        print(io,')')
    end
end

function shownice(io::IO, @nospecialize(x::Tuple))
    nf = length(x)
    if depth(io) >= maxdepth(io)
        # show no structure
        nf == 0 ? print(io, "()") : print(io, "(…)")
    else
        print(io, '(')
        if nf != 0
            if !show_circular(io, x)
                recur_io = IOContext(deeper(io), Pair{Symbol,Any}(:SHOWN_SET, x),
                                     Pair{Symbol,Any}(:typeinfo, Any))
                for i in 1:nf
                    if !isdefined(x, i)
                        print(io, undef_ref_str)
                    else
                        shownice(recur_io, getfield(x, i))
                    end
                    if i < nf
                        print(io, ", ")
                    elseif nf == 1
                        print(io, ',')
                    end
                end
            end
        end
        print(io,')')
    end
end

# Don't show type of NamedTUples
function shownice(io::IO, @nospecialize t::NamedTuple)
    n = nfields(t)
    if depth(io) >= maxdepth(io)
        # show no structure
        n == 0 ? print(io, "(;)") : print(io, "(;…)")
    else
        if n == 0
            print(io, "(;)")
        else
            #typeinfo = get(io, :typeinfo, Any)
            print(io, "(")
            for i = 1:n
                print(io, fieldname(typeof(t),i), " = ")
                # show(IOContext(io, :typeinfo =>
                # t isa typeinfo <: NamedTuple ? fieldtype(typeinfo, i) : Any),
                # getfield(t, i))
                shownice(deeper(io), getfield(t, i))
                if n == 1
                    print(io, ",")      # add a comma to indicate it's a tuple
                elseif i < n
                    print(io, ", ")
                end
            end
            print(io, ")")
        end
    end
end

size2string(d) = isempty(d) ? "0-dim" :
                 length(d) == 1 ? "$(d[1])×" :
                 join(map(string,d), '×')

function shownice(io::IO, A::AbstractArray)
    if depth(io) == 0
        # print as usual
        print(io, size2string(size(A)), " ")
        shownice(deeper(io), typeof(A))
        # show indices?

        if !isempty(A)
            println(io, ':')
            # TODO:  This still shows element types, make it not
            Base.print_array(IOContext(io, :typeinfo => eltype(A)), A)
        end

    # show summary only if:
    #  we're at maxdepth, OR
    #  we're at maxdepth-1 and nothing useful would be shown about its elements
    elseif depth(io) >= maxdepth(io) || depth(io) == maxdepth(io)-1 && eltype(A) <: Union{Tuple, NamedTuple}
        # show as M×N array_type[…]
        print(io, size2string(size(A)), " ")
        show_protected(deeper(io), eltype(A))
        length(A) == 0 ? print(io, "[]") : print(io, "[…]")
    else
        # print inline
        print(io, '[')
        truncated = print_array_inline(io, A, 8)
        print(io, ']')
        truncated && print(io, " (", size2string(size(A)), ")")
    end
end

function print_array_inline(io::IO, A::AbstractVector, max_show, delim = ',')
    # Show vectors as  [a1, a2, a3, ... a_n]
    # Show matrices as [a11 a12 a13; a21 a22 a23; ... ]
    # Show nd-arrays as nested matrices
    # If we can show all the elements, do so.
    n_show = length(A) <= max_show ? length(A) : max_show-1
    for i = 1:n_show
        shownice(deeper(io), A[i])
        i < n_show && print(io, delim, " ")
    end
    trunc = false
    if n_show < length(A)
        trunc = true
        print(io, " … ")
        show(deeper(io), A[end])
    end
    trunc
end

function print_array_inline(io::IO, A::AbstractMatrix, max_show)
    # Show vectors as  [a1, a2, a3, ... a_n]
    # Show matrices as [a11 a12 a13; a21 a22 a23; ... ]
    # Show nd-arrays as nested matrices
    # If we can show all the elements, do so.
    # number of rows to show
    if length(A) <= max_show
        n_show = size(A,1)
        row_length = size(A,2)
    else
        r = sqrt(length(A) / max_show)
        row_length = max(Int(ceil(size(A,1)/r)), 2)
        max_rows = max(Int(floor(max_show / row_length)), 2)
        n_show =  size(A,2) <= max_rows ? size(A,2) : max_rows - 1
    end
    # @info max_rows, n_show, row_length
    trunc = false
    for i = 1:n_show
        trunc |= print_array_inline(io, A[i,:], row_length, "")
        i < size(A,1) && print(io, ";  ")
    end
    if n_show < size(A,1)
        trunc = true
        print(io, "… ;  ")
        print_array_inline(io, A[end,:], row_length, "")
    end
    trunc
end

function print_array_inline(io::IO, A::AbstractArray, max_show)

end

# function show(io::IO, x::)
# 	iscompact = get(io, :compact, false)
# 	X = typeof(x)
# 	if iscompact
# 		print(io, x.delay, ": ", x.name, tuple(qsites(x)...))
# 	else
# 		println(io, X, ":")
# 		ioc = IOContext(io, :compact => true)
# 		for (i, name) in enumerate(fieldnames(X))
# 			print(ioc, "   ", name, " = ")
# 			if name == :owner && x.owner != nothing
# 				# Just show the owner's name (we don't need to see its contents)
# 				show(ioc, fullname(getfield(x, i)))
# 			else
# 				# otherwise show a compact summary of the field's value
# 				show(ioc, getfield(x, i))
# 			end
# 			println(ioc)
# 		end
# 	end
# end

end # module


text:
"""
`PopulationSolution`

Solution for a population of solutions.

**Fields**
- `time`: Time vector (time steps).
- `mean`: Population mean over individual trajectories (time steps x variables).
- `trajectories`: Individual trajectories (time steps x variables x trajectories).
- `success`: True, if the integration was successful.

**Functions**
- `plot_solution`
- `select_time`
"""
struct PopulationSolution

    time::Vector
    mean::Matrix
    trajectories::Array
    events::Matrix
    success::Bool

    """
    Constructor if all fields are known.
    """
    function PopulationSolution(time::Vector, mean::Matrix, trajectories::Array, events::Matrix, success::Bool)
        new(time, mean, trajectories, events, success)
    end

    """
    Constructor if only mean is known but not the individual trajectories.
    """
    function PopulationSolution(time::Vector, mean::Matrix, events::Matrix, success::Bool)
        trajectories = Array{Float64, 3}(undef, 0, 0, 0)
        new(time, mean, trajectories, events, success)
    end

    """
    Constructor without parameters creates a solution for a failed simulation.
    """
    function PopulationSolution()
        empty_time = Vector{Float64}(undef, 0)
        empty_mean = Matrix{Float64}(undef, 0, 0)
        empty_trajectories = Array{Float64, 3}(undef, 0, 0, 0)
        empty_events = Matrix{Float64}(undef, 0, 2)
        new(empty_time, empty_mean, empty_trajectories, empty_events, false)
    end

end

"""
`plot_solution(solution::PopulationSolution; ax=gca())`

Plot a solution.

**Arguments**
- `solution`: PopulationSolution.

**Keyword Arguments**
- `ax`: Axes.
"""
function plot_solution(solution::PopulationSolution; ax=gca())

    ax.plot(solution.time, solution.mean, color="black")
    plot_events(solution.events, ax=ax)

    return ax

end

"""
`select_time`

Select a specific time from a solution.

**Arguments**
- `solution`: `PopulationSolution`.

**Keyword Arguments**
- `removeoffset`: If `true`, the first time point is set to 0.
- `offset`: Set offset (the first time point).
- `mintime`: Minimal time (inclusive).
- `maxtime`: Minimal time (exclusive).
"""
function select_time(solution::PopulationSolution; removeoffset=true, kwargs...)

    t = solution.time
    m = solution.mean
    U = solution.trajectories
    events = solution.events
    success = solution.success

    if (:mintime in keys(kwargs))

        # Remove samples before `mintime`
        mintime = kwargs[:mintime]
        indices = t .>= mintime
        t = t[indices]
        m = m[indices, :]
        if !isempty(U)
            U = U[indices, :, :]
        end

        # Remove events before `mintime`
        newevents = Matrix{Float64}(undef, 0, 2)
        for i = 1:size(events, 1)
            if events[i, 2] >= mintime
                if events[i, 1] >= mintime
                    newevents = vcat(newevents, events[i, :]')
                else
                    newevents = vcat(newevents, [t[1] events[i, 2]])
                end
            end
        end
        events = newevents

    end

    if (:maxtime in keys(kwargs))

        # Remove samples after `maxtime`
        maxtime = kwargs[:maxtime]
        indices = t .< maxtime
        t = t[indices]
        m = m[indices, :]
        if !isempty(U)
            U = U[indices, :, :]
        end

        # Remove events after `maxtime`
        newevents = Matrix{Float64}(undef, 0, 2)
        for i = 1:size(events, 1)
            if events[i, 1] < maxtime
                if events[i, 2] < maxtime
                    newevents = vcat(newevents, events[i, :]')
                else
                    newevents = vcat(newevents, [events[i, 1] t[end]])
                end
            end
        end
        events = newevents
    end

    if removeoffset
        # Set first timepoint to zero
        events .-= t[1]
        t .-= t[1]
    end

    if :offset in keys(kwargs)
        # Set first timepoint to offset
        offset = kwargs[:offset]
        events .+= offset .- t[1]
        t .+= offset .- t[1]
    end

    return PopulationSolution(t, m, U, events, success)

end

max_stars_repo_name: vkumpost/stoosc

max_issues_repo_name: vkumpost/stoosc

max_forks_repo_name: vkumpost/stoosc


text:
using CImGui
using CImGui.ImGuiGLFWBackend
using CImGui.ImGuiGLFWBackend.LibCImGui
using CImGui.ImGuiGLFWBackend.LibGLFW
using CImGui.ImGuiOpenGLBackend
using CImGui.ImGuiOpenGLBackend.ModernGL
# using CImGui.ImGuiGLFWBackend.GLFW
using CImGui.CSyntax

# include(joinpath(@__DIR__, "demo_window.jl"))

glfwDefaultWindowHints()
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2)
if Sys.isapple()
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE) # 3.2+ only
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE) # required on Mac
end

# create window
window = glfwCreateWindow(1280, 720, "Demo", C_NULL, C_NULL)
@assert window != C_NULL
glfwMakeContextCurrent(window)
glfwSwapInterval(1)  # enable vsync

# create OpenGL and GLFW context
window_ctx = ImGuiGLFWBackend.create_context(window)
gl_ctx = ImGuiOpenGLBackend.create_context()

# setup Dear ImGui context
ctx = CImGui.CreateContext()

# enable docking and multi-viewport
io = CImGui.GetIO()
io.ConfigFlags = unsafe_load(io.ConfigFlags) | CImGui.ImGuiConfigFlags_DockingEnable
io.ConfigFlags = unsafe_load(io.ConfigFlags) | CImGui.ImGuiConfigFlags_ViewportsEnable

# setup Dear ImGui style
CImGui.StyleColorsDark()
# CImGui.StyleColorsClassic()
# CImGui.StyleColorsLight()

# When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
style = Ptr{ImGuiStyle}(CImGui.GetStyle())
if unsafe_load(io.ConfigFlags) & ImGuiConfigFlags_ViewportsEnable == ImGuiConfigFlags_ViewportsEnable
    style.WindowRounding = 5.0f0
    col = CImGui.c_get(style.Colors, CImGui.ImGuiCol_WindowBg)
    CImGui.c_set!(style.Colors, CImGui.ImGuiCol_WindowBg, ImVec4(col.x, col.y, col.z, 1.0f0))
end

# load Fonts
# - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use `CImGui.PushFont/PopFont` to select them.
# - `CImGui.AddFontFromFileTTF` will return the `Ptr{ImFont}` so you can store it if you need to select the font among multiple.
# - If the file cannot be loaded, the function will return C_NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
# - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling `CImGui.Build()`/`GetTexDataAsXXXX()``, which `ImGui_ImplXXXX_NewFrame` below will call.
# - Read 'fonts/README.txt' for more instructions and details.
fonts_dir = joinpath(@__DIR__, "..", "fonts")
fonts = unsafe_load(CImGui.GetIO().Fonts)
# default_font = CImGui.AddFontDefault(fonts)
# CImGui.AddFontFromFileTTF(fonts, joinpath(fonts_dir, "Cousine-Regular.ttf"), 15)
# CImGui.AddFontFromFileTTF(fonts, joinpath(fonts_dir, "DroidSans.ttf"), 16)
# CImGui.AddFontFromFileTTF(fonts, joinpath(fonts_dir, "Karla-Regular.ttf"), 10)
# CImGui.AddFontFromFileTTF(fonts, joinpath(fonts_dir, "ProggyTiny.ttf"), 10)
# CImGui.AddFontFromFileTTF(fonts, joinpath(fonts_dir, "Roboto-Medium.ttf"), 16)
CImGui.AddFontFromFileTTF(fonts, joinpath(fonts_dir, "Recursive Mono Casual-Regular.ttf"), 16)
CImGui.AddFontFromFileTTF(fonts, joinpath(fonts_dir, "Recursive Mono Linear-Regular.ttf"), 16)
CImGui.AddFontFromFileTTF(fonts, joinpath(fonts_dir, "Recursive Sans Casual-Regular.ttf"), 16)
CImGui.AddFontFromFileTTF(fonts, joinpath(fonts_dir, "Recursive Sans Linear-Regular.ttf"), 16)
# @assert default_font != C_NULL

# creat texture for image drawing
# img_width, img_height = 256, 256
# image_id = ImGui_ImplOpenGL3_CreateImageTexture(img_width, img_height)

# setup Platform/Renderer bindings
ImGuiGLFWBackend.init(window_ctx)
ImGuiOpenGLBackend.init(gl_ctx)

try
    demo_open = true
    clear_color = Cfloat[0.45, 0.55, 0.60, 1.00]
    while glfwWindowShouldClose(window) == 0
        glfwPollEvents()
        # start the Dear ImGui frame
        ImGuiOpenGLBackend.new_frame(gl_ctx)
        ImGuiGLFWBackend.new_frame(window_ctx)
        CImGui.NewFrame()

        demo_open && @c CImGui.ShowDemoWindow(&demo_open)

        # # show image example
        # CImGui.Begin("Image Demo")
        # image = rand(GLubyte, 4, img_width, img_height)
        # ImGui_ImplOpenGL3_UpdateImageTexture(image_id, image, img_width, img_height)
        # CImGui.Image(Ptr{Cvoid}(image_id), (img_width, img_height))
        # CImGui.End()

        # rendering
        CImGui.Render()
        glfwMakeContextCurrent(window)

        width, height = Ref{Cint}(), Ref{Cint}() #! need helper fcn
        glfwGetFramebufferSize(window, width, height)
        display_w = width[]
        display_h = height[]

        glViewport(0, 0, display_w, display_h)
        glClearColor(clear_color...)
        glClear(GL_COLOR_BUFFER_BIT)
        ImGuiOpenGLBackend.render(gl_ctx)

        if unsafe_load(igGetIO().ConfigFlags) & ImGuiConfigFlags_ViewportsEnable == ImGuiConfigFlags_ViewportsEnable
            backup_current_context = glfwGetCurrentContext()
            igUpdatePlatformWindows()
            GC.@preserve gl_ctx igRenderPlatformWindowsDefault(C_NULL, pointer_from_objref(gl_ctx))
            glfwMakeContextCurrent(backup_current_context)
        end

        glfwSwapBuffers(window)
    end
catch e
    @error "Error in renderloop!" exception=e
    Base.show_backtrace(stderr, catch_backtrace())
finally
    ImGuiOpenGLBackend.shutdown(gl_ctx)
    ImGuiGLFWBackend.shutdown(window_ctx)
    CImGui.DestroyContext(ctx)
    glfwDestroyWindow(window)
end


text:
using ADVUMPS
using BenchmarkTools
using KrylovKit
using LinearAlgebra
using Zygote

foo = x -> magnetisation(Ising(), x, 20)
@benchmark foo(0.5)
@benchmark Zygote.gradient(foo,0.5)[1]


text:
using Base.Test
using CrossfilterCharts


text:
module AdventOfCode2020

include_dates = [1,2,3,4,5,6,7,8,9]

function get_filepath(date::Int64)::Tuple{String,String}
    return joinpath(@__DIR__, "Dec-$(date)", "1.jl"), joinpath(@__DIR__, "Dec-$(date)", "2.jl")
end

function include_files(x::Array{String,1})
    include.(x)
end

function include_modules_dates(dates::Array{Int64,1})
    file_paths = get_filepath.(dates)
    file_paths = collect(Iterators.flatten(file_paths))
    include_files(file_paths)
end

include_modules_dates(include_dates)

function run(part::Int64, date::String, file_path::String)
    date = join(split(date, '-'))
    module_date = Symbol("$(date)Part$(part)")
    return @eval $module_date.main($file_path)
end
end


text:
@testset "Projection math" begin
    @test eltype(Makie.rotationmatrix_x(1)) == Float64
    @test eltype(Makie.rotationmatrix_x(1f0)) == Float32
end


text:
function ⪇(x::F, y::F) where {F <: AbstractFloat}
    return x < y && eps(F) < abs(x - y)
end

function ⪉(x::F, y::F) where {F <: AbstractFloat}
    return x < y && x ≉  y
end

max_stars_repo_name: khurrumsaleem/MOCNeutronTransport

max_issues_repo_name: khurrumsaleem/MOCNeutronTransport

max_forks_repo_name: khurrumsaleem/MOCNeutronTransport


text:
#  Copyright 2016 Eric S. Tellez <eric.tellez@infotec.mx>
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.

using SimilaritySearch

import SimilaritySearch:
    search

import Base:
    push!

export Kvp, near_and_far

mutable struct Kvp{T, D} <: Index
    db::Vector{T}
    dist::D
    refs::Vector{T}
    sparsetable::Vector{Vector{Item}}
    k::Int
end

function Kvp(db::Vector{T}, dist::D, k::Int, refList::Vector{Int}) where {T,D}
    @info "Kvp, refs=$(typeof(db)), k=$(k), numrefs=$(length(refList)), dist=$(dist)"
    sparsetable = Vector{Item}[]
    refs = [db[x] for x in refList]
    for i=1:length(db)
        if (i % 10000) == 0
            @info "advance $(i)/$(length(db))"
        end
        row = near_and_far(db[i], refs, k, dist)
        # println(row)
        push!(sparsetable, row)
    end

    return Kvp(db, dist, refs, sparsetable, k)
end

function Kvp(db::Vector{T}, dist::D, k::Int, numrefs::Int) where {T,D}
    refs = rand(1:length(db), numrefs)
    Kvp(db, dist, k, refs)
end

function near_and_far(obj::T, refs::Vector{T}, k::Int, dist::D) where {T,D}
    near = KnnResult(k)
    far = KnnResult(k)
    for (refID, ref) in enumerate(refs)
        d = dist(obj, ref)
        push!(near, refID, d)
        push!(far, refID, -d)
    end

    row = Vector{Item}(undef, k+k)
    for (j, item) in enumerate(near)
        row[j] = item
    end

    for (j, item) in enumerate(far)
        row[k+j] = Item(item.objID, -item.dist)
    end

    return row
end

function search(index::Kvp{T,D}, q::T, res::Result) where {T,D}
    # for i in range(1, length(index.db))
    d::Float64 = 0.0
    dist = index.dist
    qI = [dist(q, piv) for piv in index.refs]

    for i = 1:length(index.db)
        obj::T = index.db[i]
        objSparseRow = index.sparsetable[i]

        discarded::Bool = false
        @inbounds for item in objSparseRow
            pivID = item.objID
            dop = item.dist
            if abs(dop - qI[pivID]) > covrad(res)
                discarded = true
                break
            end
        end
        if discarded
            continue
        end
        d =dist(q, obj)
        push!(res, i, d)
    end

    return res
end

function push!(index::Kvp{T}, obj::T) where {T}
    push!(index.db, obj)
    row = near_and_far(obj, index.refs, index.k, index.dist)
    push!(index.sparsetable, row)
    return length(index.db)
end