module trieMusic
using Plots
using Combinatorics

#シーケンスを階差数列にして返却する
function seqenceToDifferenceSequence(sequence)
    differenceSequence = []
    beforeElm = sequence[1]
    for elm in sequence[2:end]
        push!(differenceSequence, elm - beforeElm)
        beforeElm = elm
    end
    return differenceSequence
end

#シーケンスを増加または減少の2値（1, -1）に変換して返却する
function sequenceToBinarySequence(sequence)
    binarySequence = []
    for elm in sequence
        if elm > 0
            push!(binarySequence, 1)
        else
            push!(binarySequence, -1)
        end
    end
    return binarySequence
end

#シーケンスを独自ルールでLOUDSのTrie木構造に変換して返却する
function sequenceToLouds(sequence)
    #トライ木をLOUDSで表現するためのビット配列　初期値として1番ノードを指すtと、
    #ルートノードの終端を指すfと、
    #1番ノードの終端を指すfが入ってる
    bitAry = BitArray([true,false,false])
    #各ノードに対応するラベル　形式は[sequenceの要素, その要素が出現したインデックス1, その要素が出現したインデックス2,...]
    label = []
    #検索対象となるノードの情報。[何階層目か, その階層の中で左から何番目のノードか、bitIdx]
    searchTargetNodes = []
    #シーケンス番号
    seqIdx = 0
    #各階層のノード数　ルート直下からスタート
    eachLevelNodesAccumulatedNumber = []
    #各階層のbitAry上の区切り位置
    levelBoundaryBaIdx = [3]

    for elm in sequence
        seqIdx = seqIdx + 1
        #検索対象となるノードがあれば過去のシーケンスを検索開始
        if length(searchTargetNodes) > 0
            tmpSearchTargetNodes = []
            for searchTargetNode in searchTargetNodes
                #検索対象となるノードが持つsequence上のインデックスを取得。
                #ラベルから取得するが、現在のseqIdx　- 1 と同じ値の要素は取得しない　あくまで過去の要素が対象
                targetLabelIdx = 0
                if searchTargetNode[1] == 1
                    targetLabelIdx = searchTargetNode[2]
                else
                    targetLabelIdx = eachLevelNodesAccumulatedNumber[searchTargetNode[1] - 1] + searchTargetNode[2]
                end
                searchTargetSeqIdxes = filter!(e->e ≠ seqIdx - 1, label[targetLabelIdx][2:end])
                #ラベルから取得した過去のsequence上のインデックスの右隣が検索対象
                searchTargetChildrenSeqIdxes = searchTargetSeqIdxes + repeat([1], length(searchTargetSeqIdxes))
                #シーケンス上に、今来ているelmと一致したインデックスを取得
                foundMatchLabelIdxes = searchTargetChildrenSeqIdxes[findall(isequal(elm), [sequence[i] for i in searchTargetChildrenSeqIdxes])]
                #1つでも一致したシーケンスがあれば、bitAryとlabelを更新する必要がある
                if length(foundMatchLabelIdxes) > 0
                    #foundMatchLabelIdxesに現在のseqIdxも追加する。その配列をラベルに追加するため。
                    push!(foundMatchLabelIdxes, seqIdx)
                    #子ノードの情報（子ノードの数、子ノードと同じ階層の上の従兄弟の数、子ノードの終端のfalseのbitIdx）を取得する
                    bitIdx, childrenNumber, brotherChildrenNumber = getChild(levelBoundaryBaIdx, searchTargetNode, bitAry)
                    #子ノードの開始位置の一つ前のidxを取得する
                    childLabelStartIdx = eachLevelNodesAccumulatedNumber[searchTargetNode[1]] + brotherChildrenNumber
                    #子ノードが存在していたら
                    if childrenNumber > 0
                        #子ノードのラベルインデックスを取得
                        childrenLabelIdxes = childLabelStartIdx + 1:childLabelStartIdx + childrenNumber
                        #子ノードの中で一致したノードのラベルインデックスを取得
                        matchChildLabelIdx = findfirst(isequal(elm), [elmLabel[1] for elmLabel in label[childrenLabelIdxes]])
                        #一致したノードがあれば
                        if matchChildLabelIdx != nothing
                            #既存のノードラベルにfoundMatchLabelIdxesを追加する
                            childLabelStartIdx += matchChildLabelIdx

                            label[childLabelStartIdx] = insert!(unique!(sort(append!(label[childLabelStartIdx][2:end], foundMatchLabelIdxes))), 1, label[childLabelStartIdx][1])

                            push!(tmpSearchTargetNodes, [searchTargetNode[1] + 1, brotherChildrenNumber + matchChildLabelIdx, bitIdx - childrenNumber - 1 + matchChildLabelIdx])
                        #一致したノードがなければノード追加
                        else
                            #子ノード追加
                            insert!(bitAry, bitIdx, true)
                            #各階層のbitAry上の区切り位置更新
                            #追加した子ノードの子ノードが存在しているかもしれない階層まで、区切り位置の配列が情報を持っていなければ追加
                            #適切な位置にfalseを追加
                            insert!(bitAry, getChild(levelBoundaryBaIdx, [searchTargetNode[1] + 1, brotherChildrenNumber + childrenNumber + 1], bitAry)[1], false)
                            #子ノードを追加した階層より右側の区切り位置を1増やす
                            levelBoundaryBaIdx[searchTargetNode[1] + 2] += 1
                            levelBoundaryBaIdx[searchTargetNode[1] + 3:end] += repeat([2], length(levelBoundaryBaIdx[searchTargetNode[1] + 3:end]))
                            #子ノードを追加した階層から右側のノード数を1増やす
                            eachLevelNodesAccumulatedNumber[searchTargetNode[1] + 1:end] += repeat([1], length(eachLevelNodesAccumulatedNumber[searchTargetNode[1] + 1:end]))
                            #labelに追加
                            insert!(label, childrenLabelIdxes[end] + 1, insert!(foundMatchLabelIdxes, 1, elm))
                            #既に登録されているsearchTargetNodesを更新
                            tmpSearchTargetNodes = updateSearchTargetNodes(tmpSearchTargetNodes, bitIdx, searchTargetNode)
                            #searchTargetNodeを追加する。
                            push!(tmpSearchTargetNodes, [searchTargetNode[1] + 1, brotherChildrenNumber + childrenNumber + 1, bitIdx])
                        end
                    #子ノードが存在していなければ子ノードを追加する
                    else
                        #子ノード追加
                        insert!(bitAry, bitIdx, true)

                        #各階層のbitAry上の区切り位置更新
                        #追加した子ノードの子ノードが存在しているかもしれない階層まで、区切り位置の配列が情報を持っていなければ追加
                        if length(levelBoundaryBaIdx) < searchTargetNode[1] + 2
                            push!(levelBoundaryBaIdx, levelBoundaryBaIdx[end] + length(bitAry[levelBoundaryBaIdx[end]:end]))
                            #falseも追加
                            push!(bitAry, false)
                        #既に存在している場合
                        else
                            #適切な位置にfalseを追加
                            insert!(bitAry, getChild(levelBoundaryBaIdx, [searchTargetNode[1] + 1, brotherChildrenNumber + 1], bitAry)[1], false)
                            #子ノードを追加した階層より右側の区切り位置を1増やす
                            levelBoundaryBaIdx[searchTargetNode[1] + 2] += 1
                            levelBoundaryBaIdx[searchTargetNode[1] + 3:end] += repeat([2], length(levelBoundaryBaIdx[searchTargetNode[1] + 3:end]))
                        end
                        #今回追加した子ノードの階層まで、各階層のノード数配列が情報を持っていなければ追加
                        if length(eachLevelNodesAccumulatedNumber) < searchTargetNode[1] + 1
                            push!(eachLevelNodesAccumulatedNumber, eachLevelNodesAccumulatedNumber[end] + 1)
                        #既に存在している場合
                        else
                            #子ノードを追加した階層から右側のノード数を1増やす
                            eachLevelNodesAccumulatedNumber[searchTargetNode[1] + 1:end] += repeat([1], length(eachLevelNodesAccumulatedNumber[searchTargetNode[1] + 1:end]))
                        end
                        #labelに追加
                        insert!(label, childLabelStartIdx + 1, insert!(foundMatchLabelIdxes, 1, elm))
                        #既に登録されているsearchTargetNodesを更新
                        tmpSearchTargetNodes = updateSearchTargetNodes(tmpSearchTargetNodes, bitIdx, searchTargetNode)
                        #searchTargetNodeを追加する。
                        push!(tmpSearchTargetNodes, [searchTargetNode[1] + 1, brotherChildrenNumber + 1, bitIdx])
                    end
                end

            end
            #次回の検索用配列にコピー
            searchTargetNodes = tmpSearchTargetNodes
        end
        #ルートの子ノードに追加する
        #子ノードがあれば
        if length(eachLevelNodesAccumulatedNumber) > 0
            #同じ要素が子ノードにあるか検索
            matchRootChildLabelIdx = findfirst(isequal(elm), [labelElm[1] for labelElm in label[1:eachLevelNodesAccumulatedNumber[1]]])
            #あれば
            if matchRootChildLabelIdx !== nothing
                #一致したラベルに現在のシーケンスインデックスを追加
                push!(label[matchRootChildLabelIdx], seqIdx)
                #ターゲットノードとして追加
                push!(searchTargetNodes, [1, matchRootChildLabelIdx, 2 + matchRootChildLabelIdx])
            else
            #ルートノードの子にいない場合は追加
                insert!(bitAry, 2 + eachLevelNodesAccumulatedNumber[1] + 1, true)
                insert!(bitAry, getChild(levelBoundaryBaIdx, [1, eachLevelNodesAccumulatedNumber[1] + 1], bitAry)[1], false)
                insert!(label,  eachLevelNodesAccumulatedNumber[1] + 1, [elm, seqIdx])
                eachLevelNodesAccumulatedNumber += repeat([1], length(eachLevelNodesAccumulatedNumber))
                levelBoundaryBaIdx[2:end] += repeat([1], length(levelBoundaryBaIdx[2:end]))
            end
        #なければ追加
        else
            insert!(bitAry, 3, true)
            push!(bitAry, false)
            insert!(label, 1, [elm, seqIdx])
            push!(eachLevelNodesAccumulatedNumber, 1)
            push!(levelBoundaryBaIdx, 5)
        end
    end
    return bitAry, label, eachLevelNodesAccumulatedNumber, levelBoundaryBaIdx
end

#渡したノードの子ノードの情報を取得
function getChild(levelBoundaryBaIdx, searchTargetNode, bitAry)
    brotherNumber = 0
    countChildren = false
    childrenNumber = 0
    brotherChildrenNumber = 0
    #調査対象ノードが長男の場合、直下の階層の最初が調査対象ノードの子ノードになるので子ノードとして数えだす
    if searchTargetNode[2] == 1
        countChildren = true
    end
    bitIdx = levelBoundaryBaIdx[searchTargetNode[1] + 1] - 1
    for bit in bitAry[bitIdx + 1:end]
        bitIdx += 1
        if countChildren == true
            if bit == true
                childrenNumber += 1
            else
                brotherNumber += 1
            end
        else
            if bit == false
                brotherNumber += 1
            else
                brotherChildrenNumber += 1
            end
        end
        if brotherNumber == searchTargetNode[2] - 1
            countChildren = true
        end
        if brotherNumber == searchTargetNode[2]
            break
        end
    end
    return bitIdx, childrenNumber, brotherChildrenNumber
end

#既に登録されているsearchTargetNodesを更新
function updateSearchTargetNodes(tmpSearchTargetNodes, bitIdx, searchTargetNode)
    if length(tmpSearchTargetNodes) > 0
        for pastSearchTargetNode in tmpSearchTargetNodes
            #今回追加したbitIdxより後ろのbitIdxの場合、1つ右にずらす
            if bitIdx < pastSearchTargetNode[3]
                pastSearchTargetNode[3] += 1
                #さらに同じ階層の場合、ノードの左からの順番も1つ右にずらす
                if searchTargetNode[1] == pastSearchTargetNode[1]
                    pastSearchTargetNode[1] += 1
                end
            end
        end
    end
    return tmpSearchTargetNodes
end

#全部分列の情報を取得する
function getAllSubSequences(label, sequence, eachLevelNodesAccumulatedNumber)
    subSequences = []
    startSeqIdxes = []
    endSeqIdxes = []
    #取得したlabelつまりノードすべて計算
    for nodeNumber in 1:length(label)
        #trie木の親ノード直下のノードから、指定したノード番号（第四引数）までの部分列の形状を持つ、実際の部分列群を取得する
        subSequence, startSeqIdx, endSeqIdx = getMatchSubsequences(sequence, label, eachLevelNodesAccumulatedNumber, nodeNumber)
        #全部追加する
        push!(subSequences, subSequence)
        push!(startSeqIdxes, startSeqIdx)
        push!(endSeqIdxes, endSeqIdx)
    end
    return subSequences, startSeqIdxes, endSeqIdxes

