text:
# LibTOAST.jl: interface to the TOAST++ library
# Copyright (C) 2019 Samuel Powell

# Export integral enumerations (see mesh.h)
export BilinearIntegrals, FF, DD, BNDFF
export BilinearParamIntegrals, PFF, PDD, BNDPFF
export LinearParamIntegrals, P, PF, BNDPF

# Export special purpose assembly types
export Source, PointSource, GaussianSource
export SourceTypes, Neumann, Isotropic

# Methods
export assemble, assemble!

# The following enumerations are defined in mesh.h
"""
  BilinearIntegrals

An enumeration of integrals over products of two basis functions, supported for
sytsem matrix assembly.

# Integrals
* `FF`: ``∫ uᵢ(r) uⱼ(r) dΩ``
* `DD`: ``∫ ∇uᵢ(r) ⋅ ∇uⱼ(r) dΩ``
* `BNDFF`: ``∫ uᵢ(r) uⱼ(r) d(δΩ)``

Where ``uᵢ(r)`` is the basis function for the ``i``th node, and ``d(δΩ)``
indicates integration only over boundary nodes.
"""
@enum BilinearIntegrals FF=0 DD=1 BNDFF=12

"""
  BilinearParamIntegrals

An enumeration of integrals over products of two basis functions and a function
defined in the nodal basis, supported for sytsem matrix assembly.

# Integrals
* `PFF`: ``∑ₖ fₖ ∫ uᵢ(r) uⱼ(r) uₖ(r) dΩ``
* `PDD`: ``∑ₖ fₖ ∫ ∇uᵢ(r) ⋅ ∇uⱼ(r) uₖ(r) dΩ``
* `BNPDFF`: ``∑ₖ fₖ ∫ uᵢ(r) uⱼ(r) uₖ(r) d(δΩ)``

Where ``uᵢ(r)`` is the basis function for the ``i``th node, and ``d(δΩ)``
indicates integration only over boundary nodes.
"""
@enum BilinearParamIntegrals PFF=2 PDD=3 BNDPFF=4

"""
  BilinearParamIntegrals

An enumeration of integrals over a function defined in the nodal basis, optionally
in product with a basis function, supported for RHS assembly.

# Integrals
* `P`: ``∑ₖ fₖ ∫ uₖ(r) dΩ``
* `PF`: ``∑ₖ fₖ ∫ uᵢ(r) uₖ(r) dΩ``
* `BNPDF`: ``∑ₖ fₖ ∫ uᵢ(r) uₖ(r) d(δΩ)``

Where ``uᵢ(r)`` is the basis function for the ``i``th node, and ``d(δΩ)``
indicates integration only over boundary nodes.
"""
@enum LinearParamIntegrals P=0 PF=1 BNDPF=2

"""
    SourceTypes

An enumeration of source types, Isotropic, or Neumann.
"""
@enum SourceTypes Neumann Isotropic

@compat abstract type Source end

"""
  PointSource(mesh, centre, sourcetype=Isotropic)

Represent a unitary point source at a specified location in the mesh, used in
special-purpose RHS assembly functions.

# Constructor arguments
* `mesh`: a TOAST mesh upon which this source is applied
* `centre`: centre point of the Guassian
* `sourcetype::SourceTypes`: `Isotropic`, or `Neumann` (see SourceTypes).
"""
mutable struct PointSource <: Source

  mesh::Mesh
  centre::Vector{Float64}
  sourcetype::SourceTypes

  function PointSource(mesh::Mesh,
                       centre::Vector{Float64},
                       sourcetype::SourceTypes=Isotropic)

    @assert length(centre) == dimensions(mesh)
    return new(mesh, centre, sourcetype)

  end

end

"""
  GaussianSource(mesh, centre, width, sourcetype)

Represent a unitary Gausian source at a specified location, used in
special-purpose RHS assembly functions.

# Constructor arguments
* `mesh`: a TOAST mesh upon which this source is applied
* `centre`: centre point of the Guassian
* `width`: full-width half maximum of the Guassian source
* `sourcetype::SourceTypes`: `Isotropic`, or `Neumann` (see SourceTypes).
"""
mutable struct GaussianSource <: Source
  mesh::Mesh
  centre::Vector{Float64}
  width::Float64
  sourcetype::SourceTypes

  function GaussianSource(mesh::Mesh,
                          centre::Vector{Float64},
                          width::Float64,
                          sourcetype::SourceTypes)

    @assert length(centre) == dimensions(mesh)
    return new(mesh, centre, width, sourcetype)

  end

end

"""
    assemble(mesh, integral)

Construct a new system matrix and assemble the bilinear form over the mesh,
as specified by integral.

# Arguments
* `mesh::Mesh`: a TOAST mesh
* `integral::BilinearIntegrals`: the desired integral
"""
function assemble(mesh::Mesh, int::BilinearIntegrals)
  sysmat = SystemMatrix(mesh)
  assemble!(sysmat, int)
  return sysmat
end

"""
    assemble!(sysmat, integral)

Assemble the bilinear form over the mesh as specified by integral, and add the
result to the provided system matrix.

# Arguments
* `sysmat::SystemMatrix`: a TOAST system matrix
* `integral::BilinearIntegrals`: the desired integral
"""
function assemble!(sysmat::SystemMatrix, int::BilinearIntegrals)
  meshptr = sysmat.mesh.ptr
  icxx"""AddToSysMatrix (*$(meshptr), *$(sysmat.ptr), NULL, $(Cint(int)));"""
  return nothing
end

"""
    assemble(mesh, integral, parameter)

Construct a new system matrix and assemble the bilinear form over the mesh,
as specified by integral and associated parameter.

# Arguments
* `mesh::Mesh`: a TOAST mesh
* `integral::BilinearIntegrals`: the desired integral
* `parameter::NodalCoeff`: a function in the nodal basis
"""
function assemble(mesh::Mesh, int::BilinearParamIntegrals, param::NodalCoeff)
  sysmat = SystemMatrix(mesh)
  assemble!(sysmat, int, param)
  return sysmat
end

"""
    assemble!(sysmat, integral, parameter)

Assemble the bilinear form over the mesh as specified by integral and associated
parameter, add the result to the provided system matrix.

# Arguments
* `sysmat::SystemMatrix`: a TOAST system matrix
* `integral::BilinearIntegrals`: the desired integral
* `parameter::NodalCoeff`: a function in the nodal basis
"""
function assemble!(sysmat::SystemMatrix,
                   int::BilinearParamIntegrals,
                   param::NodalCoeff)

  @assert param.mesh == sysmat.mesh

  nprm = length(param)
  pprm = pointer(param.data)
  mode = Cint(int)
  nnd = nodecount(sysmat.mesh)

  if nprm != nnd
    error("Parameter vector length ($nprm) not equal to nodal basis ($nnd).")
  end

  icxx"""
    RVector prm($nprm, $pprm, SHALLOW_COPY);
    AddToSysMatrix (*$(sysmat.mesh.ptr), *$(sysmat.ptr), &prm, $mode);
  """

  return nothing

end

"""
    assemble(mesh, integral, parameter)

Construct a new RHS and assemble the linear form over the `mesh`, as specified
by the `integral` and associated `parameter`.

# Arguments
* `mesh::Mesh`: a TOAST mesh
* `integral::LinearParamIntegrals`: the desired integral
* `parameter::NodalCoeff`: a function in the nodal basis
"""
function assemble(mesh::Mesh, int::LinearParamIntegrals, param::NodalCoeff)
  rhs = zero(NodalCoeff, mesh)
  assemble!(rhs, int, param)
  return rhs
end

"""
    assemble!(rhs, integral, parameter)

Assemble the bilinear form over the mesh as specified by the `integral` and
associated `parameter`, add the result to the provided NodalCoeff vector `rhs`.

# Arguments
* `rhs::NodalCoeff`: a nodal coefficient vector
* `integral::LinearParamIntegrals`: the desired integral
* `parameter::NodalCoeff`: a function in the nodal basis
"""
function assemble!(rhs::NodalCoeff,
                   int::LinearParamIntegrals,
                   param::NodalCoeff)

  @assert param.mesh == rhs.mesh

  nprm = length(param)
  pprm = pointer(param.data)
  prhs = pointer(rhs.data)
  mode = Cint(int)
  nnd = nodecount(rhs.mesh)

  if nprm != nnd
    error("Parameter vector length ($nprm) not equal to nodal basis ($nnd).")
  end

  #
  # icxx"""
  #   RVector prm($nprm, $pprm, SHALLOW_COPY);
  #   RVector rhs($nprm, $prhs, SHALLOW_COPY);
  #   AddToRHS_elasticity (*$(rhs.mesh.ptr), rhs, &prm, $mode);
  # """

  error("Linear integral assembly not implemented")

  return nothing

end

"""
    assemble(source)

Special purpose assembly function to generate an right hand side source vector
from a source specification conforming to the `Sources` supertype.
"""
function assemble(source::T) where {T<:Source}

  rhs = zero(NodalCoeff, source.mesh)

  prhs = pointer(rhs.data)
  nvtx = nodecount(source.mesh)
  pcnt = pointer(source.centre)
  ncnt = length(source.centre)

  prof = Cint( T == PointSource ? 0 : 1)
  mode = Cint( source.sourcetype )

  width = (T == PointSource) ? 0.0 : source.width

  icxx"""
    // Copy the center location to a Toast pointer
    Point *cnt = NULL;

    if($ncnt == 2)
      cnt = new Point2D($(pcnt)[0], $(pcnt)[1]);

    if($ncnt == 3)
      cnt = new Point3D($(pcnt)[0], $(pcnt)[1], $(pcnt)[2]);

    SourceMode mode = $(mode) == 0 ? SRCMODE_NEUMANN : SRCMODE_ISOTROPIC;

    // Create a vector mapped to input buffer
    RVector qm($nvtx, $prhs, SHALLOW_COPY);

    // Build the vector, copy it to the linked buffer
    switch ($(prof))
    {
      case PROFILE_POINT:
        qm = QVec_Point(*$(source.mesh.ptr), *cnt, mode);
        break;
      case PROFILE_GAUSSIAN:
        qm = QVec_Gaussian(*$(source.mesh.ptr), *cnt, $(width), mode);
        break;
      case PROFILE_COSINE:
        qm = QVec_Cosine(*$(source.mesh.ptr), *cnt, $(width), mode);
        break;
    }

    delete cnt;
  """

  return rhs

end


text:
# Methods for the LinearQuadratic interface

abstract type AbstractLinearQuadraticModel <: AbstractMathProgModel end
export AbstractLinearQuadraticModel

#    writeproblem
#    freemodel!
#    optimize!

@define_interface begin
    LinearQuadraticModel
    setvarLB!
    setvarUB!
    setconstrLB!
    setconstrUB!
    setobj!
    changecoeffs!
    addvar!
    delvars!
    addconstr!
    delconstrs!
    addsos1!
    addsos2!
    writeproblem
    getvarLB
    getvarUB
    getconstrLB
    getconstrUB
    getobj
    getconstrmatrix
    numlinconstr
    getconstrsolution
    getreducedcosts
    getconstrduals
    getinfeasibilityray
    getunboundedray
    getsimplexiter, rewrap
    getbarrieriter, rewrap
    getnodecount, rewrap
    getbasis
end

# default addvar!, not adding to any existing constraints
addvar!(m::AbstractMathProgModel, collb, colub, objcoef) = addvar!(m, Int[], [], collb, colub, objcoef)

