text:
export sub2ind,
        statistics,
        prior,
        gaussian_kernel,
        kernel_density_estimate

"""
    function sub2ind(siz, x)

Convert a length n cartesian coordinate for indexing an n dimensional array into its linear (scalar) equivalent.

# Arguments

* siz::NTuple{Int}
  * Result of calling size(A) on the matrix of interest A.

* x::NTuple{Int}
  * length(siz)-length cartesian index to convert from
"""
function sub2ind(siz, x)
    k = vcat(1, cumprod(siz[1:end-1]))
    return dot(k, x .- 1) + 1
end

"""
    function statistics(vars, G, D::Matrix{Int})

Extract the statistics (ie counts) from a discrete dataset D assuming a Bayesian network with variables vars and structure G.  The dataset is an _n x m_ matrix, where n is the number of variables, and m is the number of data points.

# Returns

Returns an array *M* of length _n_.  The _i_th component consists of a _qᵢ x rᵢ_ matrix of counts.
"""
function statistics(vars, G, D)
    # n::number of variables
    n = size(D, 1)
    # rᵢ::for each variable i, number of discrete values it can take
    r = [vars[i].m for i in 1:n]

    # qᵢ: number of unique value combinations variable i's direct parents can take
    q = [ prod(Int64[r[j] for j in inneighbors(G,i)]) for i in 1:n ]

    # initialize counts matrix
    M = [zeros(q[i], r[i]) for i in 1:n]

    # for each data point (one per column of D)
    for o in eachcol(D)
        # for each variable in each sample
        for i in 1:n
            # actual category observed in this data point
            k = o[i]
            # parents of variable i
            parents = inneighbors(G,i)
            j=1

            # Find linear index of parents
            if !isempty(parents)
                j = sub2ind(r[parents], o[parents])
            end
            #increment mᵢⱼₖ
            M[i][j,k] += 1.0
        end
    end
    return M
end

"""
    function prior(vars, G)

Generate a matrix of Dirichlet priors {αᵢⱼₖ = 1 ∀ i, j, k} corresponding to a uniform prior distribution over the parameters of a bayesian network.

# Arguments

    * vars::Vector{Variables}
        The Variables of the discrete BayesianNetwork
    * G::DAG
        The bayesian directed acyclic graph structure
"""
function prior(vars, G)

    n = length(vars)

    r = [vars[i].m for i in 1:n]

    q = [prod([r[j] for j in inneighbors(G,i)]) for i in 1:n]

    return [ones(q[i], r[i]) for i in 1:n]
end

"""
    gaussian_kernel(b)

Get a scalar probability density function of a zero-mean, b standard deviation gaussian.
"""
gaussian_kernel(b) = x->pdf(Normal(0,b), x)

"""
    function kernel_density_estimate(φ, O)

"""
function kernel_density_estimate(φ, O)
    return x -> sum([φ(x - o) for o in O])/length(O)
end


text:
import ..Traits
using ..Traits: Trait
export Err, LogPdf, Mem, Intervene, Rng, Scope, Cond

# # Primitive Traits
struct Err end
struct LogPdf end
struct Mem end
struct Intervene end
struct Rng end
struct Scope end
struct Cond end

function symtotrait(x::Symbol)
  if x == :err
    Err
  elseif x == :logpdf
    LogPdf
  elseif x == :mem
    Mem
  elseif x == :intervene
    Intervene
  elseif x == :rng
    Rng
  elseif x == :scope
    Scope
  elseif x == :condition
    Cond
  else
    error("Unknown trait: $x")
  end
end

@generated function Traits.traits(k::Tags{K, V}) where {K, V}
  traits_ = map(symtotrait, K)
  Core.println(traits_)
  Trait{Union{traits_...}}()
end

@generated function Traits.traits(k::Type{Tags{K, V}}) where {K, V}
  traits_ = map(symtotrait, K)
  Trait{Union{traits_...}}()
end

ext: jl


text:
module test_stdlib_linearalgebra_det

using Test
using LinearAlgebra

A = [4 3;
     6 3]

@test det(A) == 4*3 - 3*6 == -6
@test det(exp(A)) ≈ exp(tr(A)) == 1096.6331584284585
@test sum(diag(A)) == tr(A) ≈ log(det(exp(A)))
@test tr(I - inv(A)) < tr(A - I)

A = [  4  12 -16;
      12  37 -43;
     -16 -43  98]
@test det(A) ≈ 36
@test det(2A) ≈ 288

end # module test_stdlib_linearalgebra_det

module test_stdlib_linearalgebra_eigen

using Test
using LinearAlgebra

A = [  4  12 -16;
      12  37 -43;
     -16 -43  98]