end
#trie木の親ノード直下のノードから、指定したノード（targetLabelIdx）までの部分列の形状を持つ、実際の部分列群を返却する
function getMatchSubsequences(sequence, label, eachLevelNodesAccumulatedNumber, targetLabelIdx)
    #targetLabelIdxがどの階層にいるか判定 TODO:これ高速化したい
    targetNodeLevel = 0
    for nodesAccumulatedNumber in eachLevelNodesAccumulatedNumber
        targetNodeLevel += 1
        if nodesAccumulatedNumber ≥ targetLabelIdx
            break
        end
    end
    #labelの2番目以降に格納されているseqIdxesは、部分列の終端位置を指す。
    #現時点では、ソースとなるsequenceが階差数列になっているため実際の終端位置を出すために+1する
    endSeqIndexes = label[targetLabelIdx][2:end] + repeat([1], length(label[targetLabelIdx][2:end]))
    #終端位置から所属階層位置の数を引くことで、対象のノードまでの部分列の開始位置を取得する
    startSeqIdxes = endSeqIndexes - repeat([targetNodeLevel], length(endSeqIndexes))
    #全部分列を取得する
    subSequences = []
    for i in 1:length(startSeqIdxes)
        push!(subSequences, Dict("subSequenceStartIdx" => startSeqIdxes[i], "data" => sequence[startSeqIdxes[i]:endSeqIndexes[i]]))
    end
    return subSequences, startSeqIdxes, endSeqIndexes
end

#時系列データセットから2つの時系列データを全組み合わせで取り出して、それらのDTW距離を出す。
function calcDtwDistances(subSequences)
    dtwDistances = 0
    for subSequence in subSequences
        #時系列データセットから2つの時系列データを全組み合わせで取り出す
        sequenceCombination = collect(combinations(subSequence, 2))
        #全組み合わせの時系列同士の距離を計算する
        for seq in sequenceCombination
            dtwDistances += calcDtwDistance(seq[1]["data"], seq[2]["data"])
        end
    end
    return dtwDistances
end

#DTWで2つの時系列の距離を計算する。
#s1:比較対象時系列データその1
#s2:比較対象時系列データその2
function calcDtwDistance(s1, s2)
    #DTW格納用の配列。ループごとに更新される。
    beforeDtwAry = []
    currentDtwAry = []
    #xカウンタ
    x = 0
    for elm2 in s2
    #yカウンタ
        y = 0
        x = x + 1
        currentDtwAry = []
        for elm1 in s1
            y = y + 1
            diff = abs(elm2 - elm1)
            #ループの一番最初だけは先頭同士の差の絶対値
            if x == 1 && y == 1
                currentDtwAry = [diff]
            #ループの1列目は自身の配列のひとつ前の要素
            elseif x == 1
                push!(currentDtwAry, diff + currentDtwAry[y - 1])
            #2列目以降かつ1行目は、前の配列の同じ位置の要素
            elseif x != 1 && y == 1
                push!(currentDtwAry, diff + beforeDtwAry[y])
            #それ以外は、3パターンのうちの最小値
            else
                push!(currentDtwAry, diff + minimum([beforeDtwAry[y], beforeDtwAry[y - 1], currentDtwAry[y - 1]]))
            end
        end
        #完成した現在の列を前の列にコピーする
        beforeDtwAry = currentDtwAry
    end
    #DTW配列の最後の要素がDTW-Distance
    return currentDtwAry[end]
end

#一致した部分列群をプロットする
function plotSubsequences(sequence, subSequences, startSeqIdxes, endSeqIdxes)

    subSequencesForPlot = deepcopy(subSequences)

    for i in 1:length(subSequencesForPlot)
        subSequencesForPlotData = repeat([NaN], startSeqIdxes[i] - 1)
        append!(subSequencesForPlotData, subSequences[i]["data"])
        append!(subSequencesForPlotData, repeat([NaN], length(sequence) - endSeqIdxes[i]))
        subSequencesForPlot[i]["data"] = subSequencesForPlotData
    end
    #plot装飾用
    xticksValues = [1 + i * 16 for i in 0:div(length(sequence), 16)]
    xticksLabels = [i for i in 1:div(length(sequence), 16)]
    majorScaleValue = [0, 2, 4, 5, 7, 9, 11]
    majorScaleLabel = ["C","D","E","F","G","A","B"]

    minValOffset = div(minimum(sequence), 12)
    maxValOffset = div(maximum(sequence), 12)
    yticksValues = []
    yticksLabels = []
    if minValOffset == maxValOffset
        yticksValues = majorScaleValue * minValOffset
    else
        for i in minValOffset:maxValOffset
            append!(yticksValues, majorScaleValue + repeat([12 * i], 7))
            append!(yticksLabels, majorScaleLabel)
        end
    end
    #表示
    plot([subSequencesForPlot[i]["data"] for i in 1:length(subSequencesForPlot)],
    size = (2200, 500),
    xticks = (xticksValues, xticksLabels),
    yticks = (yticksValues, yticksLabels),
    xlabel = "measure",
    ylabel = "pitch(natural tone name)")
end
# PlantUMLファイル生成。Trie木を可視化。
function trieToPuml(bitAry, label)
    open("tree.puml", "w") do file
        write(file, "'This file was automatically generated.\n@startuml tree_diagram\nobject 0\n")

        bitCnt = 0
        for bit in bitAry[3:end]
            if bit == true
                bitCnt += 1
                write(file, "object ", string(bitCnt), "\n", string(bitCnt), " : value=", string(label[bitCnt][1]), "\n", string(bitCnt), " : seqIdx=", string(label[bitCnt][2:end], "\n"))
            end
        end
        children = []
        parent = 0
        labelCnt = 0
        for bit in bitAry[3:end]
            if bit == true
                labelCnt += 1
                push!(children, labelCnt)
            elseif bit == false
                if length(children) > 0
                    for child in children
                        write(file, string(parent), "--", string(child), "\n")
                    end
                    children = []
                end
                parent += 1
            end
        end
        write(file, "@enduml")
    end
end

end


module BenchMapCatEduction

using BenchmarkTools
using Transducers

const SUITE = BenchmarkGroup()

SUITE["xf-eduction"] =
    @benchmarkable foldl(+, 1:1000 |> MapCat(x -> 1:x |> Map(x -> x + 1)))
SUITE["xf-xf"] = @benchmarkable foldl(+, 1:1000 |> MapCat(x -> 1:x) |> Map(x -> x + 1))
SUITE["base"] = @benchmarkable sum(y + 1 for x in 1:1000 for y in 1:x)

end  # module
BenchMapCatEduction.SUITE


function check_balances(user)
    balances_all  = Dict()
    # Load BNB settings
    BNB_settings = JSON.parsefile(joinpath(Pinguu.base_dir, "BNB_control/BNB_settings.json"))
    for (k,v) in user
        user_id = parse(Int64, k)
        client = user[k]["client"]

        user_config = glob(joinpath(Pinguu.base_dir, "users/$user_id/*/config.json"));
        user_invest = glob(joinpath(Pinguu.base_dir, "users/$user_id/*/config.json"));
        account = client.get_account()["balances"];
        if account !== nothing
            balances = Dict()

            for bal in account
                balances[bal["asset"]] = Dict()
                balances[bal["asset"]]["free"] = parse(Float64, bal["free"])
                balances[bal["asset"]]["locked"] = parse(Float64, bal["locked"])

                #
                ## Check Pinguus
                coin = bal["asset"]
                total_vol = 0
                locked   = 0
                locked_stopped = 0
                invested  = 0
                for c in user_config
                    tmp_config = JSON.parsefile(c)
                    if tmp_config["status"] != "dead" && tmp_config["pureEMA"] == 0
                        stat = JSON.parsefile(joinpath(dirname(c), "status.json"))
                        if stat["base_coin"] == coin
                            oo = JSON.parsefile(joinpath(dirname(c), "open_orders.json"))
                            co = JSON.parsefile(joinpath(dirname(c), "closed_orders.json"))
                            if length(oo["BUY"]) > 0
                                for (k,v) in oo["BUY"]
                                    id_str = split(k, "_")[end]
                                    if v["status"] != "stopped"
                                        locked += parse(Float64, v["price"]) * parse(Float64, v["origQty"])
                                    else
                                        locked_stopped += parse(Float64, v["price"]) * parse(Float64, v["origQty"])
                                    end
                                    for (k,v) in co[id_str]
                                        if v["side"] == "BUY"
                                            invested += parse(Float64, v["cummulativeQuoteQty"])
                                        else
                                            invested -= parse(Float64, v["cummulativeQuoteQty"])
                                        end
                                    end
                                end
                            elseif length(oo["SELL"]) > 0 && length(oo["BUY"]) == 0
                                #
                                ## Case: Bot is fully invested!
                                for (k,v) in oo["SELL"]
                                    id_str = split(k, "_")[end]
                                    for (k,v) in co[id_str]
                                        if v["side"] == "BUY"
                                            invested += parse(Float64, v["cummulativeQuoteQty"])
                                        else
                                            invested -= parse(Float64, v["cummulativeQuoteQty"])
                                        end
                                    end
                                end
                            end

                            for (k,v) in tmp_config["volume"]
                                total_vol += v
                            end
                            total_vol += tmp_config["thes_vol"]
                        end
                    end
                end
                balances[bal["asset"]]["pinguu_locked"]   = locked
                balances[bal["asset"]]["pinguu_locked_stopped"] = locked_stopped
                balances[bal["asset"]]["pinguu_invested"] = invested
                balances[bal["asset"]]["pinguu_volume"]   = total_vol
                #
                ## Check Pinguu Invest
                user_invest = glob(joinpath(Pinguu.base_dir, "invest/users/$user_id/filled/*/*/" * bal["asset"] * "*.json"))
                user_invest_sold = glob(joinpath(Pinguu.base_dir, "invest/users/$user_id/sold/*/*/" * bal["asset"] * "*.json"))
                balances[bal["asset"]]["invest"] = 0
                for inv in user_invest
                    tmp = JSON.parsefile(inv)
                    balances[bal["asset"]]["invest"] += parse(Float64, tmp["amount"])
                end
                for inv in user_invest_sold
                    tmp = JSON.parsefile(inv)
                    balances[bal["asset"]]["invest"] -= parse(Float64, tmp["amount"])
                end
                balances[bal["asset"]]["estimated_free"]  = balances[bal["asset"]]["free"] - balances[bal["asset"]]["invest"] - locked_stopped - (total_vol - invested - locked - locked_stopped)
            end
        end

        if haskey(BNB_settings, k)
            # Check if user is registered and uses BNB Control
            if BNB_settings[k]["active"] == 1
                # Get current BNB value
                current = parse(Float64, client.get_ticker(symbol="BNBEUR")["lastPrice"])
                # Calculate amount of EUR that should be kept aside for next BNB purchase
                keep_aside = BNB_settings[k]["val"] + BNB_settings[k]["low_limit"] - balances["BNB"]["estimated_free"]*current
                if keep_aside > 0
                    balances["EUR"]["estimated_free"] -= keep_aside
                end
            end
        end

        #
        ## Grid Bots
        grid_config_paths = glob(joinpath(Pinguu.base_dir, "grid_bots/user/$user_id/*/config.json"))
        grid_volume, grid_locked, grid_invested, grid_sub_from_free = 0, 0, 0, 0
        for grid_config_path in grid_config_paths
            tmp = JSON.parsefile(grid_config_path)
            if !(tmp["status"] in ["finished", "prep"]) && tmp["testing"] == "0"
#                 total_value = typeof(tmp["total_value"]) == Float64 ? tmp["total_value"] : parse(Float64, tmp["total_value"])
                total_value = Pinguu.get_current_volume(tmp)
                executedQty = typeof(tmp["init_state"]["executedQty"]) == Float64 ? tmp["init_state"]["executedQty"] : parse(Float64, tmp["init_state"]["executedQty"])
                if typeof(tmp["init_state"]["sold"]) != Int64
                    sold = typeof(tmp["init_state"]["sold"]) == Float64 ? tmp["init_state"]["sold"] : parse(Float64, tmp["init_state"]["sold"])
                else
                    sold = float(tmp["init_state"]["sold"])
                end
                cummulativeQuoteQty = typeof(tmp["init_state"]["cummulativeQuoteQty"]) == Float64 ? tmp["init_state"]["cummulativeQuoteQty"] : parse(Float64, tmp["init_state"]["cummulativeQuoteQty"])
                trade_grid = JSON.parsefile(joinpath(dirname(grid_config_path), "trade_grid.json"))
                invested, locked = 0, 0
                for (price, gridpoint) in trade_grid
                    if haskey(gridpoint, "buy_order")
                        tmp_val = gridpoint["buy_order"]["cummulativeQuoteQty"]
                        invested += typeof(tmp_val) == Float64 ? tmp_val : parse(Float64, tmp_val)
                    end
                    if haskey(gridpoint, "side")
                        if gridpoint["side"] == client.SIDE_BUY
                            if haskey(gridpoint, "order")
                                tmp_price = typeof(gridpoint["order"]["price"]) == Float64 ? gridpoint["order"]["price"] : parse(Float64, gridpoint["order"]["price"])
                                tmp_qty   = typeof(gridpoint["order"]["origQty"]) == Float64 ? gridpoint["order"]["origQty"] : parse(Float64, gridpoint["order"]["origQty"])
                                locked += tmp_price * tmp_qty
                            end
                        end
                    end
                end
                sub_from_free = total_value - cummulativeQuoteQty * (1 - sold/executedQty) - invested - locked
                grid_volume += total_value
                grid_locked += locked
                grid_invested += invested
                grid_sub_from_free += sub_from_free
            end
        end
        balances["EUR"]["estimated_free"] -= grid_sub_from_free
        balances["EUR"]["grid_volume"] = grid_volume
        balances["EUR"]["grid_locked"] = grid_locked
        balances["EUR"]["grid_invested"] = grid_invested

        balances_all[k] = balances
    end
    open(joinpath(Pinguu.base_dir, "account_balances.json"), "w") do f
        JSON.print(f, balances_all, 4)
    end
    return balances_all