# Quadratic methods

@define_interface begin
    numquadconstr
    setquadobj!
    setquadobjterms!
    addquadconstr!
    getquadconstrsolution
    getquadconstrduals
    getquadinfeasibilityray
    getquadconstrRHS
    setquadconstrRHS!
end

function setquadobjterms!(m::AbstractLinearQuadraticModel, rowidx, colidx, quadval)
    (n = length(rowidx)) == length(colidx) == length(quadval) || error("Inconsistent input dimensions")
    nquadval = copy(quadval)
    for i in 1:length(rowidx)
        if rowidx[i] == colidx[i] # if on diagonal...
            nquadval[i] *= 2
        end
    end
    if applicable(setquadobj!, m, rowidx, colidx, nquadval)
        setquadobj!(m, rowidx, colidx, nquadval)
    else
        error("Solver does not support quadratic objectives")
    end
end

setquadobj!(m::AbstractLinearQuadraticModel,Q::Matrix) = setquadobj!(m,sparse(float(Q)))
function setquadobj!(m::AbstractLinearQuadraticModel,Q::SparseMatrixCSC{Float64})
    if issymmetric(Q) || istriu(Q)
        nnz_q = nnz(Q)
        qr = Array{Cint}(undef, nnz_q)
        qc = Array{Cint}(undef, nnz_q)
        qv = Array{Cdouble}(undef, nnz_q)
        k = 0
        colptr::Vector{Int} = Q.colptr
        nzval::Vector{Float64} = Q.nzval

        for i = 1:numvar(m)
            qi::Cint = convert(Cint, i)
            for j = colptr[i]:(colptr[i+1]-1)
                qj = convert(Cint, Q.rowval[j])
                if qi <= qj
                    k += 1
                    qr[k] = qi
                    qc[k] = qj
                    qv[k] = nzval[j]
                end
            end
        end
        if applicable(setquadobj!, m, qr, qc, qv)
            setquadobj!(m,qr[1:k],qc[1:k],qv[1:k])
        else
            error("Solver does not support quadratic objectives")
        end
    else
        error("Quadratic cost coefficient matrix Q is not symmetric or upper triangular")
    end
end

ext: jl


text:
macro timing(cond,code)
    return quote
        if $(esc(cond))
            $(esc(code))
       end
    end
end

macro asserting(cond, code)
    return quote
        if $(esc(cond))
            @assert $(esc(code))
        end
    end
end

#the AD types below
const TYPE_P = 0   #param node
const TYPE_V = 1    #variable node
const TYPE_O = 2
const TYPE_O1 = 3
const TYPE_O2 = 4
const TYPE_O3 = 5
const TYPE_O4 = 6
const TYPE_O5 = 7
const TYPE_O6 = 8
const TYPE_O7 = 9

type HessStorage{I,V}
    bh_type::I
    idxmap::Vector{I}
    HH::Vector{V}
    gnvar::I
    bhi::Vector{Vector{I}}
    bhv::Vector{Vector{V}}
    bh_idxes::Vector{I}
end

function call{I,V}(::Type{HessStorage{I,V}}, bhlen::I, gnvar::I;bh_type::I=1)
    hs = HessStorage{I,V}(bh_type, Vector{V}(), Vector{I}(), gnvar, Vector{Vector{I}}(bhlen), Vector{Vector{V}}(bhlen), Vector{I}(bhlen))
    for i=1:bhlen
        @inbounds hs.bhi[i] = Vector{I}()
        @inbounds hs.bhv[i] = Vector{V}()
    end
    fill!(hs.bh_idxes,0)
    return hs
end

type Tape{I,V}
    tt::Vector{I}
    tr::Vector{I}
    stklen::I
    nvar::I
    nvnode::I
    nnode::I
    immlen::I
    depth::I

    # nzg::I

    # timing stat
    debug::Bool
    enable_timing_stats::Bool
    ep_forward_time::V
    ep_recovery_time::V
    ep_structure::V
    ep_structure_recovery::V
    ep_findnz::V
    ep_n::I

    function Tape(tt::Vector{I}, tr::Vector{I}, stklen::I, gnvar::I, vnodes::I, nnodes::I, immlen::I, depth::I; with_timing=false, with_debug=false)
        tape = new(
            tt, tr, stklen, gnvar, vnodes, nnodes, immlen, depth,

            # -one(I),      #grad indicator

            with_debug, with_timing, zero(V), zero(V), zero(V), zero(V), zero(V), zero(I) #timing stat
            )
        finalizer(tape, tape_cleanup)
        return tape
    end

    function Tape()
        return new(Vector{I}(),Vector{I}(),0,0,0,0,0,0,
            false,false, zero(V), zero(V), zero(V), zero(V), zero(V), zero(I))
    end
end

function tape_analysize{I}(tt::Vector{I}, tr::Vector{I}, gnvar::I)
    @assert length(tr) == 0
    idx = one(I)
    mx_stklen = one(I)
    stklen = zero(I)
    mxn_times = one(I)
    vmax = zero(I)
    vnodes = zero(I)
    nnodes = zero(I)
    immlen = zero(I)
    n = [0,0,0,0,0,0,0,0,0]

    @inbounds while idx <= length(tt)
        @inbounds ntype = tt[idx]
        # @show ntype
        nnodes += 1
        if ntype == TYPE_P
            @assert false
        elseif ntype == TYPE_V
            n[TYPE_V] += 1
            vmax = max(vmax, tt[idx+1])
            vnodes += 1
            stklen +=1
            idx += 3
        elseif ntype == TYPE_O
            n[TYPE_O] +=1
            @assert tt[idx+3] != 0
            if OP[tt[idx+2]]==:*
                mxn_times = max(mxn_times,tt[idx+3])
            end
            stklen -= tt[idx+3]-1
            idx += 5
        elseif ntype == TYPE_O1
            n[TYPE_O1] += 1
            idx += 5
        elseif ntype == TYPE_O2
            n[TYPE_O2] += 1
            idx += 5
        elseif ntype == TYPE_O3
            n[TYPE_O3] += 1
            vmax = max(vmax,tt[idx+4])
            vnodes += 1
            stklen += 1
            idx += 6

        elseif ntype == TYPE_O4
            n[TYPE_O4] += 1
            vmax = max(vmax,tt[idx+4])
            vnodes += 1
            stklen += 1
            idx += 6

        elseif ntype == TYPE_O5
            n[TYPE_O5] += 1
            vmax = max(vmax,tt[idx+3])
            vnodes += 1
            idx += 5

        elseif ntype == TYPE_O6
            n[TYPE_O6] += 1
            vmax = max(vmax,tt[idx+3])
            vnodes += 1
            idx += 5
        elseif ntype == TYPE_O7
            n[TYPE_O7] += 1
            vmax = max(vmax,tt[idx+3])
            vnodes += 1
            stklen += 1
            idx += 5
        else
            @assert false
        end
        # @show vmax
        mx_stklen = max(mx_stklen, stklen)
    end
    @assert idx == length(tt) + 1
    @assert gnvar >= vmax && sum(n) == nnodes

    immlen = max(5, convert(I,(mxn_times-1)*mxn_times/2)+mxn_times)

    #updating ID
    nid = gnvar
    idx = one(I)
    istk = Vector{I}()
    @inbounds while(idx <= length(tt))
        @inbounds ntype = tt[idx]
        # @show istk
        # @show ntype
        if ntype == TYPE_P
            @assert false

        elseif ntype == TYPE_V
            push!(istk, tt[idx+1])
            idx += 3

        elseif ntype == TYPE_O
            nid += 1
            tt[idx+1] = nid
            num = tt[idx+3]
            istklen = length(istk)
            append!(tr,sub(istk,istklen-num+1:istklen))
            resize!(istk,istklen-num)
            push!(istk, nid)
            idx += 5

        elseif ntype == TYPE_O1
            nid += 1
            tt[idx+1] = nid
            push!(tr,pop!(istk))
            push!(istk,nid)
            idx += 5

        elseif ntype == TYPE_O2
            nid += 1
            tt[idx+1] = nid
            push!(tr,pop!(istk))
            push!(istk,nid)
            idx += 5

        elseif ntype == TYPE_O3
            nid += 1
            tt[idx+1] = nid
            push!(istk,nid)
            idx += 6

        elseif ntype == TYPE_O4
            nid += 1
            tt[idx+1] = nid
            push!(istk,nid)
            idx += 6

        elseif ntype == TYPE_O5
            nid += 1
            tt[idx+1] = nid
            push!(tr,pop!(istk))
            push!(istk,nid)
            idx += 5

        elseif ntype == TYPE_O6
            nid += 1
            tt[idx+1] = nid
            push!(tr,pop!(istk))
            push!(istk,nid)
            idx += 5

        elseif ntype == TYPE_O7
            nid += 1
            tt[idx+1] = nid
            push!(istk,nid)
            idx += 5
        else
            @assert false
        end
    end
    @assert length(istk) == 1 && idx == length(tt) + 1
    push!(tr,pop!(istk))
    @assert length(tr) == nnodes

    # @show nid, mxn, gnvar, vnodes, nnodes, immlen, stklen, mx_stklen
    return nid, mx_stklen, gnvar, vnodes, nnodes, immlen
end

function getMaxWorkingSize{I,V}(obj_tt::Tape{I,V},lag_tt::Tape{I,V}, cons_tt::Vector{Tape{I,V}})
    mx_vallen = max(obj_tt.nnode,lag_tt.nnode)
    mx_depth  = max(obj_tt.depth,lag_tt.depth)
    mx_stklen    = max(obj_tt.stklen,lag_tt.stklen)
    mx_immlen = max(obj_tt.immlen,lag_tt.immlen)
    # @show mx_vallen, mx_depth, mx_ops, mx_immlen, length(obj_tt.vals), length(obj_tt.stk), length(obj_tt.imm)

    for i=1:length(cons_tt)
        mx_immlen = max(mx_immlen,cons_tt[i].immlen)
        mx_vallen = max(mx_vallen,cons_tt[i].nnode)
        mx_depth = max(mx_depth,cons_tt[i].depth)
        mx_ops = max(mx_ops,cons_tt[i].stklen)
    end
    return mx_vallen, mx_depth, mx_ops, mx_immlen
end

function resizeHessStorage{I,V}(hs::HessStorage{I,V},bhlen::I,gnvar::I)
    @assert hs.bh_type == 1
    resize!(hs.bhi,bhlen)
    resize!(hs.bhv,bhlen)
    hs.gnvar = gnvar
    for i=1:bhlen
        @inbounds hs.bhi[i] = Vector{Int}()
        @inbounds hs.bhv[i] = Vector{Float64}()
    end
    resize!(hs.bh_idxes,bhlen)
    fill!(hs.bh_idxes,0)
end

##########################################################################################
#
# tape builder from a Julia Expr type
#   tape memory property is initialized
#
##########################################################################################

function tapeBuilderNoHess{I,V}(expr::Expr, pvals::Vector{V}, gnvar::I)
    depth = [zero(I)]
    tt = Vector{I}()
    tr = Vector{I}()
    tape_builder(expr,tt, pvals,depth)
    nid, stklen, gnvar, vnodes, nnodes, immlen = tape_analysize(tt, tr, gnvar)
    tape = Tape{I,V}(tt, tr, stklen, gnvar, vnodes, nnodes, immlen, depth[1])
    return tape
end