E1 = eigen(A)
E2 = eigen(2A)
@test E1.values ≈ [0.018804980460810916, 15.5039632294076, 123.47723179013143]
@test E2.values ≈ [0.03760996092162183 , 31.0079264588152, 246.95446358026285]
@test 2 * E1.values == E2.values
@test E1.vectors == E2.vectors

end # module test_stdlib_linearalgebra_eigen

module test_stdlib_linearalgebra_diagonal

using Test
using LinearAlgebra

A = [  4  12 -16;
      12  37 -43;
     -16 -43  98]
@test Diagonal(A) ==
      Diagonal(diag(A)) ==
      diagm(0=>diag(A)) ==
                           [  4   0   0;
                              0  37   0;
                              0   0  98]
@test I * A == A
@test I + A == Diagonal([1, 1, 1]) + A

end # module test_stdlib_linearalgebra_diagonal

module test_stdlib_linearalgebra_rank

using Test
using LinearAlgebra

@test rank(-5) == rank(5) == 1
@test rank(0) == 0
@test rank([ 0 0 0; 0 2 2; 0 3 3]) == 1
@test rank([ 0 0 0; 0 0 2; 0 3 3]) == 2

end # module test_stdlib_linearalgebra_rank

module test_stdlib_linearalgebra_cholesky

using LinearAlgebra
using Test

# https://en.wikipedia.org/wiki/Cholesky_decomposition#Example

A = [  4  12 -16;
      12  37 -43;
     -16 -43  98]

@test UpperTriangular(A) ==
      triu(A) ==
                            [  4  12 -16;
                               0  37 -43;
                               0   0  98]

@test LowerTriangular(A) ==
      tril(A) ==
                            [  4   0   0;
                              12  37   0;
                             -16 -43  98]
@test issymmetric(A)
@test A' == adjoint(A) == transpose(A) == A

C = cholesky(A)
@test C.L * C.U == A

end # module test_stdlib_linearalgebra_cholesky

module test_stdlib_linearalgebra_dot

using Test
using LinearAlgebra

@test dot([1, 3, -5], [4, -2, -1]) == 3
# 1 * 4 + 3 * -2 + -5 * -1

@test [1 2; 3 4] * [5 6; 7 8] == [19 22; 43 50]
# 1 * 5 + 2 * 7    1 * 6 + 2 * 8
# 3 * 5 + 4 * 7    3 * 6 + 4 * 8

end # module test_stdlib_linearalgebra_dot

module test_stdlib_linearalgebra_cross

using Test
using LinearAlgebra

@test cross([2, 3, 4], [5, 6, 7]) == [-3, 6, -3]
# 3 * 7 - 4 * 6
# 4 * 5 - 2 * 7
# 2 * 6 - 3 * 5

end # module test_stdlib_linearalgebra_cross

module test_stdlib_linearalgebra_norm

using Test
using LinearAlgebra

@test (norm ∘ normalize)(rand(10)) ≈ 1

end # module test_stdlib_linearalgebra_norm


text:
using HTTP, JSON2
using Mocking

function getForexAPI(endpoint::String)
    return "https://api-fxtrade.oanda.com/$endpoint"
end

function getHeaders()
    token = CarrotConfig["oanda_token"]
    return Dict(
        "Authorization" => "Bearer $token",
        "Accept-Datetime-Format" => "RFC3339",
        "Content-Type" => "application/json",
    )
end

function getAccounts()
    resp = @mock HTTP.request(
        "GET",
        getForexAPI("v3/accounts"),
        getHeaders();
        require_ssl_verification = false,
    )
    return JSON2.read(String(resp.body))
end

function getCandles(
    symbol::String,
    time::String;
    count::Int64 = 1500,
    complete::Bool = true,
)
    resp = @mock HTTP.request(
        "GET",
        getForexAPI("v3/instruments/$symbol/candles"),
        getHeaders();
        query = "granularity=$time&count=$count",
        readtimeout = 2,
    )
    respBody = JSON2.read(String(resp.body))
    if complete
        candles = filter(c -> c.complete == true, respBody.candles)
    else
        candles = respBody.candles
    end

    return create_time_series_metrix(candles)
end

function create_time_series_metrix(candles::Array{Any,1})::Array{Float64,2}
    candlesMatrix = Matrix{Float64}(undef, size(candles)[1], 5)
    for (index, candle) in enumerate(candles)
        mid = candle.mid
        item = [
            parse(Float64, mid.o),
            parse(Float64, mid.h),
            parse(Float64, mid.l),
            parse(Float64, mid.c),
            candle.volume,
        ]
        candlesMatrix[index, :] = item
    end
    return candlesMatrix
end