end
export check_balances


cell_to_vtkcell(::Type{LineCell}) = WriteVTK.VTKCellTypes.VTK_LINE
cell_to_vtkcell(::Type{RectangleCell}) = WriteVTK.VTKCellTypes.VTK_QUAD
cell_to_vtkcell(::Type{TriangleCell}) = WriteVTK.VTKCellTypes.VTK_TRIANGLE
cell_to_vtkcell(::Type{HexagonCell}) = WriteVTK.VTKCellTypes.VTK_POLYGON
cell_to_vtkcell(::Type{HexahedronCell}) = WriteVTK.VTKCellTypes.VTK_HEXAHEDRON
cell_to_vtkcell(::Type{TetrahedronCell}) = WriteVTK.VTKCellTypes.VTK_TETRA

"""
    vtk_grid(filename::AbstractString, grid::Grid)

Create a unstructured VTK grid from a `PolytopalMesh`. Return a `DatasetFile`
which data can be appended to, see `vtk_point_data` and `vtk_cell_data`.
"""
function vtk_grid(filename::AbstractString, mesh::AbstractPolytopalMesh{dim,T}; compress::Bool=true) where {dim,T}
    cls = WriteVTK.MeshCell[]
    for cell_idx in 1:getncells(mesh)
        celltype = cell_to_vtkcell(getCellType(mesh, cell_idx))
        push!(cls, WriteVTK.MeshCell(celltype, getverticesindices(mesh,getcell(mesh,cell_idx))))
    end
    coords = get_vertices_matrix(mesh)'
    return WriteVTK.vtk_grid(filename, coords, cls; compress=compress)
end


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

using BAT
using Test

using LinearAlgebra
using ArraysOfArrays, Distributions

@testset "logdval" begin
    @test @inferred(logvalof(LogDVal(log(4.2)))) == log(4.2)
    @test @inferred(logvalof((foo = 55, logval = log(4.2), bar = 2.7))) == log(4.2)
    @test @inferred(logvalof((foo = 55, logd = log(4.3), bar = 2.7))) == log(4.3)
    @test @inferred(logvalof((foo = 55, log = log(4.4), bar = 2.7))) == log(4.4)
    @test_throws ArgumentError logvalof((foo = 55, logval = log(4.3), logd = 2.7))
    @test_throws ArgumentError logvalof((foo = 55, logval = log(4.3), log = 2.7))
    @test_throws ArgumentError logvalof(42)
end


function codegen(::Type{Val{:iterate}}, ::Type{NUTS}, job::BasicMCJob)
  result::Expr
  whilebody = []
  ifwhilebody = []
  burninbody = []
  ifburninbody = []
  body = []
  dindex::Integer

  vform = variate_form(job.pstate)
  if vform != Univariate && vform != Multivariate
    error("Only univariate or multivariate parameter states allowed in NUTS code generation")
  end

  # push!(body, :(_job.sstate.count += 1))

  if job.tuner.verbose
    push!(body, :(_job.sstate.tune.proposed += 1))
  end

  if vform == Univariate
    push!(body, :(_job.sstate.momentum = randn()))
  elseif vform == Multivariate
    push!(body, :(_job.sstate.momentum[:] = randn(_job.pstate.size)))
  end

  push!(body, :(_job.sstate.oldhamiltonian = hamiltonian(_job.pstate.logtarget, _job.sstate.momentum)))

  if vform == Univariate
    push!(body, :(_job.sstate.pstateplus.value = _job.pstate.value))
    push!(body, :(_job.sstate.pstateplus.gradlogtarget = _job.pstate.gradlogtarget))
    push!(body, :(_job.sstate.momentumplus = _job.sstate.momentum))
    push!(body, :(_job.sstate.pstateminus.value = _job.pstate.value))
    push!(body, :(_job.sstate.pstateminus.gradlogtarget = _job.pstate.gradlogtarget))
    push!(body, :(_job.sstate.momentumminus = _job.sstate.momentum))
  elseif vform == Multivariate
    push!(body, :(_job.sstate.pstateplus.value = copy(_job.pstate.value)))
    push!(body, :(_job.sstate.pstateplus.gradlogtarget = copy(_job.pstate.gradlogtarget)))
    push!(body, :(_job.sstate.momentumplus = copy(_job.sstate.momentum)))
    push!(body, :(_job.sstate.pstateminus.value = copy(_job.pstate.value)))
    push!(body, :(_job.sstate.pstateminus.gradlogtarget = copy(_job.pstate.gradlogtarget)))
    push!(body, :(_job.sstate.momentumminus = copy(_job.sstate.momentum)))
  end

  push!(body, :(_job.sstate.j = 0))
  push!(body, :(_job.sstate.n = 1))
  push!(body, :(_job.sstate.s = true))
  if in(:accept, job.outopts[:diagnostics])
    push!(body, :(_job.sstate.update = false))
  end

  push!(body, :(_job.sstate.u = log(rand())+_job.sstate.oldhamiltonian))

  push!(whilebody, :(_job.sstate.v = rand(Bool) ? 1 : -1))

  push!(whilebody, :(
    if _job.sstate.v == -1
      _job.sstate.pstateminus,
      _job.sstate.momentumminus,
      _,
      _,
      _job.sstate.pstateprime,
      _job.sstate.nprime,
      _job.sstate.sprime =
        build_tree!(
          _job.sstate,
          _job.sstate.pstateminus,
          _job.sstate.momentumminus,
          _job.sstate.u,
          _job.sstate.v,
          _job.sstate.j,
          _job.sstate.tune.step,
          _job.parameter.logtarget!,
          _job.parameter.gradlogtarget!,
          _job.sampler
        )
    else
      _,
      _,
      _job.sstate.pstateplus,
      _job.sstate.momentumplus,
      _job.sstate.pstateprime,
      _job.sstate.nprime,
      _job.sstate.sprime =
        build_tree!(
          _job.sstate,
          _job.sstate.pstateplus,
          _job.sstate.momentumplus,
          _job.sstate.u,
          _job.sstate.v,
          _job.sstate.j,
          _job.sstate.tune.step,
          _job.parameter.logtarget!,
          _job.parameter.gradlogtarget!,
          _job.sampler
        )
    end
  ))

  if vform == Univariate
    push!(ifwhilebody, :(_job.pstate.value = _job.sstate.pstateprime.value))
    push!(ifwhilebody, :(_job.pstate.gradlogtarget = _job.sstate.pstateprime.gradlogtarget))
    if in(:gradloglikelihood, job.outopts[:monitor]) && job.parameter.gradloglikelihood! != nothing
      push!(ifwhilebody, :(_job.pstate.gradloglikelihood = _job.sstate.pstateprime.gradloglikelihood))
    end
    if in(:gradlogprior, job.outopts[:monitor]) && job.parameter.gradlogprior! != nothing
      push!(ifwhilebody, :(_job.pstate.gradlogprior = _job.sstate.pstateprime.gradlogprior))
    end
  elseif vform == Multivariate
    push!(ifwhilebody, :(_job.pstate.value = copy(_job.sstate.pstateprime.value)))
    push!(ifwhilebody, :(_job.pstate.gradlogtarget = copy(_job.sstate.pstateprime.gradlogtarget)))
    if in(:gradloglikelihood, job.outopts[:monitor]) && job.parameter.gradloglikelihood! != nothing
      push!(ifwhilebody, :(_job.pstate.gradloglikelihood = copy(_job.sstate.pstateprime.gradloglikelihood)))
    end
    if in(:gradlogprior, job.outopts[:monitor]) && job.parameter.gradlogprior! != nothing
      push!(ifwhilebody, :(_job.pstate.gradlogprior = copy(_job.sstate.pstateprime.gradlogprior)))
    end
  end
  push!(ifwhilebody, :(_job.pstate.logtarget = _job.sstate.pstateprime.logtarget))
  if in(:loglikelihood, job.outopts[:monitor]) && job.parameter.loglikelihood! != nothing
    push!(ifwhilebody, :(_job.pstate.loglikelihood = _job.sstate.pstateprime.loglikelihood))
  end
  if in(:logprior, job.outopts[:monitor]) && job.parameter.logprior! != nothing
    push!(ifwhilebody, :(_job.pstate.logprior = _job.sstate.pstateprime.logprior))
  end
  if in(:accept, job.outopts[:diagnostics])
    push!(ifwhilebody, :(_job.sstate.update = true))
  end

  push!(
    whilebody,
    Expr(:if, :(_job.sstate.sprime && (rand() < _job.sstate.nprime/_job.sstate.n)), Expr(:block, ifwhilebody...))
  )

  push!(whilebody, :(_job.sstate.j += 1))
  push!(whilebody, :(_job.sstate.n += _job.sstate.nprime))
  push!(
    whilebody,
    :(
      _job.sstate.s =
      _job.sstate.sprime &&
      !uturn(
        _job.sstate.pstateplus.value,
        _job.sstate.pstateminus.value,
        _job.sstate.momentumplus,
        _job.sstate.momentumminus
      )
    )
  )

  push!(body, Expr(:while, :(_job.sstate.s && (_job.sstate.j < _job.sampler.maxndoublings)), Expr(:block, whilebody...)))

  dindex = findfirst(job.outopts[:diagnostics], :accept)
  if dindex != 0
    push!(body, :( _job.pstate.diagnosticvalues[$dindex] = _job.sstate.update))
  end

  dindex = findfirst(job.outopts[:diagnostics], :ndoublings)
  if dindex != 0
    push!(body, :( _job.pstate.diagnosticvalues[$dindex] = _job.sstate.j))
  end

  if !job.plain
    push!(body, :(produce()))
  end

  @gensym _iterate

  result = quote
    function $_iterate(_job::BasicMCJob)
      $(body...)
    end
  end

  result
end


using PyCall
using DFTK
import JSON
using LinearAlgebra

function ase_bulk(args...; kwargs...)
    ase_build = pyimport("ase.build")
    if first(args) == "SiO2"
        # Lattice parameters for α-quartz from
        # https://www.materialsproject.org/materials/mp-549166/
        # All lengths in Ångström (used by ASE)
        a = b = 4.98530291
        c = 5.47852971
        α = β = 90
        γ = 120

        silicon = [  # Fractional coordinates
            [0.000000  0.471138  0.833333],
            [0.471138  0.000000  0.166666],
            [0.528862  0.528862  0.500000],
        ]
        oxygen = [  # Fractional coordinates
            [0.148541  0.734505  0.046133],
            [0.265495  0.414036  0.712800],
            [0.585964  0.851459  0.379466],
            [0.734505  0.148541  0.953867],
            [0.414036  0.265495  0.287200],
            [0.851459  0.585964  0.620534],
        ]
        symbols = append!(fill("Si", 3), fill("O", 6))
        pyimport("ase").Atoms(symbols=symbols, cell=[a, b, c, α, β, γ], pbc=true,
                              scaled_positions=vcat(vcat(silicon, oxygen)...))
    elseif first(args) == "GaAs"
        # Lattice parameters for ZnS-type GaAs from
        # Haynes, William M., ed. (2011). CRC Handbook of Chemistry and Physics
        # (92nd ed.). CRC Press. ISBN 978-1439855119.
        a = 5.65315  # for zinc-blende cell
        ase_build.bulk("GaAs", "zincblende"; a=a, kwargs...)
    elseif first(args) == "AlN"
        a = b = 3.12858814
        c = 5.01695500
        α = β = 90
        γ = 120
        aluminium = [
            [1/3  2/3  0.99928700],
            [2/3  1/3  0.49928700],
        ]
        nitrogen = [
            [1/3  2/3  0.38071300],
            [2/3  1/3  0.88071300],
        ]
        symbols = ["Al", "Al", "N", "N"]
        pyimport("ase").Atoms(symbols=symbols, cell=[a, b, c, α, β, γ], pbc=true,
                              scaled_positions=vcat(vcat(aluminium, nitrogen)...))
    else
        ase_build.bulk(args...; kwargs...)
    end
end

function bulk(args...; kwargs...)
    atoms_ase = ase_bulk(args...; kwargs...)
    atoms = load_atoms(atoms_ase)
    atoms = [ElementPsp(el.symbol, psp=load_psp(el.symbol, functional="pbe")) => position
             for (el, position) in atoms]
    load_lattice(atoms_ase), atoms