function appendMultParam{I,V}(tape::Tape{I,V},pholder::Vector{I})
    tt = tape.tt
    push!(tt, TYPE_O1)
    push!(tt, -1)
    push!(tt, S_TO_OC[:*])
    push!(tt, -2)
    push!(pholder,length(tt))
    push!(tt, TYPE_O1)
end

function appendTapeMultParam{I,V}(to::Tape{I,V}, from::Tape{I,V}, pvals::Vector{V},ttstarts::Vector{I},ttends::Vector{I},trends::Vector{I},pholder::Vector{I})
    to.stklen = max(to.stklen,from.stklen)
    @assert to.nvar == from.nvar
    tt = to.tt
    tr = to.tr
    to.nvnode = max(to.nvnode, from.nvnode)
    to.nnode = max(to.nnode,from.nnode)
    to.immlen = max(to.immlen, from.immlen)
    to.depth = max(to.depth,from.depth)

    push!(ttstarts, length(tt)+1)
    append!(to.tt, from.tt)
    push!(ttends,   length(tt))
    append!(to.tr, from.tr)
    push!(trends,   length(tr))

    push!(tt, TYPE_O1)
    push!(tt, -1)
    push!(tt, S_TO_OC[:*])
    push!(tt, -2)
    push!(pholder,length(tt))
    push!(tt, TYPE_O1)
end

function buildSumTape{I,V}(tt::Vector{I}, mx_depth::I, n::I, gnvar::I, hs::HessStorage{I,V})
    tr = Vector{I}()
    if n > 1
        push!(tt, TYPE_O)
        push!(tt, -1)
        push!(tt, S_TO_OC[:+])
        push!(tt, n)
        push!(tt, TYPE_O)
        mx_depth += 1
    end
    nid, stklen, gnvar, vnodes, nnodes, immlen = tape_analysize(tt, tr, gnvar)
    tape = Tape{I,V}(tt, tr, stklen, gnvar, vnodes, nnodes, immlen, mx_depth)
    resizeHessStorage(hs,nid,gnvar)
    return tape
end

function mergeTapes{I,V}(obj_tape::Tape{I,V}, tapes::Vector{Tape{I,V}}, pvals::Vector{V}, gnvar::I,hs::HessStorage{I,V})
    tt = Vector{I}()
    tr = Vector{I}()
    mx_depth = zero(I)

    #objective
    mx_depth = max(obj_tape.depth,mx_depth)
    append!(tt, obj_tape.tt)
    push!(pvals,1.0)

    push!(tt,TYPE_O1)
    push!(tt,-1)
    push!(tt,S_TO_OC[:*])
    push!(tt,length(pvals))
    push!(tt,TYPE_O1)

    #constraints
    for i=1:length(tapes)
        mx_depth = max(tapes[i].depth, mx_depth)
        append!(tt, tapes[i].tt)
        push!(pvals,1.0)

        push!(tt,TYPE_O1)
        push!(tt,-1)
        push!(tt,S_TO_OC[:*])
        push!(tt,length(pvals))
        push!(tt,TYPE_O1)
    end

    #adding all
    if length(tapes) > 0
        push!(tt, TYPE_O)
        push!(tt,-1)
        push!(tt,S_TO_OC[:+])
        push!(tt,length(tapes)+1)
        push!(tt, TYPE_O)
    end

    nid, stklen, gnvar, vnodes, nnodes, immlen = tape_analysize(tt, tr, gnvar)
    tape = Tape{I,V}(tt, tr, stklen, gnvar, vnodes, nnodes, immlen, mx_depth)
    resizeHessStorage(hs,nid,gnvar)

    return tape
end

type B_NODE{I,V}
    t::I
    vidx::I
    pval::V
end

function tape_builder{I,V}(expr,tt::Vector{I}, pvals::Vector{V}, depth::Vector{I}, d::I=0)
    # @show expr
    # @show d
    @assert length(depth) == 1
    d += 1
    depth[1] = max(depth[1],d)

    if isa(expr, Real)
        return B_NODE(0,0,expr)
    elseif isa(expr, Expr) && expr.head == :ref  #a JuMP variable
        @assert length(expr.args) == 2
        vidx = expr.args[2]
        return B_NODE(1,vidx,0.0)
    elseif isa(expr, Expr) && expr.head == :call
        op = expr.args[1]
        n = length(expr.args)-1
        @assert typeof(op)==Symbol
        if (n==1 || n==0) && op==:+
            #simpliy the expression eliminate 1-ary + node
            return tape_builder(expr.args[2],tt,pvals,depth,d)
        elseif op == :- && n==1
            node = tape_builder(expr.args[2],tt,pvals,depth,d)

            if node.t == 0
                return B_NODE(0,0,-node.pval)
            elseif node.t == 1
                push!(tt,TYPE_O7)
                push!(tt,-1)
                push!(tt.S_TO_OC[op])
                push!(tt,node.vidx)
                push!(tt,TYPE_O7)
                return B_NODE(9,0,0.0)
            elseif node.t == 2 || node.t == 3 || node.t == 4 || node.t==5 || node.t == 6 || node.t == 7 || node.t == 8 || node.t == 9
                push!(tt, TYPE_O)
                push!(tt,-1)
                push!(tt,S_TO_OC[op])
                push!(tt,n)
                push!(tt,TYPE_O)
                return B_NODE(2,0,0.0)
            else
                @assert false
            end
        elseif n == 1
            node = tape_builder(expr.args[2],tt,pvals,depth,d)
            if node.t == 0
                pval = eval(op)(node.pval)
                return B_NODE(0,0,pval)
            elseif node.t == 1
                push!(tt, TYPE_O7)
                push!(tt, -1)
                push!(tt, S_TO_OC[op])
                push!(tt, node.vidx)
                push!(tt, TYPE_O7)
                return B_NODE(9,0,0.0)
            elseif node.t == 2 || node.t == 3 || node.t == 4 || node.t==5 || node.t == 6 || node.t == 7 || node.t == 8 || node.t == 9
                push!(tt, TYPE_O)
                push!(tt, -1)
                push!(tt, S_TO_OC[op])
                push!(tt, n)
                push!(tt, TYPE_O)
                return B_NODE(2,0,0.0)
            else
                @assert false
            end
        elseif n == 2
            ln = tape_builder(expr.args[2],tt,pvals,depth,d)
            rn = tape_builder(expr.args[3],tt,pvals,depth,d)
            if ln.t == rn.t == 0
                pval = eval(op)(ln.pval,rn.pval)
                return B_NODE(0,0,pval)
            elseif ln.t == 0 && rn.t != 1
                push!(pvals, ln.pval)

                push!(tt,TYPE_O1)
                push!(tt,-1)
                push!(tt,S_TO_OC[op])
                push!(tt,length(pvals))
                push!(tt,TYPE_O1)
                return B_NODE(3,0,0.0)
            elseif ln.t == 0 && rn.t == 1
                push!(pvals, ln.pval)

                push!(tt,TYPE_O3)
                push!(tt, -1)
                push!(tt, S_TO_OC[op])
                push!(tt, length(pvals))
                push!(tt, rn.vidx)
                push!(tt, TYPE_O3)
                return B_NODE(5,0,0.0)
            elseif rn.t == 0 && ln.t != 1
                push!(pvals, rn.pval)

                push!(tt,TYPE_O2)
                push!(tt,-1)
                push!(tt,S_TO_OC[op])
                push!(tt,length(pvals))
                push!(tt,TYPE_O2)
                return B_NODE(4,0,0.0)
            elseif rn.t == 0 && ln.t == 1
                push!(pvals,rn.pval)

                push!(tt, TYPE_O4)
                push!(tt,-1)
                push!(tt,S_TO_OC[op])
                push!(tt,length(pvals))
                push!(tt,ln.vidx)
                push!(tt,TYPE_O4)
                return B_NODE(6,0,0.0)
            elseif ln.t == 1 && rn.t == 1
                push!(tt,TYPE_V)
                push!(tt,ln.vidx)
                push!(tt,TYPE_V)

                push!(tt,TYPE_V)
                push!(tt,rn.vidx)
                push!(tt,TYPE_V)

                push!(tt,TYPE_O)
                push!(tt,-1)
                push!(tt,S_TO_OC[op])
                push!(tt,n)
                push!(tt,TYPE_O)
                return B_NODE(2,0,0.0)
            elseif ln.t == 1 && rn.t != 1
                push!(tt,TYPE_O5)
                push!(tt,-1)
                push!(tt,S_TO_OC[op])
                push!(tt,ln.vidx)
                push!(tt,TYPE_O5)
                return B_NODE(7,0,0.0)
            elseif rn.t == 1 && ln.t != 1
                push!(tt,TYPE_O6)
                push!(tt, -1)
                push!(tt,S_TO_OC[op])
                push!(tt,rn.vidx)
                push!(tt,TYPE_O6)
                return B_NODE(8,0,0.0)
            else
                push!(tt,TYPE_O)
                push!(tt,-1)
                push!(tt,S_TO_OC[op])
                push!(tt,n)
                push!(tt,TYPE_O)
                return B_NODE(2,0,0.0)
            end
        else #n>2
            @assert (op == :* || op == :+) && n>2
            rstk = Vector{B_NODE}()
            pval = zero(V)
            pnode = zero(I)
            for i in 2:length(expr.args)
                node = tape_builder(expr.args[i],tt,pvals,depth,d)
                if node.t == 0
                    pnode==zero(I)? pval = node.pval : ( op == :+? pval += node.pval : pval *= node.pval )
                    pnode += 1
                elseif node.t == 1
                    push!(rstk,node)
                else
                    #nothing
                end
            end

            nv = length(rstk)
            on = n - pnode - nv

            if pnode == n
                return B_NODE(0,0,pval)
            elseif nv == 1
                if on == 1 && pnode == 0
                    push!(tt,TYPE_O5)
                    push!(tt,-1)
                    push!(tt,S_TO_OC[op])
                    push!(tt,rstk[1].vidx)
                    push!(tt,TYPE_O5)
                    return B_NODE(7,0,0.0)
                elseif on == 1 && pnode > 0
                    push!(tt,TYPE_O5)
                    push!(tt,-1)
                    push!(tt,S_TO_OC[op])
                    push!(tt,rstk[1].vidx)
                    push!(tt,TYPE_O5)

                    push!(pvals, pval)
                    push!(tt,TYPE_O1)
                    push!(tt,-1)
                    push!(tt,S_TO_OC[op])
                    push!(tt,length(pvals))
                    push!(tt,TYPE_O1)
                    return B_NODE(3,0,0.0)
                elseif on > 1 && pnode == 0
                    push!(tt,TYPE_O)
                    push!(tt,-1)
                    push!(tt,S_TO_OC[op])
                    push!(tt,on)
                    push!(tt,TYPE_O)

                    push!(tt,TYPE_O5)
                    push!(tt,-1)
                    push!(tt,S_TO_OC[op])
                    push!(tt,rskt[1].vidx)
                    push!(tt,TYPE_O5)
                    return B_NODE(7,0,0.0)
                elseif on == 0
                    @assert pnode > 0
                    push!(pvals,pval)
                    push!(tt,TYPE_O3)
                    push!(tt,-1)
                    push!(tt,S_TO_OC[op])
                    push!(tt,length(pvals))
                    push!(tt,rstk[1].vidx)
                    push!(tt,TYPE_O3)
                    return B_NODE(5,0,0.0)
                else
                    # @show expr, pnode, nv , on , n
                    @assert on > 1 && pnode > 1 && nv == 1
                    push!(tt,TYPE_O)
                    push!(tt,-1)
                    push!(tt,S_TO_OC[op])
                    push!(tt,on)
                    push!(tt,TYPE_O)

                    push!(tt,TYPE_O5)
                    push!(tt,-1)
                    push!(tt,S_TO_OC[op])
                    push!(tt,rskt[1].vidx)
                    push!(tt,TYPE_O5)

                    push!(pvals, pval)
                    push!(tt,TYPE_O1)
                    push!(tt,-1)
                    push!(tt,S_TO_OC[op])
                    push!(tt,length(pvals))
                    push!(tt,TYPE_O1)
                    return B_NODE(3,0,0.0)
                end
            elseif nv > 1
                for item in rstk
                    push!(tt, TYPE_V)
                    push!(tt, item.vidx)
                    push!(tt, TYPE_V)
                end
                @assert n-pnode > 1
                push!(tt,TYPE_O)
                push!(tt,-1)
                push!(tt,S_TO_OC[op])
                push!(tt,n-pnode)
                push!(tt,TYPE_O)

                if pnode > 0
                    push!(pvals, pval)
                    push!(tt, TYPE_O1)
                    push!(tt,-1)
                    push!(tt, S_TO_OC[op])
                    push!(tt, length(pvals))
                    push!(tt, TYPE_O1)
                    return B_NODE(3,0,0)
                else
                    @assert pnode == 0
                    return B_NODE(2,0,0.0)
                end
            elseif nv == 0
                if on == 1
                    @assert pnode > 1
                    push!(pvals, pval)
                    push!(tt, TYPE_O1)
                    push!(tt,-1)
                    push!(tt, S_TO_OC[op])
                    push!(tt, length(pvals))
                    push!(tt, TYPE_O1)
                    return B_NODE(3,0,0)
                else
                    @assert on > 1
                    @assert n-pnode > 0
                    push!(tt,TYPE_O)
                    push!(tt,-1)
                    push!(tt,S_TO_OC[op])
                    push!(tt,n-pnode)
                    push!(tt,TYPE_O)

                    if pnode > 0
                        push!(pvals, pval)
                        push!(tt, TYPE_O1)
                        push!(tt,-1)
                        push!(tt, S_TO_OC[op])
                        push!(tt, length(pvals))
                        push!(tt, TYPE_O1)
                        return B_NODE(3,0,0)
                    else
                        # @show expr, pnode, nv , on , n
                        @assert pnode == 0 && on == n
                        return B_NODE(2,0,0.0)
                    end
                end
            end
        end
    else
        @show "error !"
        dump(expr)
        assert(false)
    end
    d -= 1
    nothing