end

function randomised_supercell(name; n_repeat=5)
    kwargs = ()
    if name == "Al"
        factor = 1/4  # 4 Al per repeat
        kwargs = (cubic=true, )
        amplitude = 1e-2
    elseif name == "GaAs"
        factor = 1    # One Ga and one As per repeat
        amplitude = 1e-2
    elseif name == "SiO2"
        factor = 1/3  # One Si and two O per repeat
        amplitude = 1e-2
    else
        error("Not implemented: $system")
    end
    @assert isinteger(n_repeat * factor)
    n_repeat = Int(n_repeat * factor)

    # Make supercell
    prim = ase_bulk(name; kwargs...)
    super = pyimport("ase.build").make_supercell(prim,
                                                 Array(Diagonal([1, 1, n_repeat])))

    # Introduce some random noise
    cartesian_pos = super.get_positions()
    ampl_Angstr = amplitude / DFTK.units.Å
    # noise = ampl_Angstr * randn(size(cartesian_pos))
    noise = 4ampl_Angstr .* (-0.5 .+ 0.5*rand(size(cartesian_pos)...))
    super.set_positions(cartesian_pos .+ noise)

    # Return DFTK datastructures
    atoms = [ElementPsp(el.symbol, psp=load_psp(el.symbol, functional="pbe")) => position
             for (el, position) in load_atoms(super)]
    load_lattice(super), atoms
end

function ase_surface(identifier; n_repeat=5, distance=nothing)
    # n_repeat gives the number of repeats in the surfaces
    # (i.e. n_repeat == 5 means 5 cells one material and 5 cells the other)
    ase_build = pyimport("ase.build")
    repeat = Array(Diagonal([1, 1, n_repeat]))  # repeat along z-dirn

    function get_distance(surface, user_set)  # in Å
        if isnothing(user_set)
            return maximum(abs, hcat(surface.cell...))
        else
            return user_set / DFTK.units.Å
        end
    end

    if identifier == "AlGaAs"
        gaas_110 = ase_lego("GaAs", (1, 1, 0), n_repeat=n_repeat)
        al_100 = ase_lego("Al", (1, 0, 0), n_repeat=n_repeat)
        return ase_build.stack(gaas_110, al_100 * (2, 1, 1), fix=0, maxstrain=1.7, distance=3.0)
    elseif identifier == "AlSiO2H"
        sio2h_110 = ase_lego("SiO2H", (1, 1, 0), cubic=false, n_repeat=n_repeat)
        ase_build.add_vacuum(sio2h_110, 1.0)
        sio2h_110.center()

        al_100 = ase_lego("Al", (1, 0, 0), n_repeat=n_repeat) * (2, 1, 1)
        ase_build.add_vacuum(al_100, 1.0)
        al_100.center()

        return ase_build.stack(sio2h_110, al_100, fix=0, maxstrain=1.6, distance=2.5)
    elseif identifier == "AlVac"
        al_100 = ase_lego("Al", (1, 0, 0), n_repeat=n_repeat)
        ase_build.add_vacuum(al_100, get_distance(al_100, distance))
        return al_100
    elseif identifier == "GaAsVac"
        gaas_110 = ase_lego("GaAs", (1, 1, 0), n_repeat=n_repeat)
        ase_build.add_vacuum(gaas_110, get_distance(gaas_110, distance))
        return gaas_110
    elseif identifier == "GaAsSiO2H"
        gaas_110 = ase_lego("GaAs", (1, 1, 0), n_repeat=n_repeat)
        sio2h_110 = ase_lego("SiO2H", (1, 1, 0), cubic=false, n_repeat=n_repeat)
        return ase_build.stack(gaas_110, sio2h_110, fix=0, maxstrain=0.7, distance=2.5)
    elseif identifier == "SiO2HVac"
        sio2h_110 = ase_lego("SiO2H", (1, 1, 0), cubic=false, n_repeat=n_repeat)
        ase_build.add_vacuum(sio2h_110, get_distance(sio2h_110, distance))
        return sio2h_110
    elseif identifier == "AlGaAsSiO2H"
        gaas_110  = ase_lego("GaAs", (1, 1, 0), n_repeat=n_repeat)
        sio2h_110 = ase_lego("SiO2H", (1, 1, 0), cubic=false, n_repeat=n_repeat)
        interm = ase_build.stack(gaas_110, sio2h_110, fix=0, maxstrain=0.7, distance=2.5)

        al_100 = ase_lego("Al", (1, 0, 0), n_repeat=n_repeat) * (2, 1, 1)
        return ase_build.stack(interm, al_100, fix=0, maxstrain=1.7, distance=2.5)
    else
        error("Unknown surface identifier: $identfier")
    end
end

function surface(args...; kwargs...)
    atoms_ase = ase_surface(args...; kwargs...)
    atoms = load_atoms(atoms_ase)
    atoms = [ElementPsp(el.symbol, psp=load_psp(el.symbol, functional="pbe")) => position
             for (el, position) in atoms]
    load_lattice(atoms_ase), atoms
end

function store_system(lattice, atoms, name, repeat)
    atnums = Int[]
    pspfiles = String[]
    positions = Vector{Float64}[]
    for (elem, pos) in atoms
        append!(atnums, fill(elem.Z, length(pos)))
        append!(pspfiles, fill(elem.psp.identifier, length(pos)))
        append!(positions, pos)
    end

    if !isdir(joinpath(@__DIR__, "systems"))
        mkdir(joinpath(@__DIR__, "systems"))
    end
    open(joinpath(@__DIR__, "systems/$(name)_$repeat.json"), "w") do fp
        save_dict = Dict(
            "lattice" => Array(lattice),
            "atnums" => atnums,
            "pspfiles" => pspfiles,
            "positions" => positions,
        )
        JSON.print(fp, save_dict)
    end
    nothing
end

function load_system(name, repeat)
    store = joinpath(@__DIR__, "systems/$(name)_$repeat.json")
    if !isfile(store)
        if name in ("Al", "SiO2", "GaAs")
            lattice, atoms = randomised_supercell(name, n_repeat=repeat)
        elseif name == "SiO2H"
            error("Sorry cannot build passivated SiO2")
        else
            lattice, atoms = surface(name, n_repeat=repeat)
        end
        store_system(lattice, atoms, name, repeat)
    end

    saved_dict = open(JSON.parse, store, "r")
    lattice = Mat3(hcat(saved_dict["lattice"]...))
    atoms = map(unique(saved_dict["atnums"])) do atnum
        pspid = saved_dict["pspfiles"][findfirst(atnum .== saved_dict["atnums"])]
        elem = ElementPsp(atnum, psp=load_psp(pspid))
        coords = Vec3.(saved_dict["positions"][atnum .== saved_dict["atnums"]])
        elem => coords
    end
    lattice, atoms
end

function ase_lego(system, miller; cubic=true, n_repeat=1)
    ase_build = pyimport("ase.build")
    ase_io = pyimport("ase.io")

    extra = ""
    if cubic
        extra = "_cubic"
    end
    mstr = prod(string, miller)

    store = joinpath(@__DIR__, "systems/ase_lego_$(system)$(extra)_$(mstr)_$(n_repeat).xyz")
    if !isfile(store)
        atoms = ase_bulk(system, cubic=cubic)

        factor = 1.0
        if system == "GaAs"
            factor = 0.5  # Two Ga and two As per layer
        elseif system == "SiO2"
            factor = 0.5  # Three Si and 6 O per layer
        elseif system == "Al"
            factor = 0.5  # 2 Al per layer
        else
            error("Not implemented: $system")
        end
        @assert isinteger(n_repeat * factor)
        n_repeat = Int(n_repeat * factor)
        atoms = ase_build.surface(atoms, miller, n_repeat, 0; periodic=true)
        ase_io.write(store, atoms)
    end

    ase_io.read(store)
end


@testset "IndicatorDifferentFrom2PseudoMILP: $(fct_type), $(T), $(A)" for fct_type in ["vector of variables", "vector affine function"], T in [Int, Bool, Float64], A in [MOI.ACTIVATE_ON_ZERO, MOI.ACTIVATE_ON_ONE]
    mock = MOIU.MockOptimizer(AbsoluteValueIndicatorPseudoMILPModel{T}())
    model = if A == MOI.ACTIVATE_ON_ZERO
        COIB.Indicator0DifferentFrom2PseudoMILP{T}(mock)
    elseif A == MOI.ACTIVATE_ON_ONE
        COIB.Indicator1DifferentFrom2PseudoMILP{T}(mock)
    end

    @test MOI.supports_constraint(model, MOI.VariableIndex, MOI.ZeroOne)
    @test MOI.supports_constraint(
        model,
        MOI.VectorAffineFunction{T},
        MOI.Indicator{A, MOI.EqualTo{T}},
    )
    @test MOIB.supports_bridging_constraint(
        model,
        MOI.VectorAffineFunction{T},
        MOI.Indicator{A, CP.DifferentFrom{T}},
    )

    x, _ = MOI.add_constrained_variable(model, MOI.ZeroOne())
    if T == Int
        y, _ = MOI.add_constrained_variable(model, MOI.Integer())
    elseif T == Bool
        y, _ = MOI.add_constrained_variable(model, MOI.ZeroOne())
    elseif T == Float64
        y = MOI.add_variable(model)
    else
        @assert false
    end

    fct = if fct_type == "vector of variables"
        MOI.VectorOfVariables([x, y])
    elseif fct_type == "vector affine function"
        MOIU.vectorize([x, y])
    else
        @assert false
    end
    c = MOI.add_constraint(model, fct, MOI.Indicator{A}(CP.DifferentFrom(zero(T))))

    @test MOI.is_valid(model, x)
    @test MOI.is_valid(model, c)

    bridge = MOIBC.bridges(model)[MOI.ConstraintIndex{MOI.ScalarAffineFunction{T}, CP.DifferentFrom{T}}(1)] # Why a 1 for this specific case? Usually, it's -1.

    @testset "Bridge properties" begin
        @test MOIBC.concrete_bridge_type(typeof(bridge), MOI.VectorOfVariables, CP.DifferentFrom{T}) == typeof(bridge)
        @test MOIB.added_constrained_variable_types(typeof(bridge)) == Tuple{Type}[]

        if T == Int
            @test MOIB.added_constraint_types(typeof(bridge)) == [
                (MOI.VectorAffineFunction{T}, CP.AbsoluteValue),
                (MOI.VectorAffineFunction{T}, MOI.Indicator{A, MOI.GreaterThan{T}}),
            ]
        elseif T == Bool
            @test MOIB.added_constraint_types(typeof(bridge)) == [
                (MOI.VectorAffineFunction{Bool}, MOI.Indicator{A, MOI.EqualTo{Bool}}),
            ]
        elseif T == Float64
            @test MOIB.added_constraint_types(typeof(bridge)) == [
                (MOI.VectorAffineFunction{T}, CP.AbsoluteValue),
                (MOI.VectorAffineFunction{T}, MOI.Indicator{A, CP.Strictly{MOI.GreaterThan{T}}}),
            ]
        else
            @assert false
        end

        @test MOI.get(bridge, MOI.NumberOfVariables()) == ((T == Bool) ? 0 : 1)
        @test MOI.get(bridge, MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, CP.AbsoluteValue}()) == ((T == Bool) ? 0 : 1)
        @test MOI.get(bridge, MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, MOI.Indicator{A, CP.Strictly{MOI.GreaterThan{T}}}}()) == ((T == Float64) ? 1 : 0)
        @test MOI.get(bridge, MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, MOI.Indicator{A, MOI.GreaterThan{T}}}()) == ((T == Int) ? 1 : 0)
        @test MOI.get(bridge, MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, MOI.Indicator{A, MOI.EqualTo{T}}}()) == ((T == Bool) ? 1 : 0)

        if T != Bool
            @test MOI.get(bridge, MOI.ListOfVariableIndices()) == [bridge.var_abs]
        end
        if T != Bool
            @test MOI.get(bridge, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, CP.AbsoluteValue}()) == [bridge.con_abs]
        end
        if T == Float64
            @test MOI.get(bridge, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, MOI.Indicator{A, CP.Strictly{MOI.GreaterThan{T}}}}()) == [bridge.con_indic]
        end
        if T == Int
            @test MOI.get(bridge, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, MOI.Indicator{A, MOI.GreaterThan{T}}}()) == [bridge.con_indic]
        end
        if T == Bool
            @test MOI.get(bridge, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, MOI.Indicator{A, MOI.EqualTo{T}}}()) == [bridge.con_indic]
        end
    end

    if T != Bool
        @testset "Absolute value" begin
            @test MOI.is_valid(model, bridge.con_abs)
            f = MOI.get(model, MOI.ConstraintFunction(), bridge.con_abs)
            @test length(f.terms) == 2
            @test MOI.get(model, MOI.ConstraintSet(), bridge.con_abs) == CP.AbsoluteValue()

            t1 = f.terms[1]
            @test t1.output_index == 1
            @test t1.scalar_term.coefficient === one(T)
            @test t1.scalar_term.variable == bridge.var_abs

            t1 = f.terms[2]
            @test t1.output_index == 2
            @test t1.scalar_term.coefficient === one(T)
            @test t1.scalar_term.variable == y
        end
    end

    if T == Float64
        @testset "Strictly greater than" begin
            @test MOI.is_valid(model, bridge.con_indic)
            f = MOI.get(model, MOI.ConstraintFunction(), bridge.con_indic)
            @test length(f.terms) == 2
            @test MOI.get(model, MOI.ConstraintSet(), bridge.con_indic) == MOI.Indicator{A}(CP.Strictly(MOI.GreaterThan(zero(T))))

            t1 = f.terms[1]
            @test t1.output_index == 1
            @test t1.scalar_term.coefficient == one(T)
            @test t1.scalar_term.variable == x

            t2 = f.terms[2]
            @test t2.output_index == 2
            @test t2.scalar_term.coefficient == one(T)
            @test t2.scalar_term.variable == bridge.var_abs
        end
    end

    if T == Int
        @testset "Greater than" begin
            @test MOI.is_valid(model, bridge.con_indic)
            @test MOI.get(model, MOI.ConstraintSet(), bridge.con_indic) == MOI.Indicator{A}(MOI.GreaterThan(one(T)))
            f = MOI.get(model, MOI.ConstraintFunction(), bridge.con_indic)
            @test length(f.terms) == 2

            t1 = f.terms[1]
            @test t1.output_index == 1
            @test t1.scalar_term.coefficient == one(T)
            @test t1.scalar_term.variable == x

            t2 = f.terms[2]
            @test t2.output_index == 2
            @test t2.scalar_term.coefficient == one(T)
            @test t2.scalar_term.variable == bridge.var_abs
        end
    end

    if T == Bool
        @testset "EqualTo" begin
            @test MOI.is_valid(model, bridge.con_indic)
            f = MOI.get(model, MOI.ConstraintFunction(), bridge.con_indic)
            @test length(f.terms) == 2
            @test MOI.get(model, MOI.ConstraintSet(), bridge.con_indic) == MOI.Indicator{A}(MOI.EqualTo(one(T)))

            t1 = f.terms[1]
            @test t1.output_index == 1
            @test t1.scalar_term.coefficient == one(T)
            @test t1.scalar_term.variable == x

            t2 = f.terms[2]
            @test t2.output_index == 2
            @test t2.scalar_term.coefficient == one(T)
            @test t2.scalar_term.variable == y
        end
    end
end


GLAbstraction.gl_convert{T}(::Type{T}, img::Images.Image) = gl_convert(T, Images.data(img))

_default{T <: Colorant, X}(main::Images.Image{T, 2, X}, s::Style, d::Dict) = _default(Images.data(main), s, d)
_default{T <: Colorant, X}(main::Signal{Images.Image{T, 2, X}}, s::Style, d::Dict) = _default(const_lift(Images.data, main), s, d)

"""
A matrix of colors is interpreted as an image
"""
_default{T <: Colorant}(main::MatTypes{T}, ::Style, data::Dict) = @gen_defaults! data begin
    image                 = main => (Texture, "image, can be a Texture or Array of colors")
    primitive::GLUVMesh2D = SimpleRectangle{Float32}(0f0, 0f0, size(value(main))...) => "the 2D mesh the image is mapped to. Can be a 2D Geometry or mesh"
    boundingbox           = GLBoundingBox(primitive)
    preferred_camera      = :orthographic_pixel
    shader                = GLVisualizeShader("fragment_output.frag", "uv_vert.vert", "texture.frag")
end
function _default{T <: Colorant}(main::VecTypes{T}, ::Style, data::Dict)
    @gen_defaults! data begin
        image                 = main => (Texture, "image, can be a Texture or Array of colors")
        primitive::GLUVMesh2D = SimpleRectangle{Float32}(0f0, 0f0, length(value(main)), 50f0) => "the 2D mesh the image is mapped to. Can be a 2D Geometry or mesh"
        boundingbox           = GLBoundingBox(primitive)
        preferred_camera      = :orthographic_pixel
        shader                = GLVisualizeShader("fragment_output.frag", "uv_vert.vert", "texture.frag")
    end
end

"""
A matrix of Intensities will result in a contourf kind of plot
"""
function _default{T <: Intensity}(main::MatTypes{T}, s::Style, data::Dict)
    main_v = value(main)
    @gen_defaults! data begin
        ranges = (0:size(main_v, 1), 0:size(main_v, 2))
    end
    x, y, xw, yh = first(ranges[1]), first(ranges[2]), last(ranges[1]), last(ranges[2])
    @gen_defaults! data begin
        intensity             = main => Texture
        color_map             = default(Vector{RGBA{U8}},s) => Texture
        primitive::GLUVMesh2D = SimpleRectangle{Float32}(x, y, xw-x, yh-y)
        color_norm            = const_lift(extrema2f0, main)
        stroke_width::Float32 = 0.05f0
        levels::Float32       = 5f0
        stroke_color          = RGBA{Float32}(1,1,1,1)
        boundingbox           = GLBoundingBox(primitive)
        shader                = GLVisualizeShader("fragment_output.frag", "uv_vert.vert", "intensity.frag")
        preferred_camera      = :orthographic_pixel
    end
end

"""
Float matrix with the style distancefield will be interpreted as a distancefield.
A distancefield is describing a shape, with positive values denoting the inside
of the shape, negative values the outside and 0 the border
"""
function _default{T <: AbstractFloat}(main::MatTypes{T}, s::style"distancefield", data::Dict)
    @gen_defaults! data begin
        distancefield = main => Texture
        shape         = DISTANCEFIELD
    end
    rect = SimpleRectangle{Float32}(0f0,0f0, size(value(main))...)
    _default((rect, Point2f0[0]), s, data)
end

export play
"""
With play, you can slice a 3D array along `timedim` at time `t`.
This can be used to treat a 3D array like a video and create an image stream from it.
"""
function play{T}(array::Array{T, 3}, timedim::Integer, t::Integer)
    index = ntuple(dim->dim==timedim ? t : Colon(), Val{3})
    array[index...]
end
"""
Turns an Image into a video stream
"""
function play{T<:Colorant, X}(img::Images.Image{T, 3, X})
    props = img.properties
    if haskey(props, "timedim")
        timedim = props["timedim"]
        return const_lift(play, img.data, timedim, loop(1:size(img, timedim)))
    end
    error("Image has no time channel")
end
"""
Plays a video stream from VideoIO.jl. You need to supply the image `buffer`,
which will be reused for better performance.
"""
function play{T}(buffer::Array{T, 2}, video_stream, t)
    eof(video_stream) && seekstart(video_stream)
    w,h     = size(buffer)
    buffer     = reinterpret(UInt8, buffer, (3, w,h))
    read!(video_stream, buffer) # looses type and shape
    return reinterpret(T, buffer, (w,h))
end

"""
Takes a 3D image and decides if it is a volume or an animated Image.
"""
function _default{T<:Colorant, X}(img::Images.Image{T, 3, X}, s::Style, data::Dict)
    props = img.properties
    if haskey(props, "timedim")
        timedim = props["timedim"]
        video_signal = const_lift(play, img.data, timedim, loop(1:size(img, timedim)))
        return _default(video_signal, s, data)
    elseif haskey(props, "pixelspacing")
        spacing = Vec3f0(map(float, img.properties["pixelspacing"]))
        pdims   = Vec3f0(size(img))
        dims    = pdims .* spacing
        dims    = dims/maximum(dims)
        data[:dimensions] = dims
    end
    _default(img.data, s, data)
end

"""
Takes a shader as a parametric function. The shader should contain a function stubb
like this:
```GLSL
uniform float arg1; // you can add arbitrary uniforms and supply them via the keyword args
float function(float x) {
 return arg1*sin(1/tan(x));
}
```
"""
_default(func::Shader, s::Style, data::Dict) = @gen_defaults! data begin
    color                 = default(RGBA, s)  => Texture
    dimensions            = (120f0,120f0)
    primitive::GLUVMesh2D = SimpleRectangle{Float32}(0f0,0f0, dimensions...)
    preferred_camera      = :orthographic_pixel
    boundingbox           = GLBoundingBox(primitive)
    shader                = GLVisualizeShader("fragment_output.frag", "parametric.vert", "parametric.frag", view=Dict(
         "function" => Compat.String(func.source)
     ))
end

#Volumes
typealias VolumeElTypes Union{Colorant, AbstractFloat}

const default_style = Style{:default}()

function _default{T<:VolumeElTypes}(a::VolumeTypes{T}, s::Style{:iso}, data::Dict)
    data = @gen_defaults! data begin
        isovalue  = 0.5f0
        algorithm = IsoValue
    end
     _default(a, default_style, data)
end

function _default{T<:VolumeElTypes}(a::VolumeTypes{T}, s::Style{:absorption}, data::Dict)
    data = @gen_defaults! data begin
        absorption = 1f0
        algorithm  = Absorption
    end
    _default(a, default_style, data)
end

immutable VolumePrerender
end
@compat function (::VolumePrerender)()
    GLAbstraction.StandardPrerender()()
    glEnable(GL_CULL_FACE)
    glCullFace(GL_FRONT)
end

_default{T<:VolumeElTypes}(main::VolumeTypes{T}, s::Style, data::Dict) = @gen_defaults! data begin
    intensities      = main => Texture
    dimensions       = Vec3f0(1)
    hull::GLUVWMesh  = AABB{Float32}(Vec3f0(0), dimensions)
    light_position   = Vec3f0(0.25, 1.0, 3.0)
    light_intensity  = Vec3f0(15.0)

    color_map        = default(Vector{RGBA}, s) => Texture
    color_norm       = color_map == nothing ? nothing : const_lift(extrema2f0, main)
    color            = color_map == nothing ? default(RGBA, s) : nothing

    algorithm        = MaximumIntensityProjection
    boundingbox      = hull
    absorption       = 1f0
    isovalue         = 0.5f0
    isorange         = 0.01f0
    shader           = GLVisualizeShader("fragment_output.frag", "util.vert", "volume.vert", "volume.frag")
    # prerender        = VolumePrerender()
    # postrender       = () -> begin
    #     glDisable(GL_CULL_FACE)
    # end
end


# FASTA Index
# ===========
#
# Index for random access to FASTA files.
#
# This file is a part of BioJulia.
# License is MIT: https://github.com/BioJulia/BioSequences.jl/blob/master/LICENSE.md

# http://www.htslib.org/doc/faidx.html
struct Index
    # offset for the record's sequence by header: See above specification
    names::Dict{String, Int}
    lengths::Vector{Int}
    offsets::Vector{Int}
    linebases::Vector{Int}
    linewidths::Vector{Int}
end

function Index(filepath::AbstractString)
    return open(read_faidx, filepath)
end

function read_faidx(input::IO)
    names = Dict{String, Int}()
    lengths = Int[]
    offsets = Int[]
    linebases = Int[]
    linewidths = Int[]
    for (i, line) in enumerate(eachline(input))
        values = split(chomp(line), '\t')
        name = values[1]
        length = parse(Int, values[2])
        offset = parse(Int, values[3])
        linebase = parse(Int, values[4])
        linewidth = parse(Int, values[5])
        names[name] = i
        push!(lengths, length)
        push!(offsets, offset)
        push!(linebases, linebase)
        push!(linewidths, linewidth)
    end
    return Index(names, lengths, offsets, linebases, linewidths)
end

function Base.getindex(index::Index, name::AbstractString)
    i = get(index.names, convert(String, name), nothing)
    if i === nothing
        throw(ArgumentError("sequence \"$(name)\" is not in the index"))
    elseif i == 1
        offset = 0
    # Else, we go to the previous one and calculate the length of the previous
    # sequence in bytes, then seek to right after that one.
    else
        prev_offset = index.offsets[i - 1]
        prev_len = index.lengths[i - 1]
        prev_linebase = index.linebases[i - 1]
        prev_linewidth = index.linewidths[i - 1]

        newline_len = prev_linewidth - prev_linebase
        len = cld(prev_len, prev_linebase) * newline_len + prev_len
        offset = prev_offset + len
    end
    return i
end

# Set the reading position of `input` to the starting position of the record `name`.
# TODO: Return the seek position to be consistent with Base?
function seekrecord(input::IO, index::Index, name::AbstractString)
    i = index[name]
    if i == 1
        offset = 0
    # Else, we go to the previous one and calculate the length of the previous
    # sequence in bytes, then seek to right after that one.
    else
        prev_offset = index.offsets[i - 1]
        prev_len = index.lengths[i - 1]
        prev_linebase = index.linebases[i - 1]
        prev_linewidth = index.linewidths[i - 1]

        # Note: newline_len may differ between sequences in the same file, as per
        # the specification.
        newline_len = prev_linewidth - prev_linebase
        len = cld(prev_len, prev_linebase) * newline_len + prev_len
        offset = prev_offset + len
    end
    seek(input, offset)
    return nothing
end