end

##########################################################################################
#
# tape builder from types
#
##########################################################################################
function tapeBuilder{I}(data::Array{I,1}, t::I=1)
    tape = Tape{I,Float64}(tt=data, bh_type=t)
    tape_analysize(tape)
    return tape
end

##########################################################################################
#
# operator overload interface
#
##########################################################################################
immutable AD{I}
    data::Vector{I}
end

function AD_V{V}(vvals::Vector{V}, val::V) #provide variable value
    push!(vvals,val)
    I = typeof(length(vvals))
    this = AD{I}(Array{I,1}())
    push!(this.data,TYPE_V)
    push!(this.data,length(vvals))
    push!(this.data,TYPE_V)
    return this
end
function AD_V{I}(idx::I) #without variable value
    this = AD{I}(Array{I,1}())
    push!(this.data,TYPE_V)
    push!(this.data,-1)
    push!(this.data,idx)
    push!(this.data,TYPE_V)
    return this
end
function AD_P{V}(pvals::Vector{V},val)
    push!(pvals,val)
    I = typeof(length(pvals))
    this = AD{I}(Array{I,1}())
    push!(this.data,TYPE_P)
    push!(this.data,-1)
    push!(this.data,length(pvals))
    push!(this.data,TYPE_P)
    return this
end

function AD_O{I}(s::Symbol,l::AD{I})
    this = AD{I}(Array{I,1}())
    append!(this.data,l.data)
    push!(this.data,TYPE_O)
    push!(this.data,-1)
    push!(this.data,S_TO_OC[s])
    push!(this.data,1) #1 operand simply
    push!(this.data,TYPE_O)
    return this
end

function AD_O{I}(s::Symbol,l::AD{I},r::AD{I})
    AD_O(s,tuple(l,r))
end

function AD_O{I,N}(s::Symbol,args::NTuple{N,AD{I}})
    # @show s
    # @show args
    # @show N
    # @show n
    assert(N>1)
    this = AD{I}(Vector{I}())
    @simd for i = 1:1:N
        append!(this.data,args[i].data)
    end
    push!(this.data,TYPE_O)
    push!(this.data,-1)
    push!(this.data,S_TO_OC[s])
    push!(this.data,N)
    push!(this.data,TYPE_O)
    return this
end

function Base.show(io::IO,m::AD)
    print(io, m.data)
end

function tape_cleanup{I,V}(tape::Tape{I,V})
    #output timing
    @timing tape.enable_timing_stats begin
        f = open("ep_forward_time.txt","w")
        writedlm(f, tape.ep_forward_time)
        close(f)

        f = open("ep_recovery_time.txt","w")
        writedlm(f, tape.ep_recovery_time)
        close(f)

        f = open("ep_n.txt","w")
        writedlm(f,tape.ep_n)
        close(f)

        f = open("ep_structure.txt","w")
        writedlm(f, tape.ep_structure)
        close(f)

        f = open("ep_structure_recovery.txt","w")
        writedlm(f, tape.ep_structure_recovery)
        close(f)

        f = open("tt.txt","w")
        writedlm(f, tape.tt)
        close(f)

        f = open("tr.txt","w")
        writedlm(f, tape.tr)
        close(f)

        f = open("ep_findnz.txt","w")
        writedlm(f, tape.ep_findnz)
        close(f)

        if tape.bh_type == 1
            f = open("ep_bhi.txt","w")
            v = Vector{I}(length(tape.bhi))
            for i =1:length(tape.bhi)
                v[i] = length(tape.bhi[i])
                for j = 1:length(tape.bhi[i])
                    write(f, "$(tape.bhi[i][j]) ")
                end
                write(f,"\n")
            end
            close(f)

            f = open("ep_num_edges.txt","w")
            writedlm(f, v)
            close(f)
        end
    end
end


text:
type Model
    g::Graph
end

function Model{T}(::Type{T}, hsize::Int)
    x = Var(rand(T,784,10))
    y = Var(rand(1:1,1,10))
    h = Linear(T,784,hsize)(x)
    h = relu(h)
    h = Linear(T,hsize,hsize)(h)
    h = relu(h)
    h = Linear(T,hsize,10)(h)
    g = Graph([x], [h])
    Model(g)
end

function (m::Model)(x::Var, y=nothing)
    z = m.g(x)
    y == nothing ? argmax(z,1) : crossentropy(y,z)
end

max_stars_repo_name: hshindo/Merlin-Examples

max_issues_repo_name: hshindo/Merlin-Examples

max_forks_repo_name: hshindo/Merlin-Examples


text:
export KNNRegressor, KNNClassifier

import MLJBase: @mlj_model, metadata_model, metadata_pkg
using Distances

import NearestNeighbors

const NN = NearestNeighbors

const KNNRegressorDescription =
    """
    K-Nearest Neighbors regressor: predicts the response associated with a new point
    by taking an average of the response of the K-nearest points.
    """

const KNNClassifierDescription =
    """
    K-Nearest Neighbors classifier: predicts the class associated with a new point
    by taking a vote over the classes of the K-nearest points.
    """

const KNNFields =
    """
    ## Keywords

    * `K=5`                 : number of neighbors
    * `algorithm=:kdtree`   : one of `(:kdtree, :brutetree, :balltree)`
    * `metric=Euclidean()`  : a `Metric` object for the distance between points
    * `leafsize=10`         : at what number of points to stop splitting the tree
    * `reorder=true`        : if true puts points close in distance close in memory
    * `weights=:uniform`    : one of `(:uniform, :distance)` if `:uniform` all neighbors are
                              considered as equally important, if `:distance`, closer neighbors
                              are proportionally more important.

    See also the [package documentation](https://github.com/KristofferC/NearestNeighbors.jl).
    """

"""
KNNRegressoor(;kwargs...)

$KNNRegressorDescription

$KNNFields
"""
@mlj_model mutable struct KNNRegressor <: MLJBase.Deterministic
    K::Int            = 5::(_ > 0)
    algorithm::Symbol = :kdtree::(_ in (:kdtree, :brutetree, :balltree))
    metric::Metric    = Euclidean()
    leafsize::Int     = 10::(_ ≥ 0)
    reorder::Bool     = true
    weights::Symbol   = :uniform::(_ in (:uniform, :distance))
end

"""
KNNRegressor(;kwargs...)

$KNNClassifierDescription

$KNNFields
"""
@mlj_model mutable struct KNNClassifier <: MLJBase.Probabilistic
    K::Int            = 5::(_ > 0)
    algorithm::Symbol = :kdtree::(_ in (:kdtree, :brutetree, :balltree))
    metric::Metric    = Euclidean()
    leafsize::Int     = 10::(_ ≥ 0)
    reorder::Bool     = true
    weights::Symbol   = :uniform::(_ in (:uniform, :distance))
end

const KNN = Union{KNNRegressor, KNNClassifier}

MMI.reformat(::KNN, X) = (MMI.matrix(X, transpose=true),)
MMI.reformat(::KNN, X, y) = (MMI.matrix(X, transpose=true), y)
MMI.reformat(::KNN, X, y, w) =
    error("$Weights must be abstract vectors with `AbstractFloat` "*
          "or `Integer` eltype, or be `nothing`. ")
MMI.reformat(::KNN, X, y, w::Union{Nothing,AbstractVector{<:AbstractFloat}}) =
    (MMI.matrix(X, transpose=true), y, w)
MMI.reformat(::KNN, X, y, w::AbstractVector{<:Integer}) =
    (MMI.matrix(X, transpose=true), y, float.(w))

MMI.selectrows(::KNN, I, Xmatrix) =
    (view(Xmatrix, :, I),)
MMI.selectrows(::KNN, I, Xmatrix, y) =
    (view(Xmatrix, :, I), view(y, I))
MMI.selectrows(::KNN, I, Xmatrix, y, w) =
    (view(Xmatrix, :, I), view(y, I), view(w, I))
MMI.selectrows(::KNN, I, Xmatrix, y, ::Nothing) =
    (view(Xmatrix, :, I), view(y, I), nothing)

function MLJBase.fit(m::KNN, verbosity::Int, Xmatrix, y, w=nothing)
    if m.algorithm == :kdtree
        tree = NN.KDTree(Xmatrix; leafsize=m.leafsize, reorder=m.reorder)
    elseif m.algorithm == :balltree
        tree = NN.BallTree(Xmatrix; leafsize=m.leafsize, reorder=m.reorder)
    elseif m.algorithm == :brutetree
        tree = NN.BruteTree(Xmatrix; leafsize=m.leafsize, reorder=m.reorder)
    end
    report = NamedTuple{}()
    return (tree, y, w), nothing, report
end

MLJBase.fitted_params(model::KNN, (tree, _)) = (tree=tree,)