#=
Copyright (c) 2015, Intel Corporation

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of Intel Corporation nor the names of its contributors
      may be used to endorse or promote products derived from this software
      without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=#

using Latte
using FactCheck

facts("Testing embed_id layer") do
    net = Net(8)
    data, data_value = MemoryDataLayer(net, :data, (1,))
    embed = EmbedIDLayer(:embed, net, data, 100, 128)
    for i in 1:8
        data_value[1,i] = i
    end
    init(net)

    params = SolverParameters(
        lr_policy    = LRPolicy.Decay(.01f0, 5.0f-7),
        mom_policy   = MomPolicy.Fixed(0.9),
        max_epoch    = 300,
        regu_coef    = .0005)
    sgd = SGD(params)
    forward(net; solver=sgd)
    expected = get_buffer(net, :embedweights)
    actual = get_buffer(net, :embedvalue)
    for i in 1:8
        @fact actual[:, i][:] --> roughly(expected[i, :][:])
    end
    rand!(get_buffer(net, :embed∇))
    backward(net)
    expected = get_buffer(net, :embed∇)
    actual = get_buffer(net, :embed∇weights)
    for i in 1:8
        @fact sum(actual[i, :, :], 3)[:] --> roughly(expected[:, i][:])
    end
end

FactCheck.exitstatus()


# A brief test script for the JCM

include("jcm.jl")

function testplanting()
    tr1 = jcm.Tree(jcm.Species(1), 0, 0)
    tr2 = jcm.Tree(jcm.Species(2), 5, 3)
    tr3 = jcm.Tree(jcm.Species(3), 4, -1)
    jcm.planttree!(tr1)
    jcm.planttree!(tr2)
    jcm.planttree!(tr3)
    println(jcm.forest)
end

function testcompetition()
    # Expected outcome:
    # Update 1: tr2 & tr3 killed (overlapped by tr1)
    # Update 2: tr1 killed (overlapped by tr4)
    # Update 3: tr4 & tr5 survive
    tr1 = jcm.Tree(jcm.Species(1), 0, 0)
    tr2 = jcm.Tree(jcm.Species(2), -2, 3)
    tr3 = jcm.Tree(jcm.Species(3), 0, -2)
    tr4 = jcm.Tree(jcm.Species(4), 4, 7)
    tr5 = jcm.Tree(jcm.Species(5), 12, 27)
    tr1.size = 5
    tr2.size = 3
    tr3.size = 1
    tr4.size = 11
    tr5.size = 9
    jcm.planttree!(tr1)
    jcm.planttree!(tr2)
    jcm.planttree!(tr3)
    jcm.planttree!(tr4)
    jcm.planttree!(tr5)
    jcm.recordindividual(tr1)
    jcm.recordindividual(tr2)
    jcm.recordindividual(tr3)
    jcm.recordindividual(tr4)
    jcm.recordindividual(tr5)
    jcm.settings["verbosity"] = "Debug"
    jcm.settings["datafile"] = "jcm_test_data.csv"
    jcm.settings["datafreq"] = 1
    jcm.run(3, true)
    println(jcm.forest)
end


"""
    import_data(filename:: String ; transf = "DEFAULT")

extracts the desired data from a file *.EMTPFT.
# Arguments
- `transf = "DEFAULT"` : is the label for the transformation if Only 1 configuration was computed.
"""
function import_data(filename:: String ; transf = "DEFAULT")
    df            = DataFrame(readdlm(filename,' ')) # this line will have to be modified with DataFrames 0.11
    dfPabs,dfPrad = extract_data( df , transf)
    return dfPabs,dfPrad
end

"""
    import_data(filename:: Vector{String} ; transf = "DEFAULT")

puts everything in a single dataframe if given a list of files.
# Arguments
- `transf = "DEFAULT"` : is the label for the transformation if Only 1 configuration was computed.
"""
function import_data(filename:: Vector{String} ; transf = "DEFAULT")
    dfPabsv = DataFrame[]
    dfPradv = DataFrame[]
    for i = 1:length(filename)
        dfPabs_temp, dfPrad_temp = import_data(filename[i] ; transf=transf)
        dfPabsv = push!(dfPabsv,dfPabs_temp)
        dfPradv = push!(dfPradv,dfPrad_temp)
    end
    dfPabs = extract_data(dfPabsv , transf)
    dfPrad = extract_data(dfPradv , transf)
    return dfPabs,dfPrad
end

function extract_data(df :: DataFrame , transf)
    df1 = df[df[:x1] .== transf, :]
    nrows = size(df1, 1)
    ncols = size(df1, 2)

    Pabs  = zeros(Float64,Int(nrows/4),5)
    Prad  = zeros(Float64,Int(nrows/4),5)

    colnames    = [:x1 , :x2, :x3, :x4, :x5]
    newcolnames = [:Freq , :c11, :c12, :c21 ,:c22]

    count = 1
    for i = 1:4:nrows
        Pabs[count,1] = df1[i,2]
        Prad[count,1] = df1[i,2]
        for j=0:3
            Pabs[count,j+2]   = df1[i+j,4]
            Prad[count,j+2]   = df1[i+j,5]
        end
        count +=1
    end
    dfPabs = DataFrame(Pabs)
    dfPrad = DataFrame(Prad)

    rename!(dfPabs,colnames,newcolnames)
    rename!(dfPrad,colnames,newcolnames)

    return dfPabs, dfPrad
end

function extract_data(df :: Vector{DataFrame} , transf)
    df_temp = df[1]
    for i =2:length(df)
        append!(df_temp,df[i])
    end
    sort!(df_temp, cols = :Freq)
    return df_temp
end

function convert_to_array(dfPabs,dfPrad,columnname)
    dfPabs[:Freq] = w0.*dfPabs[:Freq]
    dfPrad[:Freq] = w0.*dfPrad[:Freq]
    wv   = dfPabs[:Freq]
    Prad = dfPrad[:,columnname]
    Pabs = dfPabs[:,columnname]
    return wv, Pabs, Prad
end

function write_data(flname,dict)
   for k in keys(dict)
        propname = k
        writetable(flname*"_"*propname*".dat", dict[k])
    end
end

"""
    simulation_data(filetype :: SIFlux,...)

Return and save (optional) the spectral flux and transfer function.

# Arguments
- `trans = "DEFAULT"` : the number of the transformation, trans = "DEFAULT" if no transformation is given
- `filename ::  Union{String,Vector{String}}` : can be single filename or an array of filenames for parallel computations
- `columnname` : column name. Example : [:c12]. Only use one name at the time.
"""
function simulation_data(filetype :: SIFlux, filename :: Union{String,Vector{String}}, columnname ,T1,T2, trans = "DEFAULT"; savefile = (false," "))
    dfPabs,dfPrad  = import_data(filename ; transf = transf)
    wv, Pabs, Prad = convert_to_array(dfPabs,dfPrad,columnname)

    qtrans = zeros(Float64,length(wv))
    qtrans = transfer_w.(T1,T2,wv,Prad)
    Qtrans = [wv qtrans]

    if savefile[1] == true
        dfQtrans = DataFrame(Qtrans)
        dc = Dict("Prad" => dfPrad ,"Pabs" => dfPabs , "Qtrans" => dfQtrans)
        write_data(savefile[2], dc)
    end
    return wv, Pabs, Prad, qtrans
end

"""
    simulation_data(filetype :: TotalFlux{:vsT},...)

Return and save (optional) total heat_transfer as a function temparature of one of the bodies and transfer function.
# Arguments
- `Tmin` : minimum value of temperature of body 2
- `Tmax` : maximum value of temperature of body 2
- `T1 = 0.0` : temperature of body 1. Set by default at 0.0K
"""
function simulation_data(filetype :: TotalFlux{:vsT}, filename:: Union{String,Vector{String}}, columnname,Tmin,Tmax, trans= "DEFAULT";T1=0.0, savefile = (false," "))
    dfPabs,dfPrad  = import_data(filename ; transf = transf)
    wv, Pabs, Prad = convert_to_array(dfPabs,dfPrad,columnname)

    Tempv = collect(linspace(Tmin,Tmax,100))
    q_tot = zeros(Float64,100)
    tt(T) = total_transfer(T1,T,wv,Prad)
    q_tot = tt.(Tempv)
    Qtrans = [Tempv q_tot]

    if savefile[1] == true
        dfQtrans = DataFrame(Qtrans)
        dc = Dict("Prad" => dfPrad ,"Pabs" => dfPabs , "Qtrans_vs_T_" => dfQtrans)
        write_data(savefile[2], dc)
    end
    return wv, Pabs, Prad, Tempv, Qtrans
end

"""
    simulation_data(filetype :: TotalFlux{:vsd},...)

Return and save (optional) total heat_transfer as a function separation distance and transfer function for each separation distance.
# Arguments
- `T1` : Temperature of body 1
- `T2` : Temperature of body 2
- `trans` : vector containing the transformation label
"""
function simulation_data(filetype :: TotalFlux{:vsd}, filename :: Union{String,Vector{String}}, columnname ,T1,T2, trans; savefile = (false," "))
    q_tot   = zeros(Float64,length(collect(trans)))

    for i in trans
        dfPabs,dfPrad  = import_data(filename ; transf = Float64(trans[i]))
        wv, Pabs, Prad = convert_to_array(dfPabs,dfPrad,columnname)
        q_tot[i]       = total_transfer(T1,T2,wv,Prad)
        if savefile[1] == true
            dc = Dict("Prad_trans="*"_$i" => dfPrad ,"Pabs_trans="*"_$i" => dfPabs)
            write_data(savefile[2], dc)
        end
    end

    Qtrans = [trans q_tot]
    if savefile[1] == true
        dfQtrans = DataFrame(Qtrans)
        dc = Dict("Qtrans_vs_dist_" => dfQtrans)
        write_data(savefile[2], dc)
    end
    return trans, q_tot
end

"""
    simulation_data(filetype :: TotalFlux{:vsth},...)

Return and save (optional) total heat_transfer as a function cylinder thickness.
# Arguments
- `T1` : temperature of body 1
- `T2` : temperature of body 2
"""
function simulation_data(filetype :: TotalFlux{:vsth}, filename:: Vector{String}, columnname,T1,T2, trans= "DEFAULT"; savefile = (false," "))

    lengthlist = length(filename)
    q_tot      = zeros(Float64,lengthlist)
    thv        = zeros(Float64,lengthlist)

    for i in 1:lengthlist
        dfPabs,dfPrad  = import_data(filename[i] ; transf = trans)
        wv, Pabs, Prad = convert_to_array(dfPabs,dfPrad,columnname)
        q_tot[i]       = total_transfer(T1,T2,wv,Prad)
        indexb         = searchindex(filename[i],"=")
        indexe         = searchindex(filename[i],"microns")
        thv[i]         = parse(Float64,filename[i][indexb+1:indexe-1])

        if savefile[1] == true
            dc = Dict("Prad_th="*"_$i" => dfPrad ,"Pabs_th="*"_$i" => dfPabs)
            write_data(savefile[2], dc)
        end

    end

    Qtrans = [thv q_tot]

    if savefile[1] == true
        dfQtrans = DataFrame(Qtrans)
        dc = Dict("Qtrans_vs_th_" => dfQtrans)
        write_data(savefile[2], dc)
    end
    return thv, q_tot
end


include("synapse.jl")
include("compartment.jl")
include("dendrite.jl")
include("axon.jl")
include("soma.jl")

# The Cell is the implementation of a Neuron.
# The cell generates streams of spikes and thus is a bit stream too.
# The stream is written to disc in spans.

mutable struct Cell <: AbstractBitStream
    # Contains a Soma.
    soma::AbstractSoma
    model::Model.ModelData
    samples::Model.Samples

    # Interfaces with InterNeurons (IN)
    # Dendrites Compartments also interact with INs

    function Cell(soma::AbstractSoma, model::Model.ModelData, samples::Model.Samples)
        o = new()
        o.soma = soma
        o.model = model
        o.samples = samples
        o
    end
end

function initialize!(cell::Cell)
    initialize!(cell.soma)
end

function reset!(cell::Cell)
    reset!(cell.soma)
end

function integrate!(cell::Cell, span_t::Int64, t::Int64)
    integrate!(cell.soma, span_t, t)
end

function output(cell::Cell)
    output(cell.soma)
end


module GridPortions

using Gridap
using Gridap.CellValuesGallery

export GridPortion

import Gridap: points
import Gridap: cells
import Gridap: celltypes
import Gridap: cellorders

struct GridPortion{D,Z} <: Grid{D,Z}
  oldgrid::Grid{D,Z}
  newcell_to_oldcell::IndexCellValue{Int}
  _newcells
  _newcelltypes
  _newcellorders
end

function GridPortion(oldgrid::Grid, newcell_to_oldcell)
  _newcell_to_oldcell = _setup_newcell_to_oldcell(newcell_to_oldcell)
  oldcells = cells(oldgrid)
  oldcelltypes = celltypes(oldgrid)
  oldcellorders = cellorders(oldgrid)
  _newcells = reindex(oldcells, _newcell_to_oldcell)
  _newcelltypes = reindex(oldcelltypes, _newcell_to_oldcell)
  _newcellorders = reindex(oldcellorders, _newcell_to_oldcell)
  GridPortion(
    oldgrid,_newcell_to_oldcell,_newcells,_newcelltypes,_newcellorders)