function MLJBase.predict(m::KNNClassifier, (tree, y, w), Xmatrix)
    # for each entry, get the K closest training point + their distance
    idxs, dists = NN.knn(tree, Xmatrix, m.K)

    preds       = Vector{MLJBase.UnivariateFinite}(undef, length(idxs))
    classes     = MLJBase.classes(y[1])
    probas      = zeros(length(classes))

    w_ = ones(m.K)

    # go over each test record, and for each go over the k nearest entries
    for i in eachindex(idxs)
        idxs_  = idxs[i]
        dists_ = dists[i]
        labels = y[idxs_]
        if w !== nothing
            w_ = w[idxs_]
        end
        probas .*= 0.0
        if m.weights == :uniform
            for (k, label) in enumerate(labels)
                probas[classes .== label] .+= 1.0 / m.K * w_[k]
            end
        else
            for (k, label) in enumerate(labels)
                probas[classes .== label] .+= 1.0 / dists_[k] * w_[k]
            end
        end
        # normalize so that sum to 1
        probas ./= sum(probas)
        preds[i] = MLJBase.UnivariateFinite(classes, probas)
    end
    return preds
end

function MLJBase.predict(m::KNNRegressor, (tree, y, w), Xmatrix)
    idxs, dists = NN.knn(tree, Xmatrix, m.K)
    preds       = zeros(length(idxs))

    w_ = ones(m.K)

    for i in eachindex(idxs)
        idxs_  = idxs[i]
        dists_ = dists[i]
        values = y[idxs_]
        if w !== nothing
            w_ = w[idxs_]
        end
        if m.weights == :uniform
            preds[i] = sum(values .* w_) / sum(w_)
        else
            preds[i] = sum(values .* w_ .* (1.0 .- dists_ ./ sum(dists_))) / (sum(w_) - 1)
        end
    end
    return preds
end

# ====

metadata_pkg.((KNNRegressor, KNNClassifier),
    name="NearestNeighbors",
    uuid="b8a86587-4115-5ab1-83bc-aa920d37bbce",
    url="https://github.com/KristofferC/NearestNeighbors.jl",
    julia=true,
    license="MIT",
    is_wrapper=false
    )

metadata_model(KNNRegressor,
    input=MLJBase.Table(MLJBase.Continuous),
    target=AbstractVector{MLJBase.Continuous},
    weights=true,
    descr=KNNRegressorDescription
    )

metadata_model(KNNClassifier,
    input=MLJBase.Table(MLJBase.Continuous),
    target=AbstractVector{<:MLJBase.Finite},
    weights=true,
    descr=KNNClassifierDescription
    )


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

mutable struct PeerExpressRouteCircuitConnectionPropertiesFormat <: SwaggerModel
    expressRouteCircuitPeering::Any # spec type: Union{ Nothing, SubResource } # spec name: expressRouteCircuitPeering
    peerExpressRouteCircuitPeering::Any # spec type: Union{ Nothing, SubResource } # spec name: peerExpressRouteCircuitPeering
    addressPrefix::Any # spec type: Union{ Nothing, String } # spec name: addressPrefix
    circuitConnectionStatus::Any # spec type: Union{ Nothing, CircuitConnectionStatus } # spec name: circuitConnectionStatus
    connectionName::Any # spec type: Union{ Nothing, String } # spec name: connectionName
    authResourceGuid::Any # spec type: Union{ Nothing, String } # spec name: authResourceGuid
    provisioningState::Any # spec type: Union{ Nothing, ProvisioningState } # spec name: provisioningState

    function PeerExpressRouteCircuitConnectionPropertiesFormat(;expressRouteCircuitPeering=nothing, peerExpressRouteCircuitPeering=nothing, addressPrefix=nothing, circuitConnectionStatus=nothing, connectionName=nothing, authResourceGuid=nothing, provisioningState=nothing)
        o = new()
        validate_property(PeerExpressRouteCircuitConnectionPropertiesFormat, Symbol("expressRouteCircuitPeering"), expressRouteCircuitPeering)
        setfield!(o, Symbol("expressRouteCircuitPeering"), expressRouteCircuitPeering)
        validate_property(PeerExpressRouteCircuitConnectionPropertiesFormat, Symbol("peerExpressRouteCircuitPeering"), peerExpressRouteCircuitPeering)
        setfield!(o, Symbol("peerExpressRouteCircuitPeering"), peerExpressRouteCircuitPeering)
        validate_property(PeerExpressRouteCircuitConnectionPropertiesFormat, Symbol("addressPrefix"), addressPrefix)
        setfield!(o, Symbol("addressPrefix"), addressPrefix)
        validate_property(PeerExpressRouteCircuitConnectionPropertiesFormat, Symbol("circuitConnectionStatus"), circuitConnectionStatus)
        setfield!(o, Symbol("circuitConnectionStatus"), circuitConnectionStatus)
        validate_property(PeerExpressRouteCircuitConnectionPropertiesFormat, Symbol("connectionName"), connectionName)
        setfield!(o, Symbol("connectionName"), connectionName)
        validate_property(PeerExpressRouteCircuitConnectionPropertiesFormat, Symbol("authResourceGuid"), authResourceGuid)
        setfield!(o, Symbol("authResourceGuid"), authResourceGuid)
        validate_property(PeerExpressRouteCircuitConnectionPropertiesFormat, Symbol("provisioningState"), provisioningState)
        setfield!(o, Symbol("provisioningState"), provisioningState)
        o
    end
end # type PeerExpressRouteCircuitConnectionPropertiesFormat

const _property_map_PeerExpressRouteCircuitConnectionPropertiesFormat = Dict{Symbol,Symbol}(Symbol("expressRouteCircuitPeering")=>Symbol("expressRouteCircuitPeering"), Symbol("peerExpressRouteCircuitPeering")=>Symbol("peerExpressRouteCircuitPeering"), Symbol("addressPrefix")=>Symbol("addressPrefix"), Symbol("circuitConnectionStatus")=>Symbol("circuitConnectionStatus"), Symbol("connectionName")=>Symbol("connectionName"), Symbol("authResourceGuid")=>Symbol("authResourceGuid"), Symbol("provisioningState")=>Symbol("provisioningState"))
const _property_types_PeerExpressRouteCircuitConnectionPropertiesFormat = Dict{Symbol,String}(Symbol("expressRouteCircuitPeering")=>"SubResource", Symbol("peerExpressRouteCircuitPeering")=>"SubResource", Symbol("addressPrefix")=>"String", Symbol("circuitConnectionStatus")=>"CircuitConnectionStatus", Symbol("connectionName")=>"String", Symbol("authResourceGuid")=>"String", Symbol("provisioningState")=>"ProvisioningState")
Base.propertynames(::Type{ PeerExpressRouteCircuitConnectionPropertiesFormat }) = collect(keys(_property_map_PeerExpressRouteCircuitConnectionPropertiesFormat))
Swagger.property_type(::Type{ PeerExpressRouteCircuitConnectionPropertiesFormat }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_PeerExpressRouteCircuitConnectionPropertiesFormat[name]))}
Swagger.field_name(::Type{ PeerExpressRouteCircuitConnectionPropertiesFormat }, property_name::Symbol) =  _property_map_PeerExpressRouteCircuitConnectionPropertiesFormat[property_name]

function check_required(o::PeerExpressRouteCircuitConnectionPropertiesFormat)
    true
end

function validate_property(::Type{ PeerExpressRouteCircuitConnectionPropertiesFormat }, name::Symbol, val)
end


text:
using Chess, Chess.PGN, Chess.UCI
using Pkg
using ArgParse
using Chain

const VALUE_MATE = 32000
const WIN_SCORE = 3.0

"Command line options"
function parse_commandline()
    s = ArgParseSettings()
    s.prog = "savvy"
    s.description = "Analyze positions in the game and output annotated game."
    s.add_version = true
    s.version = "0.37.0"

    @add_arg_table s begin
        "--engine"
            help = "The filename or path/filename of the engine that will be used to analyze the game."
            arg_type = String
            required = true
        "--inpgn"
            help = "This is the input pgn filename where games will be analyzed."
            arg_type = String
            required = true
        "--outpgn"
            help = "The output pgn filename where position analysis will be saved."
            arg_type = String
            default = "out.pgn"
        "--movetime"
            help = "Time in milliseconds to analyze each position in the game, note 1s=1000ms."
            arg_type = Int
        "--depth"
            help = "The maximum depth that the engine is allowed to analyze."
            arg_type = Int
        "--evalstartmove"
            help = "The game move number where the engine starts its analysis."
            arg_type = Int
            default = 1
        "--evalendmove"
            help = "The game move number where the engine ends its analysis."
            arg_type = Int
            default = 1000
        "--engineoptions"
            help = "This is the engine options, example: --engineoptions \"Hash=128, Threads=1, Analysis Contempt=Off\""
            arg_type = String
            default = ""
        "--variationlength"
            help = "The length of engine PV (principal variation) or number of moves in the PV to be saved in the annotated game."
            arg_type = Int
            default = 5
        "--includeexistingcomment"
            help = "A flag to include existing comment in the pgn output."
            action = :store_true
        "--playername"
            help = "An option to analyze the game of a specified player name. Example: --playername \"Carlsen, Magnus\""
            arg_type = String
        "--analyzewin"
            help = "A flag to enable analyzing games that have 1-0 or 0-1 results or when a specified player name has won the game."
            action = :store_true
        "--analyzeloss"
            help = "A flag to enable analyzing games that have 1-0 or 0-1 results or when a specified player name has lost the game."
            action = :store_true
        "--analyzedraw"
            help = "A flag to enable analyzing games which have 1/2-1/2 results only."
            action = :store_true
        "--analyzewhite"
            help = "A flag to only analyze the game of a player name when this player is playing white. This is useful when --playername option is used."
            action = :store_true
        "--analyzeblack"
            help = "A flag to only analyze the game of a player name when this player is playing black. This is useful when --playername option is used."
            action = :store_true
    end

    return parse_args(s), s.version
end

"Get package version"
function get_pkg_version(name::AbstractString)
    @chain Pkg.dependencies() begin
        values
        [x for x in _ if x.name == name]
        only
        _.version
    end
end

"Create mate comment and is applied when game and engine moves are the same."
function matescorecomment(escore::Score, includeexistingcomment::Bool, existingcomment::Union{Nothing, String})::String
    matecomment = ""
    movetomate = abs(escore.value)

    # If side to move wins, subtract movetomate by 1. This comment is read after the game move is pushed.
    if escore.value > 0
        movetomate -= 1
        if movetomate == 0
            if includeexistingcomment && !isnothing(existingcomment)
                matecomment = "$existingcomment checkmate"
            else
                matecomment = "checkmate"
            end
        else
            if includeexistingcomment && !isnothing(existingcomment)
                matecomment = "$existingcomment mated in $(movetomate)"
            else
                matecomment = "mated in $(movetomate)"
            end
        end
    else
        if includeexistingcomment && !isnothing(existingcomment)
            matecomment = "$existingcomment mate in $movetomate"
        else
            matecomment = "mate in $movetomate"
        end
    end

    return matecomment
end

"Build comment from score and depth info"
function scoreanddepthcomment(
    score::Float64,
    depth::Int64,
    existingcomment::Union{Nothing, String}=nothing,
    includeexistingcomment::Bool=false
)::String
    if includeexistingcomment && !isnothing(existingcomment)
        return "$existingcomment $score/$depth"
    end

    return "$score/$depth"
end

"Convert value from centipawn to pawn unit"
function centipawntopawn(value::Int64, ismate::Bool)::Float64
    if ismate
        return round(matenumtocp(value)/100, digits=2)
    end

    return round(value/100, digits=2)
end

"Convert user options in string to dictionary"
function optionstringtodict(engineoptions::String)::Dict{String, String}
    optdict = Dict{String, String}()

    if engineoptions == ""
        return optdict
    end

    opt = split(engineoptions, ",")
    opt_clean = strip.(opt, [' '])
    for n in opt_clean
        if occursin("=", n)
            k = split(n, "=")[1]
            v = split(n, "=")[2]
            optdict[k] = v
        else
            # UCI option without value like button
            optdict[n] = ""
        end
    end

    return optdict
end

"Converts mate value to score cp"
function matenumtocp(matenum::Int)::Int
    score = 0

    if matenum > 0
        score = VALUE_MATE - 2*matenum + 1
    elseif matenum < 0
        score = -VALUE_MATE - 2*matenum
    end

    return score
end

"Evaluate the board position with an engine and returns bestmove, bestscore pv and depth."
function evaluate(engine::Engine, game::Game, movetime::Union{Nothing, Int64}, searchdepth::Union{Nothing, Int})
    bm = nothing
    score = nothing
    pv = nothing
    depth = 0

    if ischeckmate(board(game))
        return bm, Score(-32000, false, Chess.UCI.exact), pv, depth
    end

    if isstalemate(board(game))
        return bm, Score(0, false, Chess.UCI.exact), pv, depth
    end

    setboard(engine, game)

    if isnothing(searchdepth)
        sendcommand(engine, "go movetime $movetime")
    elseif isnothing(movetime)
        sendcommand(engine, "go depth $searchdepth")
    else
        sendcommand(engine, "go movetime $movetime depth $searchdepth")
    end

    movecount = 0

    while true
        line = readline(engine.io)

        # Get out of the loop once we have the bestmove.
        if startswith(line, "bestmove")
            bi = parsebestmove(line)
            bm = bi.bestmove
            break

        # Save the score and depth.
        elseif startswith(line, "info")
            info = parsesearchinfo(line)

            # Engine like Stockfish outputs a pv with single move even if the previous
            # pv has the same first move and have more than 1 move in the pv, if this is
            # the case, we will not save such pv.
            # Todo: Refactor the following code.
            currentpv = info.pv
            update_scoreanddepth = true

            # Not all pv line has a score, check it also.
            if !isnothing(currentpv) && !isnothing(info.score)
                if length(currentpv) == 1
                    movecount += 1
                    # If this is the first time with a single move pv, we save it in our pv.
                    if movecount == 1
                        pv = currentpv
                    else
                        # Replace our pv if current pv move 1 is different from our pv move 1.
                        if !isnothing(pv)
                            if currentpv[1] != pv[1]
                                pv = currentpv
                            else
                                update_scoreanddepth = false
                                # println("Do not replace old pv")
                            end
                        else
                            pv = currentpv
                        end
                    end
                else
                    pv = currentpv
                end

                if update_scoreanddepth
                    score = info.score
                    depth = info.depth
                end
            end
        end
    end

    return bm, score, pv, depth
end

"Set engine option"
function dosetoption(engine::Engine, k1::String, v, v1)
    datatype = v1.type
    if datatype == Chess.UCI.check
        value = parse(Bool, v)
        setoption(engine, k1, value)
        println("check: setoption name $k1 value $value")
    elseif datatype == Chess.UCI.spin
        value = parse(Int, v)
        setoption(engine, k1, value)
        println("spin: setoption name $k1 value $value")
    elseif datatype == Chess.UCI.combo
        setoption(engine, k1, "$v")
        println("combo: setoption name $k1 value $v")
    elseif datatype == Chess.UCI.button
        setoption(engine, k1, nothing)
        println("button: setoption name $k1")
    elseif datatype == Chess.UCI.string
        setoption(engine, k1, "$v")
        println("string: setoption name $k1 value $v")
    else
        throw("Option type is not defined.")
    end

    return nothing
end

"Set user options to the engine"
function setengineoption(engine::Engine, engineoptions::Dict)
    if isempty(engineoptions)
        return nothing
    end

    for (k, v) in engineoptions
        for (k1, v1) in engine.options
            if k != k1
                continue
            end

            dosetoption(engine, k1, v, v1)
            break
        end
    end

    return nothing
end

"""
Add move NAG depending on the game move score and engine best move score.

Ref.: NAG - https://en.wikipedia.org/wiki/Numeric_Annotation_Glyphs
"""
function addmovenag(mygame::Game, em_score::Float64, gm_score::Float64, gscore::Score)::Nothing
    # Add ?? if game move score turns from playable to losing.
    # If game move score is 3 or more pawns behind but engine score is only 1 pawn behind.
    if gm_score <= -WIN_SCORE && em_score >= -1.0
        addnag!(mygame, 4)

    # Add ?? if game move is just equal or less from a winning score.
    elseif em_score >= WIN_SCORE && gm_score <= 0.5
        addnag!(mygame, 4)

    # Add ?? if game move score is to be mated and engine move is only a queen behind or better.
    elseif gscore.ismate && gm_score < 0 && em_score >= -10.0
        addnag!(mygame, 4)

    # Add ?, ... from playable to bad score.
    elseif gm_score < -1.0 && em_score >= -1.0
        addnag!(mygame, 2)

    # Add !, if game move is better by at least 5 cp than engine move.
    elseif gm_score - 0.05 >= em_score && abs(em_score) <= WIN_SCORE
        addnag!(mygame, 1)
    end

    return nothing
end

"""
Get the threat of the game move.

Push the game move, do null move and evaluate the resulting position. The returned engine
bestmove will be the threat of the game move.
"""
function get_threatmove(
    e::Engine,
    g::Game,
    escore::Int,
    mt::Union{Nothing, Int64},
    searchdepth::Union{Nothing, Int}
    )::Union{Nothing, String}
    bmsan = nothing

    # Calculate the threat move if current engine score is not winning yet.
    if escore >= WIN_SCORE
        return bmsan
    end

    # If game move is a capture do not calculate the threat move.
    # Usually it is obvious that the same piece will move to escape the recapture.
    # Also if game move is a check move, we will not calculate the threat move as
    # the king will be captured if we do a null move.
    nextmovesan = movetosan(board(g), nextmove(g))
    if occursin("x", nextmovesan) || occursin("+", nextmovesan)
        return bmsan
    end

    forward!(g)
    bd = board(g)

    # Check game termination.
    if isterminal(board(g))
        back!(g)
        return bmsan
    end

    nbd = donullmove(bd)
    ug = Game(nbd)

    # Evaluate the position after the null move.
    bm, score, _, _ = evaluate(e, ug, mt, searchdepth)
    if score.ismate
        mscore = matenumtocp(score.value)
    else
        mscore = score.value
    end

    # If the best move found is worth at least a pawn ahead consider it as a threat.
    if mscore > escore && mscore >= 1.0
        # At this moment we do not consider a capture threat move as a threat as it is obvious.
        bmsan = movetosan(nbd, bm)
        if occursin("x", bmsan)
            bmsan = nothing
        end
    end

    back!(g)

    return bmsan
end

"Read pgn file and analyze the positions in the game. Save the analysis in output file."
function analyze(in_pgnfn::String, out_pgnfn::String, engine_filename::String;
                movetime::Union{Nothing, Int64}=nothing,
                searchdepth::Union{Nothing, Int}=nothing,
                evalstartmove::Int64=1,
                evalendmove::Int64=1000,
                engineoptions::Dict=Dict(),
                variationlength::Int64=5,
                includeexistingcomment::Bool=false,
                playername::Union{Nothing, String}=nothing,
                analyzewin::Bool=false,
                progversion::String="",
                analyzedraw::Bool=false,
                analyzewhite::Bool=false,
                analyzeblack::Bool=false,
                analyzeloss::Bool=false)
    tstart = time_ns()

    # Init engine.
    engine = runengine(engine_filename)

    # Set engine options.
    setengineoption(engine, engineoptions)

    game_num = 0

    for g in gamesinfile(in_pgnfn; annotations=true)
        game_num += 1
        println("analyzing game $game_num between $(headervalue(g, "White")) and $(headervalue(g, "Black")) ...")
        gameresult = headervalue(g, "Result")

        # Analyze the game only if one of the players is the one specified in playername option.
        if !isnothing(playername)
            if playername != whiteplayer(g) && playername != blackplayer(g)
                println("Don't analyze game $game_num, $playername is not playing in this game.")
                continue
            end

            # Don't analyze this game if player is playing the white side and analyzeblack is true.
            if analyzeblack && !analyzewhite && playername == whiteplayer(g)
                println("Don't analyze game $game_num, $playername is not playing the black side.")
                continue
            # Don't analyze this game if player is playing the black side and analyzewhite is true.
            elseif analyzewhite && !analyzeblack && playername == blackplayer(g)
                println("Don't analyze game $game_num, $playername is not playing the white side.")
                continue
            end

            # Don't analyze this game if analyzeloss flag is true and player does not lose.
            if analyzeloss && !analyzewin && !analyzedraw
                if playername == whiteplayer(g) && gameresult != "0-1"
                    println("Don't analyze game $game_num, $playername playing white did not lose this game.")
                    continue
                elseif playername == blackplayer(g) && gameresult != "1-0"
                    println("Don't analyze game $game_num, $playername playing black did not lose this game.")
                    continue
                end
            end

            # Don't analyze this game if analyzewin flag is true and player does not win.
            if analyzewin && !analyzedraw && !analyzeloss
                if playername == whiteplayer(g) && gameresult != "1-0"
                    println("Don't analyze game $game_num, $playername playing white did not win this game.")
                    continue
                elseif playername == blackplayer(g) && gameresult != "0-1"
                    println("Don't analyze game $game_num, $playername playing black did not win this game.")
                    continue
                end
            end
        end

        # Analyze only those games with 1-0 or 0-1 results.
        if (analyzewin || analyzeloss) && !analyzedraw
            if gameresult != "1-0" && gameresult != "0-1"
                println("Don't analyze game $game_num, result $gameresult is not decisive.")
                continue
            end
        # Analyze only those games with 1/2-1/2 results.
        elseif analyzedraw && !(analyzewin || analyzeloss)
            if gameresult != "1/2-1/2"
                println("Don't analyze game $game_num, result $gameresult is not a draw.")
                continue
            end
        end

        # Save annotated game to a new game and copy its header.
        mygame = Game()
        mygame.headers = g.headers

        # Parse the game, visit each position.
        while !isatend(g)
            move = nextmove(g)
            bd = board(g)
            gm_movesan = movetosan(bd, move)  # gm=game move
            myply = ply(mygame)
            mymovenum = Int(ceil(myply/2))

            existingcomment = nothing
            if includeexistingcomment
                forward!(g)
                existingcomment = comment(g.node)
                back!(g)
            end

            # If current move number is below evalstartmove don't analyze this position.
            if mymovenum < evalstartmove
                domove!(mygame, move)  # save move to mygame
                if includeexistingcomment && !isnothing(existingcomment)
                    addcomment!(mygame, existingcomment)
                end
                forward!(g)  # Push the move on the main board.
                continue
            end

            # If current move number is above evalendmove don't analyze this position.
            if mymovenum > evalendmove
                domove!(mygame, move)  # save move to mygame
                if includeexistingcomment && !isnothing(existingcomment)
                    addcomment!(mygame, existingcomment)
                end
                forward!(g)
                continue
            end

            domove!(mygame, move)  # save move to mygame

            # Evaluate this position with the engine.
            bm, escore, pv, edepth = evaluate(engine, g, movetime, searchdepth)
            em_movesan = movetosan(bd, bm)  # em=engine move

            # Prepare engine variation. If score is mate show all moves in the pv.
            em_pv = pv[1 : min(escore.ismate ? length(pv) : variationlength, length(pv))]

            em_score = centipawntopawn(escore.value, escore.ismate)

            em_comment = scoreanddepthcomment(em_score, edepth, existingcomment, includeexistingcomment)

            # If engine best move and game move are not the same, evaluate the game move too.
            if gm_movesan != em_movesan
                # Push the move and evaluate.
                forward!(g)
                _, gscore, _, depth = evaluate(engine, g, movetime, searchdepth)
                # Restore the position.
                back!(g)

                # Negate the score since we pushed the move before analyzing it.
                gm_score = centipawntopawn(-gscore.value, gscore.ismate)

                gm_comment = scoreanddepthcomment(gm_score, depth, existingcomment, includeexistingcomment)

                # Add comment for the evaluation of game move.
                if gscore.ismate
                    # Read the mate comment after pushing the move.
                    movetomate = abs(gscore.value)
                    if gscore.value > 0
                        if includeexistingcomment && !isnothing(existingcomment)
                            matecomment = "$existingcomment mate in $movetomate"
                        else
                            matecomment = "mate in $movetomate"
                        end
                    else
                        if includeexistingcomment && !isnothing(existingcomment)
                            matecomment = "$existingcomment mated in $movetomate"
                        else
                            matecomment = "mated in $movetomate"
                        end
                    end
                    addcomment!(mygame, matecomment)
                else
                    addcomment!(mygame, gm_comment)
                end

                # Add NAG - Numeric_Annotation_Glyphs
                addmovenag(mygame, em_score, gm_score, gscore)

                # Insert engine move as variation to mygame.

                # Back off 1 ply and add engine pv as variation.
                back!(mygame)

                for (i, m) in enumerate(em_pv)
                    addmove!(mygame, m)

                    # Add a precomment in the variation.
                    if i == 1
                        if escore.ismate
                            matecomment = escore.value > 0 ? "mate in" : "mated in"
                            addprecomment!(mygame, "$matecomment $(abs(escore.value))")
                        else
                            # Add "better is" precomment if engine move score is at
                            # least 1 pawn ahead compared to the game move score.
                            if (em_score - 1.0 >= gm_score
                                && em_score >= -WIN_SCORE
                                && gm_score < WIN_SCORE)
                                addprecomment!(mygame, "better is")
                            end
                        end
                    end
                end

                # Add comment at the end of the variation.
                adddata!(mygame.node, "comment", scoreanddepthcomment(em_score, edepth, nothing, false))

                # Restore to a node after inserting the variation.
                for _ in em_pv
                    back!(mygame)
                end

                forward!(mygame)

            # Engine and game move are the same.
            else
                # Add comment for the evaluation of game move. Use the engine analysis.
                if escore.ismate
                    matecomment = matescorecomment(escore, includeexistingcomment, existingcomment)
                    addcomment!(mygame, matecomment)
                else
                    # Add threat move in the comment.
                    threatmove = get_threatmove(engine, g, escore.value, movetime, searchdepth)
                    if !isnothing(threatmove)
                        addcomment!(mygame, "$em_comment ... threatening $threatmove")
                    else
                        addcomment!(mygame, em_comment)
                    end
                end
            end

            forward!(g)  # Push the move on the main board.

        end

        # Update Annotator tag.
        if isnothing(searchdepth)
            anno = "@$movetime ms per pos"
        elseif isnothing(movetime)
            anno = "@depth $searchdepth per pos"
        else
            anno = "@$movetime ms and depth $searchdepth per pos"
        end
        setheadervalue!(mygame, "Annotator", "savvy $progversion, $(engine.name) $anno")

        open(out_pgnfn, "a+") do filehandle
            println(filehandle, gametopgn(mygame))
        end

    end

    quit(engine)

    elapsed = (time_ns() - tstart) / 1000000000
    print("elapsed (sec): $elapsed")

    return nothing