end

points(g::GridPortion) = points(g.oldgrid)

cells(g::GridPortion) = g._newcells

celltypes(g::GridPortion) = g._newcelltypes

cellorders(g::GridPortion) = g._newcellorders

_setup_newcell_to_oldcell(a::IndexCellValue) = a

_setup_newcell_to_oldcell(a::AbstractVector) = CellValueFromArray(a)

end # module


### Exports ###

export PatchClamp

### Types ###

# TODO: Change dir to be a type parameter
@with_kw struct PatchClamp{A<:AbstractArray} <: AbstractNode
  dir::Symbol
  buffer::A
  conns::Vector{SynapticConnection} = SynapticConnection[]
end
PatchClamp(dir::Symbol, sz::Vararg{Int,N} where N) =
  PatchClamp(dir, Float32, sz...)
PatchClamp(dir::Symbol, typ::Type{T}, sz::Vararg{Int,N} where N) where T =
  PatchClamp(dir=dir, buffer=zeros(T, sz...))

### Methods ###

Base.size(pc::PatchClamp) = size(pc.buffer)
Base.getindex(pc::PatchClamp, idx...) =
  getindex(pc.buffer, idx...)

reinit!(pc::PatchClamp) =
  fill!(pc.buffer, zero(eltype(pc.buffer)))

function addedge!(pc::PatchClamp, dstcont, dst, op, conf)
  @assert first(size(dstcont)) == first(size(pc.buffer)) "Input and output sizes must match"
  @assert pc.dir === :output "PatchClamp is not configured for output"
  if op == :input
    push!(pc.conns, SynapticConnection(dst, op, nothing))
  else
    error("PatchClamp cannot handle op: $op")
  end
end
deledge!(pc::PatchClamp, dst, op) =
  filter!(conn->conn.uuid!==dst, pc.conns)
deledges!(pc::PatchClamp) = empty!(pc.conns)
connections(pc::PatchClamp) = pc.conns

function eforward!(cont::CPUContainer{PC}, args) where PC<:PatchClamp
  pc = root(cont)

  if pc.dir === :output
    pc.buffer .= zero(eltype(pc.buffer))
    for conn in connections(pc)
      cont = args[findfirst(arg->arg[2]==conn.uuid, args)][3]
      inputs = cont[:]
      pc.buffer .+= inputs
    end
  end
end


@testset "wrap" begin
    @testset "basic" begin
        @system SWrap(Controller) begin
            a => 1 ~ preserve
            b(a) => 2a ~ track
            c(wrap(a)) => 2a' ~ track
        end
        s = instance(SWrap)
        @test s.b' == s.c' == 2
    end
end


module Fail

export FailUnsat
import ..OmegaCore

"Fails on condition"
struct FailUnsatAlg end
const FailUnsat = FailUnsatAlg()

# Set default randalg to fail
OmegaCore.defrandalg(args...) = FailUnsat

"""
`n` samples of` ω ∈ ΩT` from from model.  Throws error if such that `pred`
"""
function randsample(rng,
                    ΩT::Type{OT},
                    pred,
                    n,
                    alg::FailUnsatAlg) where {OT <: Ω}
  ωsamples = ΩT[]
  ωsamples = Vector{ΩT}(undef, n)
  accepted = 0
  for i = 1:n
    ω = ΩT()
    issat = pred(tagrng(ω, rng))
    @inbounds ωsamples[i] = ω
    # lens(Loop, (ω = ω, accepted = accepted, p = float(issat), i = i))
  end
  ωsamples
end

function Base.rand(rng::AbstractRNG,
                   x::RandVar,
                   n::Integer,
                   alg::FailUnsatAlg;
                   ΩT::Type{OT} = defΩ(alg)) where {OT <: Ω}
  pred = Omega.indomain(x)
  ωsamples = rand(rng, ΩT, pred, n, alg)
  map(ω -> apl(x, ω), ωsamples)
end

end


using CartesianGeneticProgramming
using Cambrian
using ArcadeLearningEnvironment
using ArgParse
using GR
import Cambrian.mutate
import Random

```
Playing Atari games using classic CGP on RAM values

If an individual is provided using --ind, an evaluation loop with rendering will
be performed. Otherwise, an evolution is launched. By default this uses seed=0
for each evaluation for a deterministic environment, but this can be removed for
a stochastic and more realistic result.
```

s = ArgParseSettings()
@add_arg_table s begin
    "--cfg"
    help = "configuration script"
    default = "cfg/atari_ram.yaml"
    "--game"
    help = "game rom name"
    default = "pong"
    "--seed"
    help = "random seed for evolution"
    arg_type = Int
    default = 0
    "--ind"
    help = "individual for evaluation"
    arg_type = String
    default = ""
end
args = parse_args(ARGS, s)

function imshowcolor(x::AbstractArray{UInt8,1}, dims)
    clearws()
    setviewport(0, dims[1] / dims[2], 0, 1)
    setwindow(0, 1, 0, 1)
    y = (zeros(UInt32, dims...) .+ 0xff) .<< 24
    img = UInt32.(x)
    @simd for i in 1:length(y)
        @inbounds y[i] += img[3*(i-1)+1] + img[3*(i-1)+2] << 8 + img[3*i] << 16
    end
    drawimage(0, 1, 0, 1, dims..., y)
    updatews()
end

function play_atari(ind::CGPInd, rom_name::String; seed=0, max_frames=18000, render=false)
    ale = ALE_new()
    setInt(ale, "random_seed", Cint(seed))
    #if render
    #    setBool(ale, "display_screen", true)
    #    setBool(ale, "sound", true)
    #end
    loadROM(ale, rom_name)
    actions = getMinimalActionSet(ale)
    reward = 0.0
    frames = 0
    while ~game_over(ale)
        ram = getRAM(ale) ./ typemax(UInt8)
        output = process(ind, ram)
        action = actions[argmax(output)]
        reward += act(ale, action)
        frames += 1
        if frames > max_frames
            break
        end
        if render
            x = getScreenRGB(ale)
            imshowcolor(x, (Int(getScreenWidth(ale)), Int(getScreenHeight(ale))))
        end
    end
    ALE_del(ale)
    [reward]
end

ale = ALE_new()
loadROM(ale, args["game"])
n_in = length(getRAM(ale))
n_out = length(getMinimalActionSet(ale))
ALE_del(ale)

cfg = get_config(args["cfg"]; game=args["game"], n_in=n_in, n_out=n_out)
Random.seed!(args["seed"])

if length(args["ind"]) > 0
    ind = CGPInd(cfg, read(args["ind"], String))
    reward = play_atari(ind, args["game"]; seed=args["seed"], render=true)
    println(reward)
else
    mutate(i::CGPInd) = goldman_mutate(cfg, i)
    fit(i::CGPInd) = play_atari(i, cfg.game)
    e = CGPEvolution(cfg, fit)
    run!(e)
end


using Requires
using ..Mesh.Grids: EveryDirection, VerticalDirection, HorizontalDirection
@init @require CUDAnative = "be33ccc6-a3ff-5ff2-a52e-74243cff1e17" begin
    using .CUDAnative
end
using KernelAbstractions.Extras: @unroll

const _M = Grids._M

@doc """
    knl_apply_filter!(::Val{dim}, ::Val{N}, ::Val{nstate}, ::Val{direction},
                      Q, ::Val{states}, filtermatrix,
                      elems) where {dim, N, nstate, states, direction}

Computational kernel: Applies the `filtermatrix` to the `states` of `Q`.

The `direction` argument is used to control if the filter is applied in the
horizontal and/or vertical reference directions.
""" knl_apply_filter!
@kernel function knl_apply_filter!(
    ::Val{dim},
    ::Val{N},
    ::Val{nstate},
    ::Val{direction},
    Q,
    ::Val{states},
    filtermatrix,
    elems,
) where {dim, N, nstate, direction, states}
    @uniform begin
        FT = eltype(Q)

        Nq = N + 1
        Nqk = dim == 2 ? 1 : Nq

        if direction isa EveryDirection
            filterinξ1 = filterinξ2 = true
            filterinξ3 = dim == 2 ? false : true
        elseif direction isa HorizontalDirection
            filterinξ1 = true
            filterinξ2 = dim == 2 ? false : true
            filterinξ3 = false
        elseif direction isa VerticalDirection
            filterinξ1 = false
            filterinξ2 = dim == 2 ? true : false
            filterinξ3 = dim == 2 ? false : true
        end

        nfilterstates = length(states)
    end

    s_filter = @localmem FT (Nq, Nq)
    s_Q = @localmem FT (Nq, Nq, Nqk, nfilterstates)
    l_Qfiltered = @private FT (nfilterstates,)

    e = @index(Group, Linear)
    i, j, k = @index(Local, NTuple)

    @inbounds begin
        s_filter[i, j] = filtermatrix[i, j]

        @unroll for fs in 1:nfilterstates
            l_Qfiltered[fs] = zero(FT)
        end

        ijk = i + Nq * ((j - 1) + Nq * (k - 1))

        @unroll for fs in 1:nfilterstates
            s_Q[i, j, k, fs] = Q[ijk, states[fs], e]
        end

        if filterinξ1
            @synchronize
            @unroll for n in 1:Nq
                @unroll for fs in 1:nfilterstates
                    l_Qfiltered[fs] += s_filter[i, n] * s_Q[n, j, k, fs]
                end
            end

            if filterinξ2 || filterinξ3
                @synchronize
                @unroll for fs in 1:nfilterstates
                    s_Q[i, j, k, fs] = l_Qfiltered[fs]
                    l_Qfiltered[fs] = zero(FT)
                end
            end
        end

        if filterinξ2
            @synchronize
            @unroll for n in 1:Nq
                @unroll for fs in 1:nfilterstates
                    l_Qfiltered[fs] += s_filter[j, n] * s_Q[i, n, k, fs]
                end
            end

            if filterinξ3
                @synchronize
                @unroll for fs in 1:nfilterstates
                    s_Q[i, j, k, fs] = l_Qfiltered[fs]
                    l_Qfiltered[fs] = zero(FT)
                end
            end
        end

        if filterinξ3
            @synchronize
            @unroll for n in 1:Nqk
                @unroll for fs in 1:nfilterstates
                    l_Qfiltered[fs] += s_filter[k, n] * s_Q[i, j, n, fs]
                end
            end
        end

        # Store result
        ijk = i + Nq * ((j - 1) + Nq * (k - 1))
        @unroll for fs in 1:nfilterstates
            Q[ijk, states[fs], e] = l_Qfiltered[fs]
        end

        @synchronize
    end
end

@kernel function knl_apply_TMAR_filter!(
    ::Val{nreduce},
    ::Val{dim},
    ::Val{N},
    Q,
    ::Val{filterstates},
    vgeo,
    elems,
) where {nreduce, dim, N, filterstates}
    @uniform begin
        FT = eltype(Q)

        Nq = N + 1
        Nqj = dim == 2 ? 1 : Nq

        nfilterstates = length(filterstates)
        nelemperblock = 1
    end

    l_Q = @private FT (nfilterstates, Nq)
    l_MJ = @private FT (Nq,)

    s_MJQ = @localmem FT (Nq * Nqj, nfilterstates)
    s_MJQclipped = @localmem FT (Nq * Nqj, nfilterstates)

    e = @index(Group, Linear)
    i, j = @index(Local, NTuple)

    @inbounds begin
        # loop up the pencil and load Q and MJ
        @unroll for k in 1:Nq
            ijk = i + Nq * ((j - 1) + Nqj * (k - 1))

            @unroll for sf in 1:nfilterstates
                s = filterstates[sf]
                l_Q[sf, k] = Q[ijk, s, e]
            end

            l_MJ[k] = vgeo[ijk, _M, e]
        end

        @unroll for sf in 1:nfilterstates
            MJQ, MJQclipped = zero(FT), zero(FT)

            @unroll for k in 1:Nq
                MJ = l_MJ[k]
                Qs = l_Q[sf, k]
                Qsclipped = Qs ≥ 0 ? Qs : zero(Qs)

                MJQ += MJ * Qs
                MJQclipped += MJ * Qsclipped
            end

            ij = i + Nq * (j - 1)

            s_MJQ[ij, sf] = MJQ
            s_MJQclipped[ij, sf] = MJQclipped
        end
        @synchronize

        @unroll for n in 11:-1:1
            if nreduce ≥ 2^n
                ij = i + Nq * (j - 1)
                ijshift = ij + 2^(n - 1)
                if ij ≤ 2^(n - 1) && ijshift ≤ Nq * Nqj
                    @unroll for sf in 1:nfilterstates
                        s_MJQ[ij, sf] += s_MJQ[ijshift, sf]
                        s_MJQclipped[ij, sf] += s_MJQclipped[ijshift, sf]
                    end
                end
                @synchronize
            end
        end

        @unroll for sf in 1:nfilterstates
            qs_average = s_MJQ[1, sf]
            qs_clipped_average = s_MJQclipped[1, sf]

            r = qs_average > 0 ? qs_average / qs_clipped_average : zero(FT)

            s = filterstates[sf]
            @unroll for k in 1:Nq
                ijk = i + Nq * ((j - 1) + Nqj * (k - 1))

                Qs = l_Q[sf, k]
                Q[ijk, s, e] = Qs ≥ 0 ? r * Qs : zero(Qs)
            end
        end
    end