end

"Initialize some variables before starting to analyze the games in the input pgn file."
function main()
    println("Julia $VERSION")
    println("Chess $(get_pkg_version("Chess"))")

    parsed_args, progversion = parse_commandline()
    println("savvy $progversion")
    println("Parsed args:")
    for (arg, val) in parsed_args
        println("    $arg : $val")
    end

    movetime = parsed_args["movetime"]
    searchdepth = parsed_args["depth"]

    # If movetime and depth are not specified, set movetime to 1000 ms.
    if isnothing(movetime) && isnothing(searchdepth)
        movetime = 1000
        println("Warning, movetime and depth are not specified, set movetime to $movetime ms.")
    end

    # Convert engine options string to dictionary.
    optdict = optionstringtodict(parsed_args["engineoptions"])

    analyze(
        parsed_args["inpgn"],
        parsed_args["outpgn"],
        parsed_args["engine"],
        movetime=movetime,
        searchdepth=searchdepth,
        evalstartmove=parsed_args["evalstartmove"],
        evalendmove=parsed_args["evalendmove"],
        engineoptions=optdict,
        variationlength=parsed_args["variationlength"],
        includeexistingcomment=parsed_args["includeexistingcomment"],
        playername=parsed_args["playername"],
        analyzewin=parsed_args["analyzewin"],
        progversion=progversion,
        analyzedraw=parsed_args["analyzedraw"],
        analyzewhite=parsed_args["analyzewhite"],
        analyzeblack=parsed_args["analyzeblack"],
        analyzeloss=parsed_args["analyzeloss"]
    )

    return nothing
end

if abspath(PROGRAM_FILE) == @__FILE__
    main()
end

max_stars_repo_name: fsmosca/savvy

max_issues_repo_name: fsmosca/savvy

max_forks_repo_name: fsmosca/savvy


text:
@testset "Testing Memory" begin
    @test OneDNN.dnnl_type(Float16) == OneDNN.Lib.dnnl_f16
    @test OneDNN.dnnl_type(Float32) == OneDNN.Lib.dnnl_f32
    @test OneDNN.dnnl_type(Int32) == OneDNN.Lib.dnnl_s32
    @test OneDNN.dnnl_type(Int8) == OneDNN.Lib.dnnl_s8
    @test OneDNN.dnnl_type(UInt8) == OneDNN.Lib.dnnl_u8

    @test OneDNN.dnnl_convert(Float16) == OneDNN.Lib.dnnl_f16
    @test OneDNN.dnnl_convert(Float32) == OneDNN.Lib.dnnl_f32
    @test OneDNN.dnnl_convert(Int32) == OneDNN.Lib.dnnl_s32
    @test OneDNN.dnnl_convert(Int8) == OneDNN.Lib.dnnl_s8
    @test OneDNN.dnnl_convert(UInt8) == OneDNN.Lib.dnnl_u8

    # Test conversion from an instance of a type
    @test OneDNN.dnnl_type(one(Float32)) == OneDNN.Lib.dnnl_f32

    # Dims
    # "12 dimensions is enough for anyone"
    dnnl_dims = OneDNN.dnnl_dims
    x = (1, 2, 3)
    @test dnnl_dims(x) == (1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0)
    @test dnnl_dims() == (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
    @test dnnl_dims() == dnnl_dims(())

    x = rand(Float32, 1, 2, 3)
    _strides = strides(x)
    @test dnnl_dims(x) == (_strides..., 0, 0, 0, 0, 0, 0, 0, 0, 0)
end

@testset "Testing Views" begin
    x = rand(Float32, 40, 40)
    y = rand(Float32, 40, 40)

    vx = view(x, 1, :)
    VX = OneDNN.Memory(vx)
    @test OneDNN.materialize(VX) == vx

    vy = view(x, :, 1)
    VY = OneDNN.Memory(vy)
    @test OneDNN.materialize(VY) == vy

    Z = VX + VY
    @test OneDNN.materialize(Z) == vx .+ vy

    # 2D views
    vx = view(x, 1:5, 2:11)
    VX = OneDNN.Memory(vx)
    @test OneDNN.materialize(VX) == vx

    vy = view(y, 20:30, :)
    VY = OneDNN.Memory(vy)
    @test OneDNN.materialize(VY) == vy
end

# For CachedArrays style functionality, it's important that the "pointer" used for the
# "DNNL.memory" object can be updated if the underlying storage gets changed.
#
# TODO: Fix with custom array type
#
#@testset "Testing Swapping Memory" begin
#    x = ones(Float32, 10, 10)
#    y = 2 .* ones(Float32, 10, 10)
#    X = OneDNN.Memory(x)
#    Y = OneDNN.Memory(y)
#
#    Z = X + Y
#    @test all(isequal(3), OneDNN.materialize(Z))
#
#    # Swap out the array and ensure the change is propagated.
#    x2 = zeros(Float32, 10, 10)
#    X.array = x2
#    Z = X + Y
#    @test all(isequal(2), OneDNN.materialize(Z))
#
#    # Try with views
#    x = rand(Float32, 20, 20)
#    va = view(x, 11:20, 11:20)
#    vb = view(x, 1:10, 11:20)
#    VA = OneDNN.Memory(va)
#    VB = OneDNN.Memory(vb)
#
#    Z = VA + VB
#    @test isapprox(va .+ vb, OneDNN.materialize(Z))
#    y = rand(Float32, 20, 20)
#    VA.array = y
#    Z = VA + VB
#    @test isapprox(view(y, 11:20, 11:20) .+ vb, OneDNN.materialize(Z))
#end

# @testset "Testing Memory" begin
#     #####
#     ##### Memory Descriptors
#     #####
#
#     @test OneDNN.dnnl_type(Float16) == OneDNN.Lib.dnnl_f16
#     @test OneDNN.dnnl_type(Float32) == OneDNN.Lib.dnnl_f32
#     @test OneDNN.dnnl_type(Int32) == OneDNN.Lib.dnnl_s32
#     @test OneDNN.dnnl_type(Int8) == OneDNN.Lib.dnnl_s8
#     @test OneDNN.dnnl_type(UInt8) == OneDNN.Lib.dnnl_u8
#
#     # Test conversion from an instance of a type
#     @test OneDNN.dnnl_type(one(Float32)) == OneDNN.Lib.dnnl_f32
#
#     # Construct Dimensions
#     # We pass dimensions as 12 long Int64 arrays to OneDNN
#     sz = (1,2,3)
#     dims = OneDNN.dnnl_dims(sz)
#     @test length(dims) == OneDNN.Lib.DNNL_MAX_NDIMS
#     @test dims[1:length(sz)] == collect(reverse(sz))
#     @test all(iszero, dims[length(sz)+1:OneDNN.Lib.DNNL_MAX_NDIMS])
#     @test all(iszero, OneDNN.dnnl_dims())
#     @test all(iszero, OneDNN.dnnl_dims(()))
#
#     x = rand(Float32, 2, 3)
#     md = OneDNN.memorydesc(x)
#
#     # OneDNN stores size information in reverse order from normal Julia
#     @test OneDNN.logicalsize(md) == reverse(size(x))
#     @test md.data_type == OneDNN.Lib.dnnl_f32
#
#     # Test equality
#     @test md == OneDNN.memorydesc(x)
#     @test md != OneDNN.memorydesc(rand(Float32, 3, 2))
#
#     #####
#     ##### Unwrapping Wrapper Types
#     #####
#
#     x = randn(Float32, 10, 20)
#     @test OneDNN.toparent(x) === x
#     @test OneDNN.toparent(transpose(x)) === x
#     @test OneDNN.toparent(reshape(transpose(x), :)) === x
#
#     # Views must be materialized in order to pass to OneDNN
#     #@test OneDNN.toparent(view(x, 1:2, 1:2)) == x
#
#     # batched transpose
#     x = randn(Float32, 5, 10, 15)
#     p = PermutedDimsArray(x, (2,1,3))
#     @test OneDNN.toparent(p) === x
#     @test OneDNN.toparent(reshape(p, :)) === x
#
#     # Views must be materialized in order to pass to OneDNN
#     #@test OneDNN.toparent(view(x, 1:2, 1:2, :)) === x
#
#     #####
#     ##### Extruding Dimensions
#     #####
#
#     @test OneDNN.leading(1) == ()
#     @test OneDNN.leading(1, 2) == (1,)
#     @test OneDNN.leading(1, 2, 3) == (1, 2)
#     @test OneDNN.leading(1, 2, 3, 4) == (1, 2, 3)
#     @test OneDNN.leading(1, 2, 3, 4,5) == (1, 2, 3, 4)
#
#     @test OneDNN.extrude(Float32, (10,), 4 * 12) == (12,)
#     @test OneDNN.extrude(Float32, (12,), 4 * 12) == (12,)
#     @test OneDNN.extrude(Float32, (12,), 4 * 13) == (13,)
#
#     @test OneDNN.extrude(Float32, (10, 10), 4 * 100) == (10, 10)
#     @test OneDNN.extrude(Float32, (10, 10), 4 * 101) == (10, 11)
#     @test OneDNN.extrude(Float32, (10, 10), 4 * 104) == (10, 11)
#
#     @test OneDNN.extrude(Float32, (2, 2, 2), 4 * 9) == (2, 2, 3)
#
#     #####
#     ##### Memory
#     #####
#
#     x = rand(Float32, 5, 10)
#     X = OneDNN.memory(x)
#
#     # The `memory_t` types in OneDNN return references to a cached MD.
#     # Make sure they still end up as equal to when we create one from scratch.
#     @test OneDNN.memorydesc(X) == OneDNN.memorydesc(x)
#     @test OneDNN.getdata(X) === x
#
#     @test size(X) == size(x)
#     @test eltype(X) == eltype(x)
#
#     @test OneDNN.val_ndims(X) == Val{2}()
#
#     Y = similar(X)
#     @test size(Y) == size(X)
#     @test OneDNN.memorydesc(X) == OneDNN.memorydesc(Y)
#
#     # Do view work correctly?
#     x = rand(Float32, 5, 10)
#     vx = view(x, 1:2, :)
#     VX = OneDNN.memory(vx)
#     @test size(VX) == size(vx)
#     @test OneDNN.materialize(VX) == vx
#
#     #####
#     ##### Transposes
#     #####
#
#     x = rand(Float32, 10, 5)
#     X = OneDNN.memory(transpose(x))
#     @test size(X) == (5, 10)
#     XX = OneDNN.materialize(X)
#     @test XX == transpose(x)
#
#     # batched transpose
#     x = PermutedDimsArray(rand(Float32, 10, 20, 30), (2,1,3))
#     X = OneDNN.memory(x)
#     @test size(X) == (20, 10, 30)
#     XX = OneDNN.materialize(X)
#
#     # the resulting array should be element wise equal, but since we told it the layout
#     # was different, it should be an entirely new array.
#     @test XX == x
#     @test XX !== x
# end
#
# @testset "Testing Views" begin
#     #x = rand(Float32, 10, 10)
#     #X = OneDNN.memory(x)
#
#     #@test OneDNN.materialize(view(X, :, 1)) == view(x, :, 1)
#     # Julia returns a vector ... OneDNN returns an array
#     #@test OneDNN.materialize(view(X, 1, :)) == view(x, 1, :)
# end
#


text:
using .TransformVariables: AbstractTransform, transform, inverse

setting(xf::AbstractTransform) = _setting(xf)
getting(xf::AbstractTransform) = _getting(xf)

XFBijection{INV}(xf::T) where {INV, T} = XFBijection{INV, T}(xf)
XFBijection(xf) = XFBijection{false}(xf)

Base.inv(bijection::XFBijection{INV}) where INV = XFBijection{!INV}(bijection.xf)

Bijection(xf::AbstractTransform) = XFBijection(xf)
tofield(b::XFBijection{false}, x) = transform(b.xf, x)
fromfield(b::XFBijection{false}, y) = inverse(b.xf, y)
tofield(b::XFBijection{true}, x) = inverse(b.xf, x)
fromfield(b::XFBijection{true}, y) = transform(b.xf, y)

Base.show(io::IO, lens::BijectionLens{<:XFBijection}) =
    print_apply(io, typeof(lens), _getfields(lens.bijection))


text:
using Documenter, DiscO

makedocs(;
    modules=[DiscO],
    format=Documenter.HTML(),
    pages=[
        "Home" => "index.md",
    ],
    repo="https://github.com/ahrajabi/DiscO.jl/blob/{commit}{path}#L{line}",
    sitename="DiscO.jl",
    authors="Amirhossein Rajabi",
    assets=String[],
)

deploydocs(;
    repo="github.com/ahrajabi/DiscO.jl",
)

max_stars_repo_name: ahrajabi/DiscO

max_issues_repo_name: ahrajabi/DiscO

max_forks_repo_name: ahrajabi/DiscO


text:
using Distributed
using SharedArrays

@everywhere using LatentOrderBookModel

###########
# boltz_const      [0, 20]
# sigma            [0, 0.1]
# D                [0, 20]
# eta              [0, 0.1]
# lambda           [0, 20]
# mu               [0, 1]
# alpha            [0, 2.0]
###########

# param1_name::String
# param1_values::Array{Any,1}
# param2_name::String
# param2_values::Array{Any,1}
# surface_points::Int64
# replications::Int64
# params::Dict{String, Any}
# log_prices::Array{Float64,1}

cpus_allocated = size(Sys.cpu_info(),1)
println("$cpus_allocated cores allocated for job")

replications = 10
surface_points = 32
println("Number of price paths generated: $(surface_points^2*replications)")
param1_name = "α"
param1_values = repeat(repeat(collect(range(1.0, 10.0, length=surface_points)),inner=surface_points),replications)

param2_name = "ν"
param2_values = repeat(repeat(collect(range(0.0001, 0.0003, length=surface_points)),outer=surface_points),replications)
println("Creating surface plot for parameter $param1_name [1.0, 10.0] and parameter $param2_name [0.0001, 0.0003]")

fixed_params=Dict()
fixed_params["T"]=2300
fixed_params["τ"]=20
fixed_params["initial_mid_price"]=238.745
fixed_params["n_spatial_points"]=4501
fixed_params["boltz_const"]=1.0
fixed_params["sample_std"]=7.415
fixed_params["σ"]=0.001
fixed_params["D"]=5.0
fixed_params["ν"]=0.001
fixed_params["α"]=1.0
fixed_params["λ"]=1.0
fixed_params["μ"]=0.5

objective_surface = ObjectiveSurface(
    param1_name, param1_values,
    param2_name, param2_values,
    surface_points, replications,
    fixed_params)

random_seed = Int(rand(UInt32))
price_paths = @time objective_surface(random_seed, true)
println("Random seed is $random_seed")
println(size(price_paths))
io = open("alpha_vs_nu_price_paths.csv", "w")
for i in 1:size(price_paths,1)
    write(io, join(Array(price_paths[i,:]), ", "), "\n")
end
close(io)

#
# include("preprocess_bar_data.jl")
# include("mom_basic.jl")
#
# bar_data = process_bar_data("Original_Price_Bars_2300.csv")
# log_prices = bar_data.log_price
#
# num_bootstap = 20000
# b = 20
# W = weight_matrix(4547, log_prices, b, num_bootstap)
#
# function redim_pp(price_paths, T, replications, surface_points)
#     price_paths_new_dim = Array{Float32, 3}(undef, T,replications, surface_points^2)
#     for i in 1:surface_points^2
#         for k in 1:replications
#             price_paths_new_dim[:,k,i] = price_paths[:,i + (k-1)*surface_points^2]
#         end
#
#     end
#     return price_paths_new_dim
# end
#
# price_paths_new_dim = redim_pp(price_paths, fixed_params["T"], replications, surface_points)
#
# function objective_value_surface_plot(price_paths, W, log_prices)
#     T, replications, surface_points_sq = size(price_paths)
#     objective_values = zeros(Float32, surface_points_sq)
#     objective_value_fn(price_path) = objective_value(price_path, log_prices, W)
#     for i in 1:surface_points_sq
#         objective_values[i] = objective_value_fn(price_paths[:,:,i])
#     end
#     return objective_values
# end
#
# objective_values = objective_value_surface_plot(price_paths_new_dim, W,
#     log_prices)
#
# x_s = param1_values[1:surface_points^2]
# y_s = param2_values[1:surface_points^2]
# z_s = objective_values[1:end]
# z_s[.!(isnan.(z_s))]
# z_max = log(maximum(z_s[.!(isnan.(z_s))]))
# z_min = log(minimum(z_s[.!(isnan.(z_s))]))
#
#
# using Plots
# pyplot()
# using PyPlot
# pygui(true)
#
# plt = Plots.plot(x_s, y_s, z_s, st=:surface, size=[1000,800],
#     xlabel=param1_name, ylabel=param2_name,
#     zlabel="objective value", camera=(0,30), show=true)
#
# using JLD
# save("D:/Documents/UCT 2019/dissertation/scripts/masters-dissertation/data/alpha_nu_price_paths_001.jld", "price_paths",
#     price_paths, "objective_surface", objective_surface, "objective_values", objective_values, compress=true)


text:
#---------------------------------------------------------------------#
#This function computes the global Mass and Differentiation Matrices.
#Written by F.X. Giraldo on April 19 2019
#           Department of Applied Mathematics
#           Naval Postgraduate School
#           Monterey; CA 93943-5216
#---------------------------------------------------------------------#
function create_rhs(q,M,Dwe,intma,periodicity,DFloat)

    #Get lengths of arrays
    (Np,nelem)=size(intma)
    Npoin=size(q)

    #Store local arrays
    inode=zeros(Int64,Np)
    rhs=zeros(DFloat,Npoin)

    #store flux term
    f=0.5*q.^2

    #build differentiation matrix contribution
    fe=zeros(DFloat,Np)
    for e=1:nelem
        for i=1:Np
            I=intma[i,e]
            fe[i]=f[I]
        end
        for i=1:Np
            I=intma[i,e]
            for j=1:Np
                rhs[periodicity[I]]+=Dwe[i,j]*fe[j]
            end
        end
    end

    #build flux matrix contribution (numerical flux)
    for e=1:nelem
        L=intma[Np,e]
        ep1=e+1
        if (e == nelem)
            ep1=1
        end
        R=intma[1,ep1]
        umax=max(abs(q[L]),abs(q[R]))
        flux=0.5*( f[L] + f[R] - umax*(q[R] - q[L]) )
        rhs[periodicity[L]]-=flux
        rhs[periodicity[R]]+=flux
    end

    #Build RHS vector
    R=M\rhs

    #Return arrays
    return(R)
end