end


DECODER = GNSSDecoderState(PRN = 1)
PREV_29 = 0
PREV_30 = 0
BUFFER_TEST_TRUE = [
[
    BitArray([1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1]),
    BitArray([1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0]),
    BitArray([0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
    BitArray([0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1]),
    BitArray([0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0]),
    BitArray([1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1]),
    BitArray([0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0]),
    BitArray([0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1]),
    BitArray([1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0]),
    BitArray([1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0])
    ],

[
    BitArray([1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1]),
    BitArray([1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0]),
    BitArray([0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0]),
    BitArray([0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1]),
    BitArray([1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1]),
    BitArray([1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0]),
    BitArray([1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0]),
    BitArray([0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1]),
    BitArray([1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1]),
    BitArray([1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0])
    ],

[
    BitArray([1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1]),
    BitArray([1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0]),
    BitArray([1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0]),
    BitArray([0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1]),
    BitArray([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1]),
    BitArray([0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1]),
    BitArray([1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1]),
    BitArray([0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]),
    BitArray([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1]),
    BitArray([1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0])
    ],

[
    BitArray([1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1]),
    BitArray([1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0]),
    BitArray([0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1]),
    BitArray([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0]),
    BitArray([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1]),
    BitArray([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0]),
    BitArray([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1]),
    BitArray([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0]),
    BitArray([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1]),
    BitArray([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0])
    ],

[
    BitArray([1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1]),
    BitArray([1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0]),
    BitArray([0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0]),
    BitArray([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0]),
    BitArray([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0]),
    BitArray([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0]),
    BitArray([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0]),
    BitArray([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0]),
    BitArray([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0]),
    BitArray([1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0])
    ]
]


#Define abstract planet type that functions as a wrapper of the CelestialBody struct of AstroDynBase
abstract type abstractCelestialBody end
abstract type abstractBarycenter <: abstractCelestialBody end
abstract type abstractPlanet <: abstractCelestialBody end
abstract type abstractNaturalSatellite <: abstractCelestialBody end
abstract type abstractStar <: abstractCelestialBody end

struct Planet <: abstractPlanet
    Name::Type{<:abstractPlanet}
    Environment::T where {T <: AbstractPlanetEnvironment}
end

struct NaturalSatellite <: abstractNaturalSatellite
    Name::Type{<:abstractNaturalSatellite}
    Environment::T where {T <: AbstractPlanetEnvironment}
end

struct Star <: abstractStar
    Name::Type{<:abstractStar}
end

#Unless otherwise defined, define initial values for each of the variables
j2(::Type{C}) where {C <: abstractCelestialBody} = 0.
j3(::Type{C}) where {C <: abstractCelestialBody} = 0.
j4(::Type{C}) where {C <: abstractCelestialBody} = 0.
frame(::Type{C}) where {C <: abstractCelestialBody} = "J2000"

#Wrap the functions
naif_id(Planet::T) where {T <: abstractCelestialBody} = naif_id(Planet.Name)
mu(Planet::T) where {T <: abstractCelestialBody} = mu(Planet.Name)
mean_radius(Planet::T) where {T <: abstractCelestialBody} = mean_radius(Planet.Name)
equatorial_radius(Planet::T) where {T <: abstractCelestialBody} = equatorial_radius(Planet.Name)
polar_radius(Planet::T) where {T <: abstractCelestialBody} = polar_radius(Planet.Name)
parent(Planet::T) where {T <: abstractCelestialBody} = parent(Planet.Name)
right_ascension(Planet::T) where {T <: abstractCelestialBody} = right_ascension(Planet.Name)
right_ascension_rate(Planet::T) where {T <: abstractCelestialBody} = right_ascension_rate(Planet.Name)
declination(Planet::T) where {T <: abstractCelestialBody} = declination(Planet.Name)
declination_rate(Planet::T) where {T <: abstractCelestialBody} = declination_rate(Planet.Name)
rotation_angle(Planet::T) where {T <: abstractCelestialBody} = rotation_angle(Planet.Name)
rotation_rate(Planet::T) where {T <: abstractCelestialBody} = rotation_rate(Planet.Name)
euler_angles(Planet::T) where {T <: abstractCelestialBody} = euler_angles(Planet.Name)
euler_derivatives(Planet::T) where {T <: abstractCelestialBody} = euler_derivatives(Planet.Name)
j2(Planet::T) where {T <: abstractCelestialBody} = j2(Planet.Name)
j3(Planet::T) where {T <: abstractCelestialBody} = j3(Planet.Name)
j4(Planet::T) where {T <: abstractCelestialBody} = j4(Planet.Name)
frame(Planet::T) where {T <: abstractCelestialBody} = frame(Planet.Name)

#Compute the flattening of a CelestialBody
flattening(Body::T) where {T <: abstractCelestialBody} = 1 - (polar_radius(Body) / equatorial_radius(Body))

#Helper state-transformation matrix function
function BodyFixed2InertialState(Body::T, t::S) where {T <: abstractCelestialBody, S <: Real}
    sxform(frame(Body),"J2000",Float64(t))
end

function Inertial2BodyFixedState(Body::T, t::S) where {T <: abstractCelestialBody, S <: Real}
    sxform("J2000",frame(Body),Float64(t))
end

function BodyFixed2Inertial3(Body::T, t::S) where {T <: abstractCelestialBody, S <: Real}
    pxform(frame(Body),"J2000",Float64(t))
end

function Inertial2BodyFixed3(Body::T, t::S) where {T <: abstractCelestialBody, S <: Real}
    pxform("J2000",frame(Body),Float64(t))
end

function CelestialBodyState(FromBody::T, ToBody::P, t::S) where {T,P <: abstractCelestialBody, S <: Real}
    spkezr(naif_id(FromBody), Float64(t), "J2000", naif_id(ToBody))[1]
end

function CelestialBodyPosition(FromBody::T, ToBody::P, t::S) where {T,P <: abstractCelestialBody, S <: Real}
    spkpos(naif_id(FromBody), Float64(t), "J2000", naif_id(ToBody))[1]
end


using ADFPEPS
using ADFPEPS:bulk,HORIZONTAL_RULES
using Random
using Test
using OMEinsum

@testset "ADFPEPS:bulk" begin
    Random.seed!(100)
    T = rand(2,2,4,2,2)
    # @show bulk(T)
    @show
end


"""
A note on the thinking behind this module:

we want to make it as simple as possible to optimise stuff, thats the goal here
in order to do that is it worth adding in some helper structs that store bits
of information that are useful?

It's optional, we use multiple dispatch so that familiar users can do it manually
but if you want something fast then we handle everything internally?

Previously the big problem was trying to do too much. We should make it play
nicely with something like pulse_sim.jl but the two should be separate. Pulse_sim
is for the simulation of pulses and pulse sequences efficiently.

This is for numerical optimal control and not that. Definitely need to have interop though
    so that I can keep writing pulse sequences there but use this to optimise them
    as we see fit in future.
"""

#abstract type Problem end

# abstract type ClosedSystem <: Problem end

"""
Store some pieces of information about your problem in this handy
and barely typed struct. We need to keep things flexible but also pleasant to use
"""
struct ProblemInfo
    # dimensions of your quantum system, useful to know to verify Hams make sense
    dim::Int
    # whether you've tried to optimise it
    opt::Bool
    # result of the optimisation attempt, useful for obvious reasons
    res

    # nervous about including these, maybe its better that the user really defines
    # all this stuff themselves. Don't want to over engineer things
    # drift Hamiltonian/Hamiltonians
    H_drift
    # control Hamiltonian/Hamiltonians
    H_ctrl
    # # I would like to keep a space for a full time dependent Hamiltonian, so that we
    # # can integrate stuff nicely!
    # full_Hamiltonian
    # timestep? would make it possible to use integrator quickly
    timestep
    # total time
    T
    # functional, here we can assume that things work
    functional
end

"""
I'm not sure that this is actually the right way to go, unless we copy the values
out of this struct and into the code directly and just use this to sort of save
    the state of the code as it stands.
"""


function contin_data(N_line,N_time,N_scen_normal,N_scen_contin,N_event)
    # contin: Line Capacity Limiting Coefficient 0~1| normal=0, full trip=1
    contin_normal = Array{Float64}(N_line,N_time,N_scen_normal)
    contin_normal[:] = 0
    if N_event == 1
        contin_7scen = Array{Float64}(N_line,N_time,7)
        contin_7scen[1:end] = 0
        contin_7scen[1,6:end,1] = 0.9
        contin_7scen[12,6:end,2] = 0.9
        contin_7scen[4,6:end,3] = 0.9
        contin_7scen[8,6:end,4] = 0.9
        contin_7scen[9,6:end,5] = 0.9
        contin_7scen[5,6:end,6] = 0.9
        contin_7scen[13,6:end,7] = 0.9

        contin_5scen = Array{Float64}(N_line,N_time,5)
        contin_5scen[1:end] = 0
        contin_5scen[1,6:end,1] = 0.9
        contin_5scen[4,6:end,2] = 0.9
        contin_5scen[5,6:end,3] = 0.9
        contin_5scen[12,6:end,4] = 0.9
        contin_5scen[13,6:end,5] = 0.9

        contin_1scen = Array{Float64}(N_line,N_time,1)
        contin_1scen[1:end] = 0
        contin_1scen[13,6:end,1] = 0.9
    elseif N_event == 2
        contin_7scen = Array{Float64}(N_line,N_time,7)
        contin_7scen[1:end] = 0
        contin_7scen[1,6:end,1] = 0.9
        contin_7scen[12,18:end,1] = 0.9
        contin_7scen[12,6:end,2] = 0.9
        contin_7scen[4,18:end,2] = 0.9
        contin_7scen[4,6:end,3] = 0.9
        contin_7scen[12,18:end,3] = 0.9
        contin_7scen[8,6:end,4] = 0.9
        contin_7scen[12,18:end,4] = 0.9
        contin_7scen[9,6:end,5] = 0.9
        contin_7scen[12,18:end,5] = 0.9
        contin_7scen[5,6:end,6] = 0.9
        contin_7scen[12,18:end,6] = 0.9
        contin_7scen[13,6:end,7] = 0.9
        contin_7scen[4,18:end,7] = 0.9

        contin_5scen = Array{Float64}(N_line,N_time,5)
        contin_5scen[1:end] = 0
        contin_5scen[1,6:end,1] = 0.9
        contin_5scen[8,6:end,1] = 0.9
        contin_5scen[1,6:end,2] = 0.9
        contin_5scen[9,6:end,2] = 0.9
        contin_5scen[4,6:end,3] = 0.9
        contin_5scen[12,6:end,3] = 0.9
        contin_5scen[4,6:end,4] = 0.9
        contin_5scen[13,6:end,4] = 0.9
        contin_5scen[3,6:end,5] = 0.9
        contin_5scen[10,6:end,5] = 0.9

        contin_1scen = Array{Float64}(N_line,N_time,1)
        contin_1scen[1:end] = 0
        contin_1scen[4,6:end,1] = 0.9
        contin_1scen[12,18:end,1] = 0.9

    elseif N_event == 3
        contin_5scen = Array{Float64}(N_line,N_time,5)
        contin_5scen[1:end] = 0

        contin_5scen[1,6:end,1] = 0.9
        contin_5scen[8,6:end,1] = 0.9
        contin_5scen[13,6:end,1] = 0.9

        contin_5scen[1,6:end,2] = 0.9
        contin_5scen[9,6:end,2] = 0.9
        contin_5scen[12,6:end,2] = 0.9

        contin_5scen[1,6:end,3] = 0.9
        contin_5scen[4,6:end,3] = 0.9
        contin_5scen[11,6:end,3] = 0.9

        contin_5scen[4,6:end,4] = 0.9
        contin_5scen[8,6:end,4] = 0.9
        contin_5scen[12,6:end,4] = 0.9

        contin_5scen[1,6:end,5] = 0.9
        contin_5scen[4,6:end,5] = 0.9
        contin_5scen[12,6:end,5] = 0.9
    end

    contin = Array{Float64}(N_line,N_time,N_scen_normal+N_scen_contin)
    contin[:] = 0
    if N_scen_contin == 7
        contin[:,:,N_scen_normal+1:end] = contin_7scen
    elseif N_scen_contin == 5
        contin[:,:,N_scen_normal+1:end] = contin_5scen
    elseif N_scen_contin == 1
        contin[:,:,N_scen_normal+1:end] = contin_1scen
    elseif N_scen_contin == 0
        contin = contin_normal
    else
        error("Thre is no preset contingency scenario data: go edit <contin_data.jl>")
    end

    return contin
end