text:
using RBNF
using RBNF: Token

const pi_token = Token{:reserved}("pi")
const half_pi_token = (pi_token, Token{:reserved}("/"), Token{:int}("2"))

# Intrinsics
# // 3-parameter 2-pulse single qubit gate
# gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }
# // 2-parameter 1-pulse single qubit gate
# gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }
# // 1-parameter 0-pulse single qubit gate
# gate u1(lambda) q { U(0,0,lambda) q; }
# // Pauli gate: bit-flip
# gate x a { u3(pi,0,pi) a; }
# // Pauli gate: bit and phase flip
# gate y a { u3(pi,pi/2,pi/2) a; }
# // Pauli gate: phase flip
# gate z a { u1(pi) a; }
# // Clifford gate: Hadamard
# gate h a { u2(0,pi) a; }
# // Clifford gate: sqrt(Z) phase gate
# gate s a { u1(pi/2) a; }
# // Clifford gate: conjugate of sqrt(Z)
# gate sdg a { u1(-pi/2) a; }
# // C3 gate: sqrt(S) phase gate
# gate t a { u1(pi/4) a; }
# // C3 gate: conjugate of sqrt(S)
# gate tdg a { u1(-pi/4) a; }
function code_qasm(gate::IntrinsicRoutine)
    gt = typeof(gate)
    args = Any[getfield(gate, x) for x in fieldnames(gt)]
    if isempty(args)
        return code_qasm(gt)
    else
        return code_qasm(gt, args)
    end
end

qasm_gate_name(::Type{Intrinsics.XGate}) = "x"
qasm_gate_name(::Type{Intrinsics.YGate}) = "y"
qasm_gate_name(::Type{Intrinsics.ZGate}) = "z"
qasm_gate_name(::Type{Intrinsics.HGate}) = "h"
qasm_gate_name(::Type{Intrinsics.SGate}) = "s"
qasm_gate_name(::Type{Intrinsics.TGate}) = "t"
qasm_gate_name(::Type{<:Intrinsics.Rx}) = "rx"
qasm_gate_name(::Type{<:Intrinsics.Ry}) = "ry"
qasm_gate_name(::Type{<:Intrinsics.Rz}) = "rz"

code_qasm(::Type{Intrinsics.XGate}) = QASM.UGate(pi_token, Token{:int}("0"), pi_token, [])

function code_qasm(::Type{Intrinsics.YGate})
    QASM.UGate(pi_token, half_pi_token, half_pi_token, nothing)
end

function code_qasm(::Type{Intrinsics.ZGate})
    QASM.UGate(Token{:int}("0"), Token{:int}("0"), pi_token, nothing)
end

function code_qasm(::Type{Intrinsics.HGate})
    QASM.UGate(half_pi_token, Token{:int}("0"), pi_token, nothing)
end

function code_qasm(::Type{Intrinsics.SGate})
    QASM.UGate(Token{:int}("0"), Token{:int}("0"), half_pi_token, nothing)
end

function code_qasm(::Type{Intrinsics.TGate})
    QASM.UGate(
        Token{:int}("0"),
        Token{:int}("0"),
        (pi_token, Token{:reserved}("/"), Token{:int}("4")),
        nothing,
    )
end

# // Rotation around X-axis
# gate rx(theta) a { u3(theta,-pi/2,pi/2) a; }
# // rotation around Y-axis
# gate ry(theta) a { u3(theta,0,0) a; }
# // rotation around Z axis
# gate rz(phi) a { u1(phi) a; }

to_token(x::AbstractFloat) = Token{:float64}(string(Float64(x)))
to_token(x::String) = Token{:id}(x)
to_token(x::Integer) = Token{:int}(string(x))
to_token(x) = x

function code_qasm(::Type{<:Intrinsics.Rx}, vars::Vector{Any})
    QASM.UGate(to_token(vars[1]), QASM.Negative(half_pi_token), half_pi_token, nothing)
end

# TODO: check this, U(theta, 0, 0) is Rz?
function code_qasm(::Type{<:Intrinsics.Ry}, vars::Vector{Any})
    QASM.UGate(Token{:int}("0"), to_token(vars[1]), Token{:int}("0"), nothing)
end

function code_qasm(::Type{<:Intrinsics.Rz}, vars::Vector{Any})
    QASM.UGate(Token{:int}("0"), Token{:int}("0"), to_token(vars[1]), nothing)
end

abstract type TargetQASM end
# toplevel QASM
Base.@kwdef struct TargetQASMTopLevel <: TargetQASM
    inline_intrinsic::Bool = false
end

# QASM gate decl
Base.@kwdef struct TargetQASMGate <: TargetQASM
    inline_intrinsic::Bool = false
end

struct RegMap
    cbits::Dict{Any,Tuple{String,Int}}
    regs_to_locs::Dict{Int,Vector{Int}}
    locs_to_reg_addr::Dict{Int,Tuple{Int,Int}}
end

function RegMap(target, ci::CodeInfo)
    locs_to_regs = Dict{Int,Int}()
    # ssa/slot => (name, size)
    cbits = Dict{Any,Tuple{String,Int}}()

    for (v, stmt) in enumerate(ci.code)
        if stmt isa ReturnNode && isdefined(stmt, :val) && stmt.val isa SSAValue
            ret = ci.code[stmt.val.id]
            if ret isa Expr && ret.head === :new && ret.args[1] <: NamedTuple
                cnames = ret.args[1].parameters[1]
                for i in 1:length(cnames)
                    x = ret.args[i+1]::SSAValue
                    _, size = cbits[x.id]
                    cbits[x.id] = (string(cnames[i]), size)
                end
            end
        end

        is_quantum_statement(stmt) || continue
        qt = quantum_stmt_type(stmt)

        if qt === :measure
            cvar, locs = obtain_const_measure_stmt(stmt, ci)
            if cvar isa SlotNumber
                name = string(ci.slotnames[cvar.id])
                cbits[cvar] = (name, length(locs))
            else
                name = creg_name(v)
                cbits[v] = (name, length(locs))
            end

            # allocate new register for measurements
            allocate_new_qreg!(locs_to_regs, locs)
        elseif qt === :barrier
            locs = obtain_const(stmt.args[2], ci)::Locations
            record_locations!(target, locs_to_regs, locs)
        elseif qt === :gate
            if stmt.head === :invoke
                locs = obtain_const(stmt.args[4], ci)::Locations
            elseif stmt.head === :call
                locs = obtain_const(stmt.args[3], ci)::Locations
            end
            record_locations!(target, locs_to_regs, locs)
        elseif qt === :ctrl
            if stmt.head === :invoke
                locs = obtain_const(stmt.args[4], ci)::Locations
                ctrl = obtain_const(stmt.args[5], ci)::CtrlLocations
            elseif stmt.head === :call
                locs = obtain_const(stmt.args[3], ci)::Locations
                ctrl = obtain_const(stmt.args[4], ci)::CtrlLocations
            end

            record_locations!(target, locs_to_regs, locs)
            record_locations!(target, locs_to_regs, ctrl.storage)
        end
    end

    regs_to_locs = Dict{Int,Vector{Int}}()
    for (k, r) in locs_to_regs
        locs = get!(regs_to_locs, r, Int[])
        push!(locs, k)
    end

    # loc => reg, addr
    locs_to_reg_addr = Dict{Int,Tuple{Int,Int}}()
    for (r, locs) in regs_to_locs
        sort!(locs)
        for (k, loc) in enumerate(locs)
            locs_to_reg_addr[loc] = (r, k - 1)
        end
    end

    return RegMap(cbits, regs_to_locs, locs_to_reg_addr)
end

function Base.getindex(map::RegMap, loc::Locations)
    return ntuple(length(loc)) do k
        map.locs_to_reg_addr[loc.storage[k]]
    end
end

function Base.getindex(map::RegMap, loc::CtrlLocations)
    return map[loc.storage]
end

mutable struct QASMCodeGenState
    pc::Int
    stmt::Any
    regmap::RegMap

    name::Any
    carg_names::Any
    ssa_cname_map::Any
end

function QASMCodeGenState(target::TargetQASMTopLevel, ci::CodeInfo)
    return QASMCodeGenState(0, nothing, RegMap(target, ci), nothing, nothing, nothing)
end

function QASMCodeGenState(target::TargetQASMGate, ci::CodeInfo)
    name, carg_names, ssa_cname_map = scan_cargs(ci)
    return QASMCodeGenState(0, nothing, RegMap(target, ci), name, carg_names, ssa_cname_map)
end

# NOTE:
# we can only transform routines satisfy the following:
# 1. locations are constants, in Julia we can calculate locations dynamically
#    but this is not allowed in QASM
# 2. do not contain classical functions calls except for
# fn = ("sin" | "cos" | "tan" | "exp" | "ln" | "sqrt")
# binop = ('+' | '-' | '*' | '/')
# since QASM's if is actually a GotoIfNot node
# we don't reloop the SSA here, but assume the CodeInfo should
# not contain any GotoNode, which is incompatible with QASM

function allocate_new_qreg!(locs_to_regs, locs)
    if length(locs) == 1
        raw = locs.storage
        get!(locs_to_regs, raw, 1)
    else
        k_reg = maximum(get(locs_to_regs, each, 1) for each in locs) + 1

        for each in locs
            locs_to_regs[each] = k_reg
        end
    end
end

function record_locations!(::TargetQASMTopLevel, locs_to_regs::Dict{Int,Int}, locs::Locations)
    for each in locs
        get!(locs_to_regs, each, 1)
    end
    return locs_to_regs
end

function record_locations!(::TargetQASMGate, locs_to_regs::Dict{Int,Int}, locs::Locations)
    for each in locs
        if !haskey(locs_to_regs, each)
            locs_to_regs[each] = length(keys(locs_to_regs)) + 1
        end
    end
    return locs_to_regs
end

qreg_name(idx::Int) = string("qreg_", idx)
creg_name(idx::Int) = string("creg_", idx)

function codegen(target::TargetQASMTopLevel, ci::CodeInfo)
    st = QASMCodeGenState(target, ci)
    prog = Any[]

    # allocate registers
    for (k, locs) in st.regmap.regs_to_locs
        push!(
            prog,
            QASM.RegDecl(
                Token{:reserved}("qreg"),
                # we probably want to have a better strategy
                # to avoid potential name conflicts
                Token{:id}(qreg_name(k)),
                Token{:int}(string(length(locs))),
            ),
        )
    end

    for (_, (name, size)) in st.regmap.cbits
        pushfirst!(
            prog,
            QASM.RegDecl(Token{:reserved}("creg"), Token{:id}(name), Token{:int}(string(size))),
        )
    end

    # NOTE: QASM compatible code won't have
    # branches except simple `if x == y` statement
    # as a result, we don't need program counter
    # here, just execute directly
    st.pc = 1
    while st.pc <= length(ci.code)
        inst = nothing
        st.stmt = ci.code[st.pc]
        if st.stmt isa Expr && is_quantum_statement(st.stmt)
            inst = codegen_stmt(target, ci, st)
            st.pc += 1
        elseif st.stmt isa GotoIfNot
            inst = codegen_ifnot(target, ci, st)
        else
            st.pc += 1
        end

        isnothing(inst) && continue
        if inst isa Vector
            append!(prog, inst)
        else
            push!(prog, inst)
        end
    end
    return QASM.MainProgram(v"2.0", prog)
end

function qasm_gate_name(spec)
    name = string(routine_name(spec))
    if '#' in name
        name = "__julia_lambda" * replace(name, "#" => "_")
    end
    return name
end

function scan_cargs(ci::CodeInfo)
    v = findfirst(ci.code) do stmt
        @match stmt begin
            Expr(:call, getfield, Argument(2), :variables) => true
            Expr(:call, GlobalRef(Base, :getfield), Argument(2), QuoteNode(:variables)) => true
            Expr(:call, getfield, SlotNumber(2), QuoteNode(:variables)) => true
            Expr(:call, GlobalRef(Base, :getfield), SlotNumber(2), QuoteNode(:variables)) => true
            _ => false
        end
    end

    @assert !isnothing(ci.parent)
    spec = ci.parent.specTypes.parameters[2]

    # none of the classical parameters are used
    isnothing(v) && return qasm_gate_name(spec), String[], Dict{Int,String}()

    tt = Tuple{spec.parameters[1],spec.parameters[2].parameters...}
    ms = methods(routine_stub, tt)
    length(ms) == 1 || error("ambiguous method call")
    method = first(ms)
    carg_names = split(chop(method.slot_syms), '\0')[3:end]
    cargs = Dict{Int,String}()

    for (v, stmt) in enumerate(ci.code)
        name = @match stmt begin
            Expr(:call, getfield, SSAValue(v), k, true) => carg_names[k]
            Expr(:(=), SlotNumber(slot), Expr(:call, GlobalRef(Base, :getindex), SSAValue(v), i)) => string(ci.slotnames[slot])
            _ => nothing
        end

        isnothing(name) && continue
        cargs[v] = name
    end

    return qasm_gate_name(spec), carg_names, cargs
end

function codegen(target::TargetQASMGate, ci::CodeInfo)
    st = QASMCodeGenState(target, ci)
    qargs = Any[Token{:id}(qreg_name(k)) for (k, _) in st.regmap.regs_to_locs]
    cargs = Any[Token{:id}(cname) for cname in st.carg_names]
    decl = QASM.GateDecl(Token{:id}(st.name), cargs, qargs)

    prog = Any[]

    # NOTE: QASM compatible code won't have
    # branches except simple `if x == y` statement
    # as a result, we don't need program counter
    # here, just execute directly
    st.pc = 1
    while st.pc <= length(ci.code)
        inst = nothing
        st.stmt = ci.code[st.pc]
        if st.stmt isa Expr && is_quantum_statement(st.stmt)
            inst = codegen_stmt(target, ci, st)
            st.pc += 1
        elseif st.stmt isa GotoIfNot
            inst = codegen_ifnot(target, ci, st)
        else
            st.pc += 1
        end

        isnothing(inst) && continue
        if inst isa Vector
            append!(prog, inst)
        else
            push!(prog, inst)
        end
    end
    return QASM.Gate(decl, prog)
end

function _qasm_name(x)
    if x in [:X, :Y, :Z, :H, :T, :Rx, :Ry, :Rz]
        return lowercase(string(x))
    else
        return string(x)
    end
end

function codegen_stmt(target::TargetQASM, ci::CodeInfo, st::QASMCodeGenState)
    qt = quantum_stmt_type(st.stmt)
    if qt === :gate
        inst = codegen_gate(target, ci, st)
    elseif qt === :ctrl
        inst = codegen_ctrl(target, ci, st)
    elseif qt === :measure
        inst = codegen_measure(target, ci, st)
    elseif qt === :barrier
        inst = codegen_barrier(target, ci, st)
    else
        error("incompatible statement for QASM: $(st.stmt)")
    end
    return inst
end

function index_qreg(r::Int, addr::Int, regmap::RegMap)
    # do not index qreg if it only has one qubit
    if length(regmap.regs_to_locs[r]) == 1 && addr == 0
        return QASM.Bit(qreg_name(r))
    else
        return QASM.Bit(qreg_name(r), addr)
    end
end

function index_qreg(loc::Locations, regmap::RegMap)
    return map(regmap[loc]) do (r, addr)
        index_qreg(r, addr, regmap)
    end
end

index_qreg(ctrl::CtrlLocations, regmap::RegMap) = index_qreg(ctrl.storage, regmap)

function codegen_gate(target::TargetQASM, ci::CodeInfo, st::QASMCodeGenState)
    # NOTE: QASM compatible code should have constant location
    gate, gt, locs = obtain_const_gate_stmt(st.stmt, ci)

    gt <: IntrinsicRoutine || gt <: RoutineSpec || error("invalid gate type: $gate::$gt")

    if gt <: IntrinsicRoutine
        return codegen_intrinsic_gate(target, gate, gt, locs, ci, st)
    else
        name = qasm_gate_name(gt)
        cargs = codegen_cargs(target, ci, gate, st)
        qargs = Any[]

        for k in locs.storage
            r, addr = st.regmap.locs_to_reg_addr[k]
            push!(qargs, index_qreg(r, addr, st.regmap))
        end

        return QASM.Instruction(name, cargs, qargs)
    end
end

# TODO: polish duplicated code
function codegen_intrinsic_gate(
    target::TargetQASM,
    @nospecialize(gate),
    gt,
    locs::Locations,
    ci::CodeInfo,
    st::QASMCodeGenState,
)
    # expand the single qubit gates syntax sugar
    # this might be better to be moved to codeinfo
    # pre-processing stage
    # TODO: we probably should do this right after
    #       type infer
    if target.inline_intrinsic
        if gate isa IntrinsicRoutine
            inst = code_qasm(gate)
        elseif gate isa Expr
            cargs = codegen_cargs(target, ci, gate, st)
            inst = code_qasm(gt, cargs)
        end

        if is_one_qubit_gate(gt)
            inst = inst::QASM.UGate
            insts = []
            for k in locs.storage
                r, addr = st.regmap.locs_to_reg_addr[k]
                push!(insts, QASM.UGate(inst.z1, inst.y, inst.z2, index_qreg(r, addr, st.regmap)))
            end
            return insts
        else
            inst = inst::QASM.Instruction
            for k in locs.storage
                r, addr = st.regmap.locs_to_reg_addr[k]
                push!(qargs, index_qreg(r, addr, st.regmap))
            end

            return QASM.Instruction(inst.name, copy(inst.cargs), qargs)
        end
    else
        cargs = codegen_cargs(target, ci, gate, st)
        name = qasm_gate_name(gt)

        if is_one_qubit_gate(gt)
            insts = []
            for k in locs.storage
                r, addr = st.regmap.locs_to_reg_addr[k]
                push!(insts, QASM.Instruction(name, cargs, Any[index_qreg(r, addr, st.regmap)]))
            end
            return insts
        else
            for k in locs.storage
                r, addr = st.regmap.locs_to_reg_addr[k]
                push!(qargs, index_qreg(r, addr, st.regmap))
            end
            return QASM.Instruction(name, cargs, qargs)
        end
    end
end

function codegen_cargs(::TargetQASMTopLevel, ::CodeInfo, @nospecialize(gate), ::QASMCodeGenState)
    if gate isa RoutineSpec
        # constant parameters
        # non-constant parameters in toplevel is not allowed
        return Any[Token{:unnamed}(string(x)) for x in gate.variables]
    elseif gate isa IntrinsicRoutine
        return Any[Token{:unnamed}(string(getfield(gate, each))) for each in fieldnames(typeof(gate))]
    else
        error("classical variable in toplevel is not allowed in QASM")
    end
end

function codegen_cargs(
    target::TargetQASMGate,
    ci::CodeInfo,
    @nospecialize(gate),
    st::QASMCodeGenState,
)
    if gate isa Expr
        # IntrinsicSpec/RoutineSpec take a tuple
        # so we need to find the actual variables
        if gate.head === :new || gate.head === :call
            vars = gate.args[2:end]
        end

        cargs = Any[]
        for each in vars
            val = codegen_exp(target, ci, each, st)
            if val isa Vector
                append!(cargs, val)
            else
                push!(cargs, val)
            end
        end
        return cargs
    elseif gate isa RoutineSpec
        # constant parameters
        # non-constant parameters in toplevel is not allowed
        return Any[codegen_exp(target, ci, x, st) for x in gate.variables]
    elseif gate isa IntrinsicRoutine
        return Any[
            codegen_exp(target, ci, getfield(gate, each), st) for each in fieldnames(typeof(gate))
        ]
    else
        error("invalid instruction: $gate")
    end
end

# NOTE: toplevel program does not allow non-constant
# classical function calls
function codegen_exp(target::TargetQASM, ci::CodeInfo, @nospecialize(stmt), st::QASMCodeGenState)
    if stmt isa SlotNumber
        return Token{:id}(string(ci.slotnames[stmt.id]))
    end

    if stmt isa SSAValue
        if haskey(st.ssa_cname_map, stmt.id)
            return Token{:id}(st.ssa_cname_map[stmt.id])
        else
            return codegen_exp(target, ci, ci.code[stmt.id], st)
        end
    end

    if stmt isa Int
        return Token{:int}(string(stmt))
    elseif stmt isa AbstractFloat
        return Token{:float64}(string(Float64(stmt)))
    end

    stmt isa Expr || error("classical expression for QASM cannot contain control flow, got $stmt")

    if stmt.head === :call
        f = stmt.args[1]

        if f isa GlobalRef
            mod, fn_name = f.mod, f.name
            fn = Core.Compiler.abstract_eval_global(mod, fn_name).val
            fn === Any && error("cannot determine function call: $stmt")
        else
            fn = f
            fn_name = nameof(fn)
        end
        args = stmt.args[2:end]
    elseif stmt.head === :invoke
        fn = stmt.args[2]
        fn_name = stmt.args[1].def.name
        args = stmt.args[3:end]
    else
        error("incompatible expression for QASM: $stmt")
    end

    if fn === Core.tuple
        return Any[codegen_exp(target, ci, each, st) for each in args]
    end

    if fn === Core.Intrinsics.neg_float
        fn_name = :(-)
    end

    if length(args) == 1
        return QASM.FnExp(fn_name, codegen_exp(target, ci, args[1], st))
    elseif length(args) == 2 # binop
        if (fn === +) || (fn === Core.Intrinsics.add_float) || (fn === Core.Intrinsics.add_int)
            token = Token{:reserved}("+")
        elseif (fn === -) || (fn === Core.Intrinsics.sub_float) || (fn === Core.Intrinsics.sub_int)
            token = Token{:reserved}("-")
        elseif (fn === *) || (fn === Core.Intrinsics.mul_float) || (fn === Core.Intrinsics.mul_int)
            token = Token{:reserved}("*")
        elseif (fn === /) || (fn === Core.Intrinsics.div_float)
            token = Token{:reserved}("/")
        else
            error("incompatible binop for QASM: $fn")
            # token = Token{:reserved}(fn_name)
        end
        return (codegen_exp(target, ci, args[1], st), token, codegen_exp(target, ci, args[2], st))
    else
        error("incompatible function call for QASM: $stmt")
    end
end

function codegen_ctrl(::TargetQASM, ci::CodeInfo, st::QASMCodeGenState)
    gate, gt, locs, ctrl = obtain_const_ctrl_stmt(st.stmt, ci)
    gt <: IntrinsicRoutine || gt <: RoutineSpec || error("invalid gate type: $gate::$gt")

    all(ctrl.flags) || error("inverse ctrl is not supported in QASM backend yet")
    if gate === Intrinsics.X && length(ctrl) == 1 && length(locs) == 1
        qargs = Any[index_qreg(ctrl, st.regmap)..., index_qreg(locs, st.regmap)...]
        return QASM.Instruction(Token{:id}("CX"), Any[], qargs)
    elseif gate === Intrinsics.X && length(ctrl) == 2 && length(locs) == 1
        error(
            "ccx is not valid intrinsic QASM instruction. posssible fix:\n",
            "1. include a qasm stdlib e.g qelib<version>.inc and use ccx gate from it\n",
            "2. define your own ccx gate via @device using CX and single qubit gate",
        )
        # ctrl.flags[1] || error("inverse ctrl is not supported in QASM backend yet")
        # qargs = Any[index_qreg(ctrl, st.regmap)..., index_qreg(locs, st.regmap)...]
        # return QASM.Instruction(
        #     Token{:id}("ccx"),
        #     Any[], qargs
        # )
    else
        error("invalid control statement for QASM backend, got: $(st.stmt)")
    end
end

function codegen_measure(::TargetQASM, ci::CodeInfo, st::QASMCodeGenState)
    if st.stmt.head === :(=)
        slot = st.stmt.args[1]::SlotNumber
        measure_ex = st.stmt.args[2]
        locs = obtain_const(measure_ex.args[2], ci)::Locations
        cname, _ = st.regmap.cbits[slot]
    else
        locs = obtain_const(st.stmt.args[2], ci)::Locations
        cname, _ = st.regmap.cbits[st.pc]
    end

    r, addr = st.regmap.locs_to_reg_addr[first(locs)]

    if length(locs) == 1
        qarg = index_qreg(r, addr, st.regmap)
        return QASM.Measure(qarg, QASM.Bit(cname, 0))
    else
        # by construction the registers are the same
        # and is exactly of size length(locs)
        return QASM.Measure(QASM.Bit(qreg_name(r)), QASM.Bit(cname, length(locs)))
    end
end

function codegen_barrier(::TargetQASM, ci::CodeInfo, st::QASMCodeGenState)
    locs = obtain_const(st.stmt.args[2], ci)::Locations

    qargs = Any[]
    args = Dict{Int,Vector{Int}}()

    for each in locs
        r, addr = st.regmap.locs_to_reg_addr[each]
        addrs = get!(args, r, Int[])
        push!(addrs, addr)
    end

    for (r, addrs) in args
        # do not index qreg explicitly if barrier size
        # is the same with register size
        if length(st.regmap.regs_to_locs[r]) == length(addrs)
            push!(qargs, QASM.Bit(qreg_name(r)))
        else
            for addr in addrs
                push!(qargs, QASM.Bit(qreg_name(r), addr))
            end
        end
    end
    return QASM.Barrier(qargs)
end

function codegen_ifnot(target::TargetQASM, ci::CodeInfo, st::QASMCodeGenState)
    cond = st.stmt.cond::SSAValue
    creg, right = obtain_qasm_ifnot_cond(cond, ci)

    if creg isa SlotNumber
        cname = ci.slotnames[creg.id]
    elseif creg isa SSAValue
        # NOTE:
        # in QASM, this will only be assigned once
        # from measure statement
        # TODO: we might want to make this more general
        # by having an intermediate type MeasureResult
        cname = creg_name(creg.id)
    end
    # find the first quantum stmt after goto
    move_to_first_quantum_stmt(ci, st)
    body = codegen_stmt(target, ci, st)
    return QASM.IfStmt(Token{:id}(cname), Token{:int}(string(right)), body)
end

function validate(target::TargetQASM, ci::CodeInfo)
    for (v, stmt) in enumerate(ci)
        if is_quantum_statement(stmt)
        elseif stmt isa GotoIfNot
        elseif stmt isa Expr
            if stmt.head === :call
                validate_call(target, ci, stmt)
            end
        end
        stmt_type = ci.ssavaluetypes[v]

    end
end

# NOTE:
# valid function in QASM compatible program:
# basic math functions:
# fn = ("sin" | "cos" | "tan" | "exp" | "ln" | "sqrt")
# binop = ('+' | '-' | '*' | '/')
function validate_call(::TargetQASM, ci::CodeInfo, stmt::Expr)
    if stmt.args[1] isa GlobalRef
        stmt.mod === Base
        stmt.name in []
    end
end


text:
push!(LOAD_PATH,"../src/")

using Documenter, ImplicitArrays

const pages = [
    "Home" => "index.md"
]

makedocs(
    modules   = [ImplicitArrays],
    clean     = true,
    doctest   = true,
    linkcheck = true,
    checkdocs = :all,
    pages     = pages,
    format    = Documenter.HTML(),
    sitename  = "ImplicitArrays.jl",
    repo      = "https://github.com/tkemmer/ImplicitArrays.jl/blob/master{path}"
)

deploydocs(
    repo = "github.com/tkemmer/ImplicitArrays.jl.git"
)

ext: jl


text:
Base.getindex(cb::Cbinding{T}) where {T<:Cfunction} = getglobal(cb)
Base.getindex(cb::Cbinding{T}) where {T} = unsafe_load(getglobal(cb))
Base.setindex!(cb::Cbinding{T}, val) where {T} = unsafe_store!(getglobal(cb), convert(T, val))

Base.fieldcount(::Type{Cbinding{CA}}) where {CA<:Caggregates} = fieldcount(CA)
Base.fieldtypes(::Type{Cbinding{CA}}) where {CA<:Caggregates} = fieldtypes(CA)
Base.fieldnames(::Type{Cbinding{CA}}) where {CA<:Caggregates} = fieldnames(CA)
Base.propertynames(cb::Cbinding) = fieldnames(typeof(cb))

Base.getproperty(cb::Cbinding{CA}, sym::Symbol) where {CA<:Caggregates} = getproperty(getglobal(cb), sym)
Base.setproperty!(cb::Cbinding{CA}, sym::Symbol, val) where {CA<:Caggregate} = setproperty!(getglobal(cb), sym, val)

@generated function getglobal(glob::Cbinding{T, lib, name}) where {T, lib, name}
	glob = lib isa Symbol ? :(($(QuoteNode(name)), $(String(lib)))) : QuoteNode(name)
	return quote
		return Cptr{bitstype(T)}(cglobal($(glob)))
	end
end


text:
module YouBotPingPong

using ModiaLang
using Unitful
import Modia3D

# ModiaLang models
include("$(ModiaLang.path)/models/Blocks.jl")
include("$(ModiaLang.path)/models/Electric.jl")
include("$(ModiaLang.path)/models/Rotational.jl")

import Modia3D
using  Modia3D.ModiaInterface

# some constants

# visual materials
vmat1 = VisualMaterial(color="LightBlue"  , transparency=0.0)  # material of FileMesh
vmat2 = VisualMaterial(color="DarkGrey"   , transparency=0.0)  # material of work piece
vmat3 = VisualMaterial(color="DodgerBlue3", transparency=0.0)  # material of table
vmat4 = VisualMaterial(color=[255,215,0]  , transparency=0.0)  # material of ground

xPosTable = 0.71
tableX    = 0.3
tableY    = 0.3
tableZ    = 0.01
heigthLeg = 0.37
widthLeg  = 0.02
diameter = 0.05
right_finger_offset=0.0

# all needed paths to fileMeshes
base_frame_obj           = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/base_frame.obj")
plate_obj                = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/plate.obj")
front_right_wheel_obj    = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/front-right_wheel.obj")
front_left_wheel_obj     = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/front-left_wheel.obj")
back_right_wheel_obj     = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/back-right_wheel.obj")
back_left_wheel_obj      = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/back-left_wheel.obj")
arm_base_frame_obj       = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/arm_base_frame.obj")
arm_joint_1_obj          = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/arm_joint_1.obj")
arm_joint_2_obj          = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/arm_joint_2.obj")
arm_joint_3_obj          = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/arm_joint_3.obj")
arm_joint_4_obj          = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/arm_joint_4.obj")
arm_joint_5_obj          = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/arm_joint_5.obj")
gripper_base_frame_obj   = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/gripper_base_frame.obj")
gripper_left_finger_obj  = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/gripper_left_finger.obj")
gripper_right_finger_obj = joinpath(Modia3D.path, "objects/robot_KUKA_YouBot/gripper_right_finger.obj")

# Drive train data: motor inertias and gear ratios
nullRot = nothing #SMatrix{3,3,Float64,9}(Matrix(1.0I, 3, 3))

motorInertia1 = 0.0000135 + 0.000000409
motorInertia2 = 0.0000135 + 0.000000409
motorInertia3 = 0.0000135 + 0.000000071
motorInertia4 = 0.00000925 + 0.000000071
motorInertia5 = 0.0000035 + 0.000000071
gearRatio1    = 156.0
gearRatio2    = 156.0
gearRatio3    = 100.0
gearRatio4    = 71.0
gearRatio5    = 71.0

m1=1.390
translation1 =[0.033,0,0]
rotation1 =Modia3D.rot1(180u"°")

m2=1.318
translation2=[0.155,0,0]
rotation2=Modia3D.rot123(90u"°", 0.0, -90u"°")

m3=0.821
translation3=[0,0.135,0]
rotation3=Modia3D.rot3(-90u"°")

m4=0.769
translation4=[0,0.11316,0]

m5=0.687
translation5=[0,0,0.05716]
rotation5=Modia3D.rot1(-90u"°")

### ----------------- Servo Model -----------------------
# parameters for Link
axisLink = 3
dLink  = 1.0
k1Link = 50.0
k2Link = 0.1
T2Link = 1.0
# parameters for Gripper
axisGripper = 2
dGripper  = 1.0
k1Gripper = 50.0
k2Gripper = 0.1
T2Gripper = 1.0
motorInertiaGripper = 0.0
gearRatioGripper    = 1.0

#### ----------- Path Planning ------------------
referencePath1 = Modia3D.ReferencePath(
    names = ["angle1", "angle2", "angle3", "angle4", "angle5", "gripper"],
    position = [0.0,     0.0,     pi/2,   0.0,    0.0,   0.0],
    v_max =    [2.68512, 2.68512, 4.8879, 5.8997, 5.8997, 1.0],
    a_max =    [1.5, 1.5, 1.5, 1.5, 1.5, 0.5])
#
Modia3D.ptpJointSpace(referencePath = referencePath1, positions =
    [0.0 0.0 pi/2     0.0 0.0 0.0;
    0.0  0.3 pi/2-0.3 0.0 0.0 0.0;
    0.0  0.0 0.0      0.0 0.0 0.0])

getReferencePath() = referencePath1

# Controller Model
Controller = Model(
    # Interface
    refLoadAngle     = input,
    actualMotorAngle = input,
    actualMotorSpeed = input,
    refTorque        = output,
    # Components
    gainOuter     = Gain,
    angleFeedback = Feedback,
    gainInner     = Gain,
    speedFeedback = Feedback,
    PI = PI,

    connect = :[
        (refLoadAngle, gainOuter.u)
        (gainOuter.y, angleFeedback.u1)
        (actualMotorAngle, angleFeedback.u2)
        (angleFeedback.y, gainInner.u)
        (gainInner.y, speedFeedback.u1)
        (actualMotorSpeed, speedFeedback.u2)
        (speedFeedback.y, PI.u)
        (PI.y, refTorque)]
)

Drive = Model(
    # Interface
    refTorque   = input,
    actualSpeed = output,
    actualAngle = output,
    flange      = Flange,
    # Components
    torque       = UnitlessTorque,
    speedSensor  = UnitlessSpeedSensor,
    angleSensor  = UnitlessAngleSensor,
    motorInertia = Inertia,
    idealGear    = IdealGear,

    connect = :[
        (refTorque, torque.tau)
        (torque.flange, speedSensor.flange, angleSensor.flange, motorInertia.flange_a)
        (motorInertia.flange_b, idealGear.flange_a)
        (idealGear.flange_b, flange)
        (speedSensor.w, actualSpeed)
        (angleSensor.phi, actualAngle)
        ]
)

Servo = Model(
    refLoadAngle = input,
    flange       = Flange,
    ppi          = Controller,
    drive        = Drive,

    connect = :[
        # inputs of ppi
        (refLoadAngle, ppi.refLoadAngle)
        (ppi.actualMotorSpeed, drive.actualSpeed)
        (ppi.actualMotorAngle, drive.actualAngle)
        # output of ppi --> input of motor
        (ppi.refTorque, drive.refTorque)
        # Flange of drive
        (drive.flange, flange)
        ]
)

Ground = Model(
    ground = Object3D(parent=:world,
        translation=[xPosTable, 0.0, -0.005], feature = Solid(shape =Box(lengthX=2.5, lengthY=2.5, lengthZ=0.01), collisionSmoothingRadius=0.001, solidMaterial="DryWood", visualMaterial=vmat4, collision=true))
)

Table = Model(
    plate = Object3D(parent=:world, translation=[xPosTable, 0.0, heigthLeg],
        feature=Solid(shape=Box(lengthX=tableX, lengthY=tableY, lengthZ=tableZ),
        collisionSmoothingRadius=0.001, solidMaterial="BilliardTable", collision=true, visualMaterial=vmat3)),
    leg1  = Object3D(parent=:plate, translation=[ tableX/2-widthLeg/2,  tableY/2-widthLeg/2, -heigthLeg/2],
        feature=Solid(shape=Box(lengthX=widthLeg, lengthY=widthLeg, lengthZ=heigthLeg),
        solidMaterial="DryWood", visualMaterial=vmat3)),
    leg2  = Object3D(parent=:plate, translation=[-tableX/2+widthLeg/2,  tableY/2-widthLeg/2, -heigthLeg/2],
        feature=Solid(shape=Box(lengthX=widthLeg, lengthY=widthLeg, lengthZ=heigthLeg), solidMaterial="DryWood", visualMaterial=vmat3)),
    leg3  = Object3D(parent=:plate, translation=[ tableX/2-widthLeg/2, -tableY/2+widthLeg/2, -heigthLeg/2],
        feature=Solid(shape=Box(lengthX=widthLeg, lengthY=widthLeg, lengthZ=heigthLeg), solidMaterial="DryWood", visualMaterial=vmat3)),
    leg4  = Object3D(parent=:plate, translation=[-tableX/2+widthLeg/2, -tableY/2+widthLeg/2, -heigthLeg/2],
        feature=Solid(shape=Box(lengthX=widthLeg, lengthY=widthLeg, lengthZ=heigthLeg), solidMaterial="DryWood", visualMaterial=vmat3))
)

Base = Model(
    rota = Par(value = :(nullRot)),
    trans = [0.0,0.0,0.0],
    base_frame = Object3D(parent=:world, rotation = :rota,
        translation=:(trans + [0, 0, 0.084]),
        feature=Solid(shape=FileMesh(filename = base_frame_obj), massProperties=MassPropertiesFromShapeAndMass(mass=19.803), visualMaterial=vmat1 )),
    plate = Object3D(parent=:base_frame, rotation = :rota,
        translation=[-0.159, 0, 0.046],
        feature=Solid(shape=FileMesh(filename = plate_obj), massProperties=MassPropertiesFromShapeAndMass(mass=2.397), visualMaterial=vmat1)),
    front_right_wheel = Object3D(parent=:base_frame, rotation = :rota,
        translation=[0.228, -0.158, -0.034],
        feature=Solid(shape=FileMesh(filename = front_right_wheel_obj), massProperties=MassPropertiesFromShapeAndMass(mass=1.4), visualMaterial=vmat1)),
    front_left_wheel = Object3D(parent=:base_frame, rotation = :rota,
        translation=[0.228, 0.158, -0.034],
        feature=Solid(shape=FileMesh(filename = front_left_wheel_obj), massProperties=MassPropertiesFromShapeAndMass(mass=1.4), visualMaterial=vmat1)),
    back_right_wheel = Object3D(parent=:base_frame, rotation = :rota,
        translation=[-0.228, -0.158, -0.034],
        feature=Solid(shape=FileMesh(filename = back_right_wheel_obj), massProperties=MassPropertiesFromShapeAndMass(mass=1.4), visualMaterial=vmat1)),
    back_left_wheel   = Object3D(parent=:base_frame, rotation = :rota,
        translation=[-0.228, 0.158, -0.034],
        feature=Solid(shape=FileMesh(filename = back_left_wheel_obj), massProperties=MassPropertiesFromShapeAndMass(mass=1.4), visualMaterial=vmat1))
)

servoParameters1 = Map(
                      ppi = Map(
                          gainOuter = Map(k=gearRatio1),
                          gainInner = Map(k=k1Link),
                          PI        = Map(k=k2Link, T=T2Link)
                      ),
                      drive = Map(
                          motorInertia = Map(J=motorInertia1),
                          idealGear = Map(ratio=gearRatio1)
                      )
                  )

servoParameters2 = Map(
                      ppi = Map(
                          gainOuter = Map(k=gearRatio2),
                          gainInner = Map(k=k1Link),
                          PI        = Map(k=k2Link, T=T2Link)
                      ),
                      drive = Map(
                          motorInertia = Map(J=motorInertia2),
                          idealGear = Map(ratio=gearRatio2)
                      )
                  )

servoParameters3 = Map(
                      ppi = Map(
                          gainOuter = Map(k=gearRatio3),
                          gainInner = Map(k=k1Link),
                          PI        = Map(k=k2Link, T=T2Link)
                      ),
                      drive = Map(
                          motorInertia = Map(J=motorInertia3),
                          idealGear = Map(ratio=gearRatio3)
                      )
                  )

servoParameters4 = Map(
                      ppi = Map(
                          gainOuter = Map(k=gearRatio4),
                          gainInner = Map(k=k1Link),
                          PI        = Map(k=k2Link, T=T2Link)
                      ),
                      drive = Map(
                          motorInertia = Map(J=motorInertia4),
                          idealGear = Map(ratio=gearRatio4)
                      )
                  )

servoParameters5 = Map(
                      ppi = Map(
                          gainOuter = Map(k=gearRatio5),
                          gainInner = Map(k=k1Link),
                          PI        = Map(k=k2Link, T=T2Link)
                      ),
                      drive = Map(
                          motorInertia = Map(J=motorInertia5),
                          idealGear = Map(ratio=gearRatio5)
                      )
                  )

featureBody1 = Solid(shape = FileMesh(filename = arm_joint_1_obj), massProperties = MassPropertiesFromShapeAndMass(mass=m1), visualMaterial=vmat1)
featureBody2 = Solid(shape = FileMesh(filename = arm_joint_2_obj), massProperties = MassPropertiesFromShapeAndMass(mass=m2), visualMaterial=vmat1)
featureBody3 = Solid(shape = FileMesh(filename = arm_joint_3_obj), massProperties = MassPropertiesFromShapeAndMass(mass=m3), visualMaterial=vmat1)
featureBody4 = Solid(shape = FileMesh(filename = arm_joint_4_obj), massProperties = MassPropertiesFromShapeAndMass(mass=m4), visualMaterial=vmat1)
featureBody5 = Solid(shape = FileMesh(filename = arm_joint_5_obj), massProperties = MassPropertiesFromShapeAndMass(mass=m5), visualMaterial=vmat1)

linkParameters1 = Map(
                    parent1 = Par(value = :(armBase_b)),
                    featureBody = featureBody1, # Par(value = :featureBody1),
                    initRefPos = referencePath1.position[1],
                    trans = translation1,
                    rota = Par(value = :(rotation1))
)

linkParameters2 = Map(
                    parent1 = Par(value = :(link1.obj2)),
                    featureBody = featureBody2, #Par(value = :featureBody2),
                    m = m2,
                    initRefPos = referencePath1.position[2],
                    trans = translation2,
                    rota = Par(value = :(rotation2))
)

linkParameters3 = Map(
                    parent1 = Par(value = :(link2.obj2)),
                    featureBody = featureBody3, # Par(value = :featureBody3),
                    m = m3,
                    initRefPos = referencePath1.position[3],
                    trans = translation3,
                    rota = Par(value = :(rotation3))
)

linkParameters4 = Map(
                    parent1 = Par(value = :(link3.obj2)),
                    featureBody = featureBody4, # Par(value = :featureBody4),
                    m = m4,
                    initRefPos = referencePath1.position[4],
                    trans = translation4,
                    rota = Par(value = :(nullRot))
)

linkParameters5 = Map(
                    parent1 = Par(value = :(link4.obj2)),
                    featureBody = featureBody5, # Par(value = :featureBody5),
                    m = m5,
                    initRefPos = referencePath1.position[5],
                    trans = translation5,
                    rota = Par(value = :(rotation5))
)

featureVisual1 = Visual(shape=Cylinder(axis=3, diameter=0.01, length=0.12), visualMaterial=vmat1)

Link = Model(
    parent1 = Par(value = :(nothing)),
    featureBody = featureBody1,
    # featureVisual = featureVisual1,
    trans = [0,0,0],
    rota = Par(value = :(nullRot)), #rotation1, #Modia3D.rot1(180u"°"),

    obj1 = Object3D(parent=:parent1, rotation=:rota,  visualizeFrame=false), # feature=:featureVisual,
    body = Object3D(feature = :featureBody),
    obj2 = Object3D(parent =:body, translation = :trans),
)

Gripper = Model(
    obj1 = Object3D(parent=:(link5.obj2), rotation=Modia3D.rot3(-180u"°")),
    gripper_base_frame = Object3D(parent=:obj1, feature=Solid(shape=
        FileMesh(filename = gripper_base_frame_obj), massProperties=MassPropertiesFromShapeAndMass(mass=0.199), visualMaterial=vmat1)),
    gripper_left_finger_a = Object3D(parent=:gripper_base_frame),
    gripper_left_finger = Object3D(parent=:gripper_left_finger_a,
        translation=[0, 0.0082, 0], feature=Solid(shape= FileMesh(filename = gripper_left_finger_obj), massProperties=MassPropertiesFromShapeAndMass(mass=0.010), visualMaterial=vmat1, contactMaterial="BilliardCue", collision=true)),
    gripper_right_finger_a = Object3D(parent=:gripper_base_frame,
        translation=[0,-right_finger_offset,0]),
    gripper_right_finger = Object3D(parent=:gripper_right_finger_a, translation=[0, -0.0082, 0],
        feature = Solid(shape = FileMesh(filename = gripper_right_finger_obj), massProperties=MassPropertiesFromShapeAndMass(mass=0.010), visualMaterial=vmat1, contactMaterial="BilliardCue", collision=true))
)

YouBot = Model(
    base = Base,

    arm_base_frame = Object3D(parent=:(base.base_frame),
        translation=[0.143, 0.0, 0.046],
        feature = Solid(shape = FileMesh(filename = arm_base_frame_obj), massProperties=MassPropertiesFromShapeAndMass(mass=0.961), visualMaterial=vmat1)),
    armBase_b = Object3D(parent=:arm_base_frame, translation=[0.024, 0, 0.115]),

    link1 = Link | linkParameters1,
    link2 = Link | linkParameters2,
    link3 = Link | linkParameters3,
    link4 = Link | linkParameters4,
    link5 = Link | linkParameters5,
    gripper = Gripper,

    rev1 = RevoluteWithFlange(obj1 = :(link1.obj1), obj2 = :(link1.body),
        axis=axisLink, phi = Var(init = getRefPathInitPosition(referencePath1, 1)), w=Var(init=0.0)),
    rev2 = RevoluteWithFlange(obj1 = :(link2.obj1), obj2 = :(link2.body),
        axis=axisLink, phi = Var(init = getRefPathInitPosition(referencePath1, 1)), w=Var(init=0.0)),
    rev3 = RevoluteWithFlange(obj1 = :(link3.obj1), obj2 = :(link3.body),
        axis=axisLink, phi = Var(init = getRefPathInitPosition(referencePath1, 1)), w=Var(init=0.0)),
    rev4 = RevoluteWithFlange(obj1 = :(link4.obj1), obj2 = :(link4.body),
        axis=axisLink, phi = Var(init = getRefPathInitPosition(referencePath1, 1)), w=Var(init=0.0)),
    rev5 = RevoluteWithFlange(obj1 = :(link5.obj1), obj2 = :(link5.body),
        axis=axisLink, phi = Var(init = getRefPathInitPosition(referencePath1, 1)), w=Var(init=0.0)),

    servo1 = Servo,
    servo2 = Servo,
    servo3 = Servo,
    servo4 = Servo,
    servo5 = Servo,

    equations=:[
        refPath = calculateRobotMovement(getReferencePath(), instantiatedModel),
        servo1.refLoadAngle = getRefPathPosition(refPath, 1),
        servo2.refLoadAngle = getRefPathPosition(refPath, 2),
        servo3.refLoadAngle = getRefPathPosition(refPath, 3),
        servo4.refLoadAngle = getRefPathPosition(refPath, 4),
        servo5.refLoadAngle = getRefPathPosition(refPath, 5)
    ],

    connect = :[
        (servo1.flange, rev1.flange)
        (servo2.flange, rev2.flange)
        (servo3.flange, rev3.flange)
        (servo4.flange, rev4.flange)
        (servo5.flange, rev5.flange)
        ]
)

Setup = Model(
    gravField = UniformGravityField(g=9.81, n=[0,0,-1]),
    world = Object3D(feature=Scene(gravityField = :gravField, visualizeFrames=false,
        defaultFrameLength=0.1, enableContactDetection=true, elasticContactReductionFactor=1e-4, animationFile="YouBotPingPong.json")),

    # worldFrame = Object3D(parent=:world, feature=Visual(shape=CoordinateSystem(length = 0.5))),

    table = Table,
    ground = Ground,

    sphere1 = Object3D(
        feature=Solid(shape=Sphere(diameter=diameter), visualMaterial=vmat2, solidMaterial="BilliardBall", collision=true)),

    free1 = FreeMotion(obj1=:(table.plate), obj2=:sphere1, r=Var(init=[-tableX/2+diameter/2+0.001, 0.0002, diameter/2+tableZ/2]), rot=Var(init=[pi/2, 0.0, 0.0])),

    sphere2 = Object3D(
        feature=Solid(shape=Sphere(diameter=diameter), visualMaterial=vmat2, solidMaterial="BilliardBall", collision=true)),

    free2 = FreeMotion(obj1=:(table.plate), obj2=:sphere2, r=Var(init=[tableX/2-diameter/2-0.0001, -0.003, diameter/2+tableZ/2]), rot=Var(init=[pi/2, 0.0, 0.0])),

    sphere3 = Object3D(
        feature=Solid(shape=Sphere(diameter=diameter), visualMaterial=vmat2, solidMaterial="BilliardBall", collision=true)),

    free3 = FreeMotion(obj1=:(table.plate), obj2=:sphere3, r=Var(init=[-0.01, tableX/2-diameter/2-0.002, diameter/2+tableZ/2]), rot=Var(init=[pi/2, 0.0, 0.0])),

    youbot1 = YouBot,
    youbot2 = YouBot,
    youbot3 = YouBot,
)

modelParameters = Map(

    youbot1 = Map(
        servo1 = servoParameters1,
        servo2 = servoParameters2,
        servo3 = servoParameters3,
        servo4 = servoParameters4,
        servo5 = servoParameters5,
    ),

    youbot2 = Map(
        base = Map(
            rota = Par(value = :(Modia3D.rot3(180u"°"))),
            trans = [2*xPosTable,0.0,0.0],
        ),
        servo1 = servoParameters1,
        servo2 = servoParameters2,
        servo3 = servoParameters3,
        servo4 = servoParameters4,
        servo5 = servoParameters5,
    ),

    youbot3 = Map(
        base = Map(
            rota = Par(value = :(Modia3D.rot3(270u"°"))),
            trans = [xPosTable,xPosTable,0.0],
        ),
        servo1 = servoParameters1,
        servo2 = servoParameters2,
        servo3 = servoParameters3,
        servo4 = servoParameters4,
        servo5 = servoParameters5,
    ),

)

@time youbotModel = buildModia3D(Setup) | modelParameters

@time youbot = @instantiateModel(youbotModel, unitless=true, logCode=false, log=false, logTiming=false)

# logDetails=false, logStateSelection=false, logCode=true, logExecution=false, logTiming=false

stopTime = 5.0
testTime = 2.6
tolerance = 1e-6
requiredFinalStates = [-0.032300771660657694, -0.09776355157608471, 0.02989486034548256, 0.0011936992776297414, -0.011989637576076287, 2.099730022663762e-6, 5.577214816036132, -0.1534225536631406, -3.21532234704311, -0.4788140661491195, -0.07211642268204188, -0.04304635380310547, -0.012699935790518379, -0.03485828664647874, 0.02989849118303316, -2.275491232405937e-9, -5.041402883273956e-9, -1.7398209953480762e-7, 2.563123055232681, 0.7445788546836765, 5.335313622529376, 2.7461632864541012e-9, 9.278780813150458e-8, 1.1701034876520994e-7, 0.02785869529463159, 0.07754234294775672, 0.029896332394685653, -8.128663695157891e-10, -6.03488193174259e-11, -8.194016417083457e-8, 3.299194401806199, -0.4488199388008023, 1.3333662358671825, 2.0876984002408836e-8, 5.0792452717284365e-9, -3.777196092825187e-9, -1.1073409369332207e-6, -1.5708163206493583e-6, 0.0043187679918921945, -0.05498125983659931, 0.018341522653238178, -0.23292171797074548, -2.5341171028613137e-5, 9.563725041475825e-6, 9.861867102762459e-8, -3.757424280959238e-8, -0.008202375463342989, 0.08428049560475342, -0.1986368654427638, -0.08581273410509312, 0.0003301828544531399, -1.1072500271224385e-6, -1.570907229785427e-6, 0.004318767967438113, -0.05498125981214347, 0.018341522645547594, -0.232921717963052, -2.5341188032203926e-5, 9.563742047263623e-6, 9.861938248651026e-8, -3.7574954279350363e-8, -0.008201680543548364, 0.08428030868855851, -0.19863690311168553, -0.08581279325228972, 0.0003301853295862967, -1.107507112926896e-6, -1.5706501456569102e-6, 0.004318768038688511, -0.05498125988339801, 0.01834152265864114, -0.23292171797615294, -2.534116207069802e-5, 9.563716080674608e-6, 9.862462181048238e-8, -3.7580193571310476e-8, -0.008203645720271581, 0.08428085329966532, -0.19863683899027065, -0.08581270295083859, 0.0003302035572851636]
simulate!(youbot, stopTime=testTime, tolerance=tolerance, log=true, logStates=false, requiredFinalStates=requiredFinalStates)

end


text:
# basic building block is TreeNode
type TreeNode{T, S}
    id::S
    data::T
    parent::Nullable{TreeNode{T,S}}
    children::Dict{S, TreeNode{T,S}}
    # isLeaf::Bool

    # constructors ---------------------------------------
    # with name, but without data
    function TreeNode{T, S}(id::S) where {T, S}
        x = new()
        x.id = id
        x.parent = Nullable{TreeNode{T,S}}()
        x.children = Dict{S, TreeNode{T,S}}()

        return(x)
    end

    # with name and data
    function TreeNode{T, S}(id::S, data::T) where {T, S}
        x = new()
        x.id = id
        x.parent = Nullable{TreeNode{T,S}}()
        x.children = Dict{S, TreeNode{T,S}}()
        x.data = data

        return(x)
    end
end


text:
using Flux.Optimise: ADAM, update!
using StatsBase
using Random
Random.seed!(42)

using VAN
using VAN:loss, train

nbits = 10
nsamples = 1000
nhiddens = [500]

K = randn(nbits, nbits)
K = (K+K')/2
@show exact_free_energy(K)

model = PSAModel(nbits)
#model = AutoRegressiveModel(nbits, nhiddens)

loss(K, model, nsamples)

train(K, model; optimizer=ADAM(0.1), nbatch=nsamples, niter=100)


text:
# Use baremodule to shave off a few KB from the serialized `.ji` file
baremodule HYPRE_jll
using Base
using Base: UUID
import JLLWrappers

JLLWrappers.@generate_main_file_header("HYPRE")
JLLWrappers.@generate_main_file("HYPRE", UUID("0a602bbd-b08b-5d75-8d32-0de6eef44785"))
end  # module HYPRE_jll


text:
using BandedMatrices, LinearAlgebra, ArrayLayouts, Test
import BandedMatrices: BandedColumns

@testset "Triangular" begin
    @testset "Upper" begin
        for (t, ud) in ((UpperTriangular, 'N'), (UnitUpperTriangular, 'U'))
            A = t(brand(10,10,1,2))
            @test isbanded(A)
            @test MemoryLayout(typeof(A)) == TriangularLayout{'U',ud,BandedColumns{DenseColumnMajor}}()
            @test bandwidths(A) == bandwidths(BandedMatrix(A)) == (0,2)
            @test bandwidth(A,2) == 2
            @test BandedMatrix(A) == A
            @test A[2,1] == 0
            if( ud == 'U' )
                @test A[2,2] == 1
            end
            x=rand(10)
            @test A*x ≈ BandedMatrix(A)*x ≈ Matrix(A)*x
            @test transpose(A)*x ≈ transpose(BandedMatrix(A))*x ≈ transpose(Matrix(A))*x
            @test all(A*x .=== lmul!(A, copy(x)) .=== lmul(A,x) .===
                        BandedMatrices.tbmv!('U', 'N', ud, 10, 2, parent(A).data, copy(x)))
            @test_throws DimensionMismatch BandedMatrices.tbmv('U', 'N', ud, 10, 2, parent(A).data, rand(9))

            @test A\x ≈ Matrix(A) \ x ≈ BandedMatrix(A) \ x
            @test all((A \ x) .=== ldiv!(A, copy(x)) .=== copyto!(similar(x), Ldiv(A, x)) .=== (similar(x) .= Ldiv(A, x)) .===
                        BandedMatrices.tbsv!('U', 'N', ud, 10, 2, parent(A).data, copy(x)))
            @test_throws DimensionMismatch BandedMatrices.tbsv('U', 'N', ud, 10, 2, parent(A).data, rand(9))
        end
    end
    @testset "Lower" begin
        for (t, ud) in ((LowerTriangular, 'N'), (UnitLowerTriangular, 'U'))
            A = t(brand(10,10,1,2))
            @test isbanded(A)
            @test MemoryLayout(typeof(A)) == TriangularLayout{'L', ud,BandedColumns{DenseColumnMajor}}()
            @test bandwidths(A) == bandwidths(BandedMatrix(A)) == (1,0)
            @test bandwidth(A,2) == 0
            @test BandedMatrix(A) == A
            @test A[1,2] == 0
            if( ud == 'U' )
                @test A[2,2] == 1
            end

            x=rand(10)
            @test A*x ≈ Matrix(A)*x
            @test transpose(A)*x ≈ transpose(BandedMatrix(A))*x ≈ transpose(Matrix(A))*x
            @test all(A*x .=== lmul!(A, copy(x)) .=== lmul(A,x) .===
                        BandedMatrices.tbmv!('L', 'N', ud, 10, 1, view(parent(A).data, 3:4,:), copy(x)))
            @test_throws DimensionMismatch BandedMatrices.tbmv('L', 'N', ud, 10, 1, view(parent(A).data, 3:4,:), rand(9))

            @test A\x ≈ Matrix(A) \ x ≈ BandedMatrix(A) \ x
            @test all((A \ x) .=== ldiv!(A, copy(x)) .=== copyto!(similar(x), Ldiv(A, x)) .=== (similar(x) .= Ldiv(A, x)) .===
                        BandedMatrices.tbsv!('L', 'N', ud, 10, 1, view(parent(A).data, 3:4,:), copy(x)))
            @test_throws DimensionMismatch BandedMatrices.tbsv('L', 'N', ud, 10, 1, view(parent(A).data, 3:4,:), rand(9))
        end
    end
end


text:
# # Free convection

# This script runs a simulation of convection driven by cooling at the
# surface of an idealized, stratified, rotating ocean surface boundary layer.

using LESbrary, Printf, Statistics, Oceananigans, Oceananigans.Units

# Domain

grid = RectilinearGrid(CPU(), size=(32, 32, 32), x=(0, 128), y=(0, 128), z=(-64, 0))

# Buoyancy and boundary conditions

Qᵇ = 1e-7
N² = 1e-5

buoyancy = SeawaterBuoyancy(equation_of_state=LinearEquationOfState(α=2e-4), constant_salinity=35.0)

α = buoyancy.equation_of_state.α
g = buoyancy.gravitational_acceleration

## Compute temperature flux and gradient from buoyancy flux and gradient
Qᵀ = Qᵇ / (α * g)
dTdz = N² / (α * g)

T_bcs = FieldBoundaryConditions(top = FluxBoundaryCondition(Qᵀ),
                                bottom = GradientBoundaryCondition(dTdz))

# LES Model

# Wall-aware AMD model constant which is 'enhanced' near the upper boundary.
# Necessary to obtain a smooth temperature distribution.
using LESbrary.NearSurfaceTurbulenceModels: SurfaceEnhancedModelConstant

Δz = grid.Δzᵃᵃᶜ
Cᴬᴹᴰ = SurfaceEnhancedModelConstant(Δz, C₀ = 1/12, enhancement = 7, decay_scale = 4Δz)

# Instantiate Oceananigans.NonhydrostaticModel

using Oceananigans
using Oceananigans.Advection: WENO5

model = NonhydrostaticModel(; grid, buoyancy,
                            timestepper = :RungeKutta3,
                            advection = WENO5(),
                            tracers = :T,
                            coriolis = FPlane(f=1e-4),
                            closure = AnisotropicMinimumDissipation(C=Cᴬᴹᴰ),
                            boundary_conditions = (; T=T_bcs))

# # Initial condition

Ξ(z) = rand() * exp(z / 8)
Tᵢ(x, y, z) = dTdz * z + 1e-6 * Ξ(z) * dTdz * grid.Lz
set!(model, T=Tᵢ)

# # Prepare the simulation

using LESbrary.Utils: SimulationProgressMessenger

# Adaptive time-stepping

simulation = Simulation(model, Δt=2.0, stop_time=8hour)

wizard = TimeStepWizard(cfl=1.5, max_change=1.1, max_Δt=30.0)
simulation.callbacks[:wizard] = Callback(wizard, IterationInterval(100))
simulation.callbacks[:progress] = Callback(SimulationProgressMessenger(wizard), IterationInterval(100))

# Prepare Output

using Oceananigans.Utils: GiB
using Oceananigans.OutputWriters

prefix = @sprintf("free_convection_Qb%.1e_Nsq%.1e_Nh%d_Nz%d", Qᵇ, N², grid.Nx, grid.Nz)

data_directory = joinpath(@__DIR__, "..", "data", prefix) # save data in /data/prefix

# Copy this file into the directory with data
mkpath(data_directory)
cp(@__FILE__, joinpath(data_directory, basename(@__FILE__)), force=true)

simulation.output_writers[:fields] = JLD2OutputWriter(model, merge(model.velocities, model.tracers);
                                                      schedule = TimeInterval(4hour),
                                                      prefix = prefix * "_fields",
                                                      dir = data_directory,
                                                      max_filesize = 2GiB,
                                                      force = true)

simulation.output_writers[:slices] = JLD2OutputWriter(model, merge(model.velocities, model.tracers),
                                                      schedule = AveragedTimeInterval(1hour, window=15minute),
                                                      prefix = prefix * "_slices",
                                                      field_slicer = FieldSlicer(j=floor(Int, grid.Ny/2)),
                                                      dir = data_directory,
                                                      max_filesize = 2GiB,
                                                      force = true)

# Horizontally-averaged turbulence statistics
turbulence_statistics = LESbrary.TurbulenceStatistics.first_through_second_order(model)
tke_budget_statistics = LESbrary.TurbulenceStatistics.turbulent_kinetic_energy_budget(model)

simulation.output_writers[:statistics] =
    JLD2OutputWriter(model, merge(turbulence_statistics, tke_budget_statistics),
                     schedule = AveragedTimeInterval(1hour, window=15minute),
                     prefix = prefix * "_statistics",
                     dir = data_directory,
                     force = true)

# # Run

LESbrary.Utils.print_banner(simulation)

run!(simulation)

# # Load and plot turbulence statistics

using JLD2, Plots

## Some plot parameters
linewidth = 3
ylim = (-64, 0)
plot_size = (1000, 500)
zC = znodes(Center, grid)
zF = znodes(Face, grid)

## Load data
file = jldopen(simulation.output_writers[:statistics].filepath)

iterations = parse.(Int, keys(file["timeseries/t"]))
iter = iterations[end] # plot final iteration

## Temperature
T = file["timeseries/T/$iter"][1, 1, :]

## Velocity variances
w² = file["timeseries/ww/$iter"][1, 1, :]
e  = file["timeseries/e/$iter"][1, 1, :]

## Terms in the TKE budget
buoyancy_flux =   file["timeseries/tke_buoyancy_flux/$iter"][1, 1, :]
  dissipation = - file["timeseries/tke_dissipation/$iter"][1, 1, :]

 pressure_flux = - file["timeseries/tke_pressure_flux/$iter"][1, 1, :]
advective_flux = - file["timeseries/tke_advective_flux/$iter"][1, 1, :]

transport = zeros(grid.Nz)
transport = (pressure_flux[2:end] .+ advective_flux[2:end]
             .- pressure_flux[1:end-1] .- advective_flux[1:end-1]) / Δz

## For mixing length calculation
wT = file["timeseries/wT/$iter"][1, 1, 2:end-1]

close(file)

## Post-process the data to determine the mixing length

## Mixing length, computed at cell interfaces and omitting boundaries
Tz = @. (T[2:end] - T[1:end-1]) / Δz
bz = @. α * g * Tz
eᶠ = @. (e[1:end-1] + e[2:end]) / 2

## Mixing length model: wT ∝ - ℓᵀ √e ∂z T ⟹  ℓᵀ = wT / (√e ∂z T)
ℓ_measured = @. - wT / (√(eᶠ) * Tz)
ℓ_estimated = @. min(-zF[2:end-1], sqrt(eᶠ / max(0, bz)))

# Plot data

temperature = plot(T, zC, size = plot_size,
                     linewidth = linewidth,
                        xlabel = "Temperature (ᵒC)",
                        ylabel = "z (m)",
                          ylim = ylim,
                         label = nothing)

variances = plot(e, zC, size = plot_size,
                     linewidth = linewidth,
                        xlabel = "Velocity variances (m² s⁻²)",
                        ylabel = "z (m)",
                          ylim = ylim,
                         label = "(u² + v² + w²) / 2")

plot!(variances, 1/2 .* w², zF, linewidth = linewidth,
                                    label = "w² / 2")

budget = plot([buoyancy_flux dissipation transport], zC, size = plot_size,
              linewidth = linewidth,
                 xlabel = "TKE budget terms",
                 ylabel = "z (m)",
                   ylim = ylim,
                  label = ["buoyancy flux" "dissipation" "kinetic energy transport"])

mixing_length = plot([ℓ_measured ℓ_estimated], zF[2:end-1], size = plot_size,
                                                       linewidth = linewidth,
                                                          xlabel = "Mixing length (m)",
                                                          ylabel = "z (m)",
                                                            xlim = (-5, 20),
                                                            ylim = ylim,
                                                           label = ["measured" "estimated"])

plot(temperature, variances, budget, mixing_length, layout=(1, 4))

max_stars_repo_name: ali-ramadhan/LESbrary

max_issues_repo_name: ali-ramadhan/LESbrary

max_forks_repo_name: ali-ramadhan/LESbrary


text:
# include("sindy.jl")

# Extending other function
function plot_sindy_model(m::DynamicalSystemModel, u0;
                        which_dim=1)
    recon = simulate_model(m, u0)
    names = m.variable_names
    plot(m.ts, recon[which_dim,:], label=names[which_dim]);
    title!("Sindy reconstruction (variable $which_dim)")
end

export plot_sindy_model


text:
using JSON
dhome    = ARGS[1]
trgthome = dhome * "demo/"
valfile  = dhome * "val.json"
dicfile  = dhome * "dic.json"
oclvr    = dhome * "CLEVR_v1.0/images/val/"
clvrhome = dhome * "demo/CLEVR_v1.0/"
imgshome = dhome * "demo/CLEVR_v1.0/images/"
valimgs  = dhome * "demo/CLEVR_v1.0/images/val/"

function loadFeatures(dhome,set)
    feats    = reinterpret(Float32,read(open(dhome*set*".bin")))
    reshape(feats,(14,14,1024,div(length(feats),200704)))
end

function getdemo(file;part=100)
    println("Validation feats are loading...")
    valfeats  = loadFeatures(dhome,"val")
    demofeats = Any[];
    println("Validation data are loading...")
    data      = JSON.parsefile(file)
    demo      = data[randperm(length(data))[1:part]]

    println("Random selection are collecting...")
    for d in demo
        imgname = d[1]
    	cp(oclvr*imgname ,valimgs*imgname;remove_destination=true)
        id = parse(Int,imgname[end-9:end-4])+1
        push!(demofeats,valfeats[:,:,:,id])
    end
    demofeats = cat(4,demofeats...)

    println("Demo feats are writed...")
    f = open(trgthome*"demo.bin","w")
    write(f,demofeats); close(f)

    println("Demo data is writed...")
    open(trgthome * "demo.json","w") do f
        write(f,json(demo))
    end
end

println("Creating demo folders if necessary...")
!isdir(trgthome) && mkdir(trgthome)
!isdir(clvrhome) && mkdir(clvrhome)
!isdir(imgshome) && mkdir(imgshome)
if isdir(valimgs)
   rm(valimgs;recursive=true)
end
mkdir(valimgs)

getdemo(valfile)
cp(dicfile,trgthome  * "dic.json";remove_destination=true)
run(`tar -cvzf demo.tar.gz $(trgthome)`)

max_stars_repo_name: feyzaakyurek/MAC-Network

max_issues_repo_name: feyzaakyurek/MAC-Network

max_forks_repo_name: feyzaakyurek/MAC-Network


text:
module FstFiles

using fstformat, TableTraits, IteratorInterfaceExtensions
import FileIO
import IterableTables

struct FstFile
    filename::String
    keywords
end

function load(f::FileIO.File{FileIO.format"Fst"}; keywords...)
    return FstFile(f.filename, keywords)
end

TableTraits.isiterable(x::FstFile) = true
TableTraits.isiterabletable(x::FstFile) = true

function IteratorInterfaceExtensions.getiterator(file::FstFile)
    df = fstformat.read(file.filename; file.keywords...)

    it = getiterator(df)

    return it
end

function save(f::FileIO.File{FileIO.format"Fst"}, data)
    isiterabletable(data) || error("Can't write this data to a fst file.")

    it = getiterator(data)

    fstformat.write(it, f.filename)
end

end # module


text:
function get_galileo_orbital_elements(obj::GalileoPropagator)
    return jcall(obj, "getGalileoOrbitalElements", GalileoOrbitalElements, ())
end


text:
@testset "offsets" begin
    @test @inferred(ArrayInterface.known_offsets(A)) === (1, 1, 1)
    @test @inferred(ArrayInterface.known_offsets(Ap)) === (1, 1)
    @test @inferred(ArrayInterface.known_offsets(Ar)) === (1, 1, 1)
    @test @inferred(ArrayInterface.known_offsets(Ar, static(1))) === 1
    @test @inferred(ArrayInterface.known_offsets(Ar, static(4))) === 1
    @test @inferred(ArrayInterface.known_offsets(A2)) === (1, 1, 1)
    @test @inferred(ArrayInterface.known_offsets(A2r)) === (1, 1, 1)
    @test @inferred(ArrayInterface.offsets(A)) === (StaticInt(1), StaticInt(1), StaticInt(1))
    @test @inferred(ArrayInterface.offsets(Ap)) === (StaticInt(1), StaticInt(1))
    @test @inferred(ArrayInterface.offsets(Ar)) === (StaticInt(1), StaticInt(1), StaticInt(1))
    @test @inferred(ArrayInterface.offsets(A2)) === (StaticInt(1), StaticInt(1), StaticInt(1))
    @test @inferred(ArrayInterface.offsets(A2r)) === (StaticInt(1), StaticInt(1), StaticInt(1))

    @test @inferred(ArrayInterface.offsets(S)) === (StaticInt(1), StaticInt(1), StaticInt(1))
    @test @inferred(ArrayInterface.offsets(Sp)) === (StaticInt(1), StaticInt(1), StaticInt(1))
    @test @inferred(ArrayInterface.offsets(Sp2)) === (StaticInt(1), StaticInt(1), StaticInt(1))

    @test @inferred(ArrayInterface.known_offsets(S)) === (1, 1, 1)
    @test @inferred(ArrayInterface.known_offsets(Sp)) === (1, 1, 1)
    @test @inferred(ArrayInterface.known_offsets(Sp2)) === (1, 1, 1)

    @test @inferred(ArrayInterface.known_offsets(R)) === (1,)
    @test @inferred(ArrayInterface.known_offsets(Rnr)) === (1,)
    @test @inferred(ArrayInterface.known_offsets(1:10)) === (1,)
end

@testset "strides" begin
    @test @inferred(ArrayInterface.strides(A)) === (StaticInt(1), 3, 12)
    @test @inferred(ArrayInterface.strides(Ap)) === (StaticInt(1), 12)
    @test @inferred(ArrayInterface.strides(A)) == strides(A)
    @test @inferred(ArrayInterface.strides(Ap)) == strides(Ap)
    @test @inferred(ArrayInterface.strides(Ar)) === (StaticInt{1}(), 6, 24)
    @test @inferred(ArrayInterface.strides(A2)) === (StaticInt(1), 4, 12)
    @test @inferred(ArrayInterface.strides(A2r)) === (StaticInt(1), 2, 6)

    @test @inferred(ArrayInterface.strides(S)) === (StaticInt(1), StaticInt(2), StaticInt(6))
    @test @inferred(ArrayInterface.strides(Sp)) === (StaticInt(6), StaticInt(1), StaticInt(2))
    @test @inferred(ArrayInterface.strides(Sp2)) === (StaticInt(6), StaticInt(2), StaticInt(1))
    @test @inferred(ArrayInterface.strides(view(Sp2, :, 1, 1)')) === (StaticInt(6), StaticInt(6))

    @test @inferred(ArrayInterface.stride(Sp2, StaticInt(1))) === StaticInt(6)
    @test @inferred(ArrayInterface.stride(Sp2, StaticInt(2))) === StaticInt(2)
    @test @inferred(ArrayInterface.stride(Sp2, StaticInt(3))) === StaticInt(1)
    @test @inferred(ArrayInterface.strides(Mp)) === (StaticInt(2), StaticInt(6))
    @test @inferred(ArrayInterface.strides(Mp2)) === (StaticInt(1), StaticInt(6))
    @test @inferred(ArrayInterface.strides(Mp)) == strides(Mp)
    @test @inferred(ArrayInterface.strides(Mp2)) == strides(Mp2)

    @test_throws MethodError ArrayInterface.strides(DummyZeros(3,4))

    @test @inferred(ArrayInterface.known_strides(A)) === (1, nothing, nothing)
    @test @inferred(ArrayInterface.known_strides(Ap)) === (1, nothing)
    @test @inferred(ArrayInterface.known_strides(Ar)) === (1, nothing, nothing)
    @test @inferred(ArrayInterface.known_strides(reshape(view(zeros(100), 1:60), (3,4,5)))) === (1, nothing, nothing)
    @test @inferred(ArrayInterface.known_strides(A2)) === (1, nothing, nothing)
    @test @inferred(ArrayInterface.known_strides(A2r)) === (1, nothing, nothing)

    @test @inferred(ArrayInterface.known_strides(S)) === (1, 2, 6)
    @test @inferred(ArrayInterface.known_strides(Sp)) === (6, 1, 2)
    @test @inferred(ArrayInterface.known_strides(Sp2)) === (6, 2, 1)
    @test @inferred(ArrayInterface.known_strides(Sp2, StaticInt(1))) === 6
    @test @inferred(ArrayInterface.known_strides(Sp2, StaticInt(2))) === 2
    @test @inferred(ArrayInterface.known_strides(Sp2, StaticInt(3))) === 1
    @test @inferred(ArrayInterface.known_strides(Sp2, StaticInt(4))) === ArrayInterface.known_length(Sp2)
    @test @inferred(ArrayInterface.known_strides(view(Sp2, :, 1, 1)')) === (6, 6)
end

@testset "Static-Dynamic Size, Strides, and Offsets" begin
    colors = [(R = rand(), G = rand(), B = rand()) for i ∈ 1:100];

    colormat = reinterpret(reshape, Float64, colors)
    @test @inferred(ArrayInterface.strides(colormat)) === (StaticInt(1), StaticInt(3))
    @test @inferred(ArrayInterface.dense_dims(colormat)) === (True(),True())
    @test @inferred(ArrayInterface.dense_dims(view(colormat,:,4))) === (True(),)
    @test @inferred(ArrayInterface.dense_dims(view(colormat,:,4:7))) === (True(),True())
    @test @inferred(ArrayInterface.dense_dims(view(colormat,2:3,:))) === (True(),False())

    Rr = reinterpret(reshape, Int32, R)
    @test @inferred(ArrayInterface.size(Rr)) === (StaticInt(2),StaticInt(2))
    @test @inferred(ArrayInterface.known_size(Rr)) === (2, 2)

    Sr = Wrapper(reinterpret(reshape, Complex{Int64}, S))
    @test @inferred(ArrayInterface.size(Sr)) == (static(3), static(4))
    @test @inferred(ArrayInterface.known_size(Sr)) === (3, 4)
    @test @inferred(ArrayInterface.strides(Sr)) === (static(1), static(3))
    Sr2 = reinterpret(reshape, Complex{Int64}, S);
    @test @inferred(ArrayInterface.dense_dims(Sr2)) === (True(),True())
    @test @inferred(ArrayInterface.dense_dims(view(Sr2,:,2))) === (True(),)
    @test @inferred(ArrayInterface.dense_dims(view(Sr2,:,2:3))) === (True(),True())
    @test @inferred(ArrayInterface.dense_dims(view(Sr2,2:3,:))) === (True(),False())

    Ar2c = reinterpret(reshape, Complex{Float64}, view(rand(2, 5, 7), :, 2:4, 3:5));
    @test @inferred(ArrayInterface.strides(Ar2c)) === (StaticInt(1), 5)
    Ar2c′ = reinterpret(reshape, Complex{Float64}, view(rand(4, 5, 7), Base.oneto(2), 2:4, 3:5));
    @test @inferred(ArrayInterface.strides(Ar2c′)) === (2, 10)
    Ar2c″ = reinterpret(reshape, Complex{Float64}, view(rand(3, 5, 7), Base.oneto(2), 2:4, 3:5));
    @test_throws ArgumentError ArrayInterface.strides(Ar2c″)
    Ar2c_static = reinterpret(reshape, Complex{Float64}, view(MArray(rand(2, 5, 7)), :, 2:4, 3:5));
    @test @inferred(ArrayInterface.strides(Ar2c_static)) === (StaticInt(1), StaticInt(5))

    Ac2r = reinterpret(reshape, Float64, view(rand(ComplexF64, 5, 7), 2:4, 3:6));
    @test @inferred(ArrayInterface.strides(Ac2r)) === (StaticInt(1), StaticInt(2), 10)
    Ac2r_static = reinterpret(reshape, Float64, view(MArray(rand(ComplexF64, 5, 7)), 2:4, 3:6));
    @test @inferred(ArrayInterface.strides(Ac2r_static)) === (StaticInt(1), StaticInt(2), StaticInt(10))

    Ac2t = reinterpret(reshape, Tuple{Float64,Float64}, view(rand(ComplexF64, 5, 7), 2:4, 3:6));
    @test @inferred(ArrayInterface.strides(Ac2t)) === (StaticInt(1), 5)
    Ac2t_static = reinterpret(reshape, Tuple{Float64,Float64}, view(MArray(rand(ComplexF64, 5, 7)), 2:4, 3:6));
    @test @inferred(ArrayInterface.strides(Ac2t_static)) === (StaticInt(1), StaticInt(5))

    a = rand(Float32, 100, 2);
    b = reinterpret(Float64, view(a,:,1));
    @test @inferred(ArrayInterface.contiguous_axis(a)) === StaticInt(1)
    @test @inferred(ArrayInterface.stride_rank(b)) === (StaticInt(1),)
end

@testset "Memory Layout" begin
    x = zeros(100);
    @test ArrayInterface.size(Base.Broadcast.Broadcasted(+, (x, x'))) === (100,100)
    # R = reshape(view(x, 1:100), (10,10));
    # A = zeros(3,4,5);
    A = Wrapper(reshape(view(x, 1:60), (3,4,5)));
    B = A .== 0;
    D1 = view(A, 1:2:3, :, :);  # first dimension is discontiguous
    D2 = view(A, :, 2:2:4, :);  # first dimension is contiguous

    @test @inferred(ArrayInterface.defines_strides(x))
    @test @inferred(ArrayInterface.defines_strides(A))
    @test @inferred(ArrayInterface.defines_strides(D1))
    @test !@inferred(ArrayInterface.defines_strides(view(A, :, [1,2],1)))
    @test @inferred(ArrayInterface.defines_strides(DenseWrapper{Int,2,Matrix{Int}}))

    @test @inferred(ArrayInterface.device(A)) === ArrayInterface.CPUPointer()
    @test @inferred(ArrayInterface.device(B)) === ArrayInterface.CPUIndex()
    @test @inferred(ArrayInterface.device(-1:19)) === ArrayInterface.CPUIndex()
    @test @inferred(ArrayInterface.device((1,2,3))) === ArrayInterface.CPUTuple()
    @test @inferred(ArrayInterface.device(PermutedDimsArray(A,(3,1,2)))) === ArrayInterface.CPUPointer()
    @test @inferred(ArrayInterface.device(view(A, 1, :, 2:4))) === ArrayInterface.CPUPointer()
    @test @inferred(ArrayInterface.device(view(A, 1, :, 2:4)')) === ArrayInterface.CPUPointer()
    @test isnothing(ArrayInterface.device("Hello, world!"))
    @test @inferred(ArrayInterface.device(DenseWrapper{Int,2,Matrix{Int}})) === ArrayInterface.CPUPointer()
    #=
    @btime ArrayInterface.contiguous_axis($(reshape(view(zeros(100), 1:60), (3,4,5))))
      0.047 ns (0 allocations: 0 bytes)
    =#
    @test @inferred(ArrayInterface.contiguous_axis(A)) === ArrayInterface.StaticInt(1)
    @test @inferred(ArrayInterface.contiguous_axis(B)) === ArrayInterface.StaticInt(1)
    @test @inferred(ArrayInterface.contiguous_axis(-1:19)) === ArrayInterface.StaticInt(1)
    @test @inferred(ArrayInterface.contiguous_axis(D1)) === ArrayInterface.StaticInt(-1)
    @test @inferred(ArrayInterface.contiguous_axis(D2)) === ArrayInterface.StaticInt(1)
    @test @inferred(ArrayInterface.contiguous_axis(PermutedDimsArray(A,(3,1,2)))) === ArrayInterface.StaticInt(2)
    @test @inferred(ArrayInterface.contiguous_axis(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:]))) === ArrayInterface.StaticInt(1)
    @test @inferred(ArrayInterface.contiguous_axis(transpose(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:])))) === ArrayInterface.StaticInt(2)
    @test @inferred(ArrayInterface.contiguous_axis(@view(PermutedDimsArray(A,(3,1,2))[2:3,1:2,:]))) === ArrayInterface.StaticInt(2)
    @test @inferred(ArrayInterface.contiguous_axis(@view(PermutedDimsArray(A,(3,1,2))[2:3,2,:]))) === ArrayInterface.StaticInt(-1)
    @test @inferred(ArrayInterface.contiguous_axis(PermutedDimsArray(@view(A[2,:,:]),(2,1)))) === ArrayInterface.StaticInt(-1)
    @test @inferred(ArrayInterface.contiguous_axis(@view(PermutedDimsArray(A,(3,1,2))[2:3,2,:])')) === ArrayInterface.StaticInt(-1)
    @test @inferred(ArrayInterface.contiguous_axis(@view(PermutedDimsArray(A,(3,1,2))[:,1:2,1])')) === ArrayInterface.StaticInt(1)
    @test @inferred(ArrayInterface.contiguous_axis((3,4))) === StaticInt(1)
    @test @inferred(ArrayInterface.contiguous_axis(rand(4)')) === StaticInt(2)
    @test @inferred(ArrayInterface.contiguous_axis(view(@view(PermutedDimsArray(A,(3,1,2))[2:3,2,:])', :, 1)')) === StaticInt(-1)
    @test @inferred(ArrayInterface.contiguous_axis(reshape(DummyZeros(3,4), (4, 3)))) === nothing
    @test @inferred(ArrayInterface.contiguous_axis(DummyZeros(3,4))) === nothing
    @test @inferred(ArrayInterface.contiguous_axis(PermutedDimsArray(DummyZeros(3,4), (2, 1)))) === nothing
    @test @inferred(ArrayInterface.contiguous_axis(view(DummyZeros(3,4), 1, :))) === nothing
    @test @inferred(ArrayInterface.contiguous_axis(view(DummyZeros(3,4), 1, :)')) === nothing

    @test @inferred(ArrayInterface.contiguous_axis_indicator(A)) == (true,false,false)
    @test @inferred(ArrayInterface.contiguous_axis_indicator(B)) == (true,false,false)
    @test @inferred(ArrayInterface.contiguous_axis_indicator(-1:10)) == (true,)
    @test @inferred(ArrayInterface.contiguous_axis_indicator(PermutedDimsArray(A,(3,1,2)))) == (false,true,false)
    @test @inferred(ArrayInterface.contiguous_axis_indicator(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:]))) == (true,false)
    @test @inferred(ArrayInterface.contiguous_axis_indicator(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:])')) == (false,true)
    @test @inferred(ArrayInterface.contiguous_axis_indicator(@view(PermutedDimsArray(A,(3,1,2))[2:3,1:2,:]))) == (false,true,false)
    @test @inferred(ArrayInterface.contiguous_axis_indicator(@view(PermutedDimsArray(A,(3,1,2))[2:3,2,:]))) == (false,false)
    @test @inferred(ArrayInterface.contiguous_axis_indicator(@view(PermutedDimsArray(A,(3,1,2))[2:3,2,:])')) == (false,false)
    @test @inferred(ArrayInterface.contiguous_axis_indicator(@view(PermutedDimsArray(A,(3,1,2))[:,1:2,1])')) == (true,false)
    @test @inferred(ArrayInterface.contiguous_axis_indicator(@view(PermutedDimsArray(A,(3,1,2))[:,1:2,[1,3,4]]))) == (false,true,false)
    @test @inferred(ArrayInterface.contiguous_axis_indicator(DummyZeros(3,4))) === nothing

    @test @inferred(ArrayInterface.contiguous_batch_size(A)) === ArrayInterface.StaticInt(0)
    @test @inferred(ArrayInterface.contiguous_batch_size(B)) === ArrayInterface.StaticInt(0)
    @test @inferred(ArrayInterface.contiguous_batch_size(-1:18)) === ArrayInterface.StaticInt(0)
    @test @inferred(ArrayInterface.contiguous_batch_size(PermutedDimsArray(A,(3,1,2)))) === ArrayInterface.StaticInt(0)
    @test @inferred(ArrayInterface.contiguous_batch_size(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:]))) === ArrayInterface.StaticInt(0)
    @test @inferred(ArrayInterface.contiguous_batch_size(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:])')) === ArrayInterface.StaticInt(0)
    @test @inferred(ArrayInterface.contiguous_batch_size(@view(PermutedDimsArray(A,(3,1,2))[2:3,1:2,:]))) === ArrayInterface.StaticInt(0)
    @test @inferred(ArrayInterface.contiguous_batch_size(@view(PermutedDimsArray(A,(3,1,2))[2:3,2,:]))) === ArrayInterface.StaticInt(-1)
    @test @inferred(ArrayInterface.contiguous_batch_size(@view(PermutedDimsArray(A,(3,1,2))[2:3,2,:])')) === ArrayInterface.StaticInt(-1)
    @test @inferred(ArrayInterface.contiguous_batch_size(@view(PermutedDimsArray(A,(3,1,2))[:,1:2,1])')) === ArrayInterface.StaticInt(0)
    let u_base = randn(10, 10)
        u_view = view(u_base, 3, :)
        u_reshaped_view = reshape(u_view, 1, size(u_base, 2))
        @test @inferred(ArrayInterface.contiguous_batch_size(u_view)) === ArrayInterface.StaticInt(-1)
        @test @inferred(ArrayInterface.contiguous_batch_size(u_reshaped_view)) === ArrayInterface.StaticInt(-1)
    end

    @test @inferred(ArrayInterface.stride_rank(A)) == (1,2,3)
    @test @inferred(ArrayInterface.stride_rank(B)) == (1,2,3)
    @test @inferred(ArrayInterface.stride_rank(-4:4)) == (1,)
    @test @inferred(ArrayInterface.stride_rank(view(A,:,:,1))) === (static(1), static(2))
    @test @inferred(ArrayInterface.stride_rank(view(A,:,:,1))) === ((ArrayInterface.StaticInt(1),ArrayInterface.StaticInt(2)))
    @test @inferred(ArrayInterface.stride_rank(PermutedDimsArray(A,(3,1,2)))) == (3, 1, 2)
    @test @inferred(ArrayInterface.stride_rank(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:]))) == (1, 2)
    @test @inferred(ArrayInterface.stride_rank(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:])')) == (2, 1)
    @test @inferred(ArrayInterface.stride_rank(@view(PermutedDimsArray(A,(3,1,2))[2:3,1:2,:]))) == (3, 1, 2)
    @test @inferred(ArrayInterface.stride_rank(@view(PermutedDimsArray(A,(3,1,2))[2:3,2,:]))) == (3, 2)
    @test @inferred(ArrayInterface.stride_rank(@view(PermutedDimsArray(A,(3,1,2))[2:3,2,:])')) == (2, 3)
    @test @inferred(ArrayInterface.stride_rank(@view(PermutedDimsArray(A,(3,1,2))[:,1:2,1])')) == (1, 3)
    @test @inferred(ArrayInterface.stride_rank(@view(PermutedDimsArray(A,(3,1,2))[:,2,1])')) == (2, 1)
    @test @inferred(ArrayInterface.stride_rank(@view(PermutedDimsArray(A,(3,1,2))[:,1:2,[1,3,4]]))) == (3, 1, 2)
    @test @inferred(ArrayInterface.stride_rank(DummyZeros(3,4)')) === nothing
    @test @inferred(ArrayInterface.stride_rank(PermutedDimsArray(DummyZeros(3,4), (2, 1)))) === nothing
    @test @inferred(ArrayInterface.stride_rank(view(DummyZeros(3,4), 1, :))) === nothing
    uA = reinterpret(reshape, UInt64, A)
    @test @inferred(ArrayInterface.stride_rank(uA)) === ArrayInterface.stride_rank(A)
    rA = reinterpret(reshape, NTuple{3,Float64}, A)
    @test @inferred(ArrayInterface.stride_rank(rA)) === (static(1), static(2))
    cA = copy(rA)
    rcA = reinterpret(reshape, Float64, cA)
    @test @inferred(ArrayInterface.stride_rank(rcA)) === ArrayInterface.stride_rank(A)

    #=
    @btime ArrayInterface.is_column_major($(PermutedDimsArray(A,(3,1,2))))
      0.047 ns (0 allocations: 0 bytes)
    @btime ArrayInterface.is_column_major($(ArrayInterface.is_column_major(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:]))))
      0.047 ns (0 allocations: 0 bytes)
    @btime ArrayInterface.is_column_major($(ArrayInterface.is_column_major(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:])')))
      0.047 ns (0 allocations: 0 bytes)

      PermutedDimsArray(A,(3,1,2))[2:3,1:2,:])
      @view(PermutedDimsArray(reshape(view(zeros(100), 1:60), (3,4,5)), (3,1,2)), 2:3, 1:2, :)
    =#

    @test @inferred(ArrayInterface.is_column_major(A)) === True()
    @test @inferred(ArrayInterface.is_column_major(B)) === True()
    @test @inferred(ArrayInterface.is_column_major(-4:7)) === False()
    @test @inferred(ArrayInterface.is_column_major(PermutedDimsArray(A,(3,1,2)))) === False()
    @test @inferred(ArrayInterface.is_column_major(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:]))) === True()
    @test @inferred(ArrayInterface.is_column_major(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:])')) === False()
    @test @inferred(ArrayInterface.is_column_major(@view(PermutedDimsArray(A,(3,1,2))[2:3,1:2,:]))) === False()
    @test @inferred(ArrayInterface.is_column_major(@view(PermutedDimsArray(A,(3,1,2))[2:3,2,:]))) === False()
    @test @inferred(ArrayInterface.is_column_major(@view(PermutedDimsArray(A,(3,1,2))[2:3,2,:])')) === True()
    @test @inferred(ArrayInterface.is_column_major(@view(PermutedDimsArray(A,(3,1,2))[:,1:2,1])')) === True()
    @test @inferred(ArrayInterface.is_column_major(@view(PermutedDimsArray(A,(3,1,2))[:,2,1])')) === False()
    @test @inferred(ArrayInterface.is_column_major(2.3)) === False()

    @test @inferred(ArrayInterface.dense_dims(A)) == (true,true,true)
    @test @inferred(ArrayInterface.dense_dims(B)) == (true,true,true)
    @test @inferred(ArrayInterface.dense_dims(-3:9)) == (true,)
    @test @inferred(ArrayInterface.dense_dims(PermutedDimsArray(A,(3,1,2)))) == (true,true,true)
    @test @inferred(ArrayInterface.dense_dims(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:]))) == (true,false)
    @test @inferred(ArrayInterface.dense_dims(@view(PermutedDimsArray(A,(3,1,2))[2,1:2,:])')) == (false,true)
    @test @inferred(ArrayInterface.dense_dims(@view(PermutedDimsArray(A,(3,1,2))[2:3,1:2,:]))) == (false,true,false)
    @test @inferred(ArrayInterface.dense_dims(@view(PermutedDimsArray(A,(3,1,2))[2:3,:,1:2]))) == (false,true,true)
    @test @inferred(ArrayInterface.dense_dims(@view(PermutedDimsArray(A,(3,1,2))[2:3,2,:]))) == (false,false)
    @test @inferred(ArrayInterface.dense_dims(@view(PermutedDimsArray(A,(3,1,2))[2:3,2,:])')) == (false,false)
    @test @inferred(ArrayInterface.dense_dims(@view(PermutedDimsArray(A,(3,1,2))[:,1:2,1])')) == (true,false)
    @test @inferred(ArrayInterface.dense_dims(@view(PermutedDimsArray(A,(3,1,2))[2:3,:,[1,2]]))) == (false,true,false)
    @test @inferred(ArrayInterface.dense_dims(@view(PermutedDimsArray(A,(3,1,2))[2:3,[1,2,3],:]))) == (false,false,false)
    @test @inferred(ArrayInterface.dense_dims(reshape(view(randn(10, 10, 10), 3, :, :), 1, 100))) == (false, false)
    # TODO Currently Wrapper can't function the same as Array because Array can change
    # the dimensions on reshape. We should be rewrapping the result in `Wrapper` but we
    # first need to develop a standard method for reconstructing arrays
    @test @inferred(ArrayInterface.dense_dims(vec(parent(A)))) == (true,)
    @test @inferred(ArrayInterface.dense_dims(vec(parent(A))')) == (true,true)
    @test @inferred(ArrayInterface.dense_dims(DummyZeros(3,4))) === nothing
    @test @inferred(ArrayInterface.dense_dims(DummyZeros(3,4)')) === nothing
    @test @inferred(ArrayInterface.dense_dims(PermutedDimsArray(DummyZeros(3,4), (2, 1)))) === nothing
    @test @inferred(ArrayInterface.dense_dims(view(DummyZeros(3,4), :, 1))) === nothing
    @test @inferred(ArrayInterface.dense_dims(view(DummyZeros(3,4), :, 1)')) === nothing
    @test @inferred(ArrayInterface.is_dense(A)) === @inferred(ArrayInterface.is_dense(A)) === @inferred(ArrayInterface.is_dense(PermutedDimsArray(A,(3,1,2)))) === @inferred(ArrayInterface.is_dense(Array{Float64,0}(undef))) === True()
    @test @inferred(ArrayInterface.is_dense(@view(PermutedDimsArray(A,(3,1,2))[2:3,1:2,:]))) === @inferred(ArrayInterface.is_dense(@view(PermutedDimsArray(A,(3,1,2))[2:3,:,[1,2]]))) === @inferred(ArrayInterface.is_dense(@view(PermutedDimsArray(A,(3,1,2))[2:3,[1,2,3],:]))) === False()

    C = Array{Int8}(undef, 2,2,2,2);
    doubleperm = PermutedDimsArray(PermutedDimsArray(C,(4,2,3,1)), (4,2,1,3));
    @test collect(strides(C))[collect(ArrayInterface.stride_rank(doubleperm))] == collect(strides(doubleperm))

    C1 = reinterpret(reshape, Float64, PermutedDimsArray(Array{Complex{Float64}}(undef, 3,4,5), (2,1,3)));
    C2 = reinterpret(reshape, Complex{Float64}, PermutedDimsArray(view(A,1:2,:,:), (1,3,2)));
    C3 = reinterpret(reshape, Complex{Float64}, PermutedDimsArray(Wrapper(reshape(view(x, 1:24), (2,3,4))), (1,3,2)));

    @test @inferred(ArrayInterface.defines_strides(C1))
    @test @inferred(ArrayInterface.defines_strides(C2))
    @test @inferred(ArrayInterface.defines_strides(C3))

    @test @inferred(ArrayInterface.device(C1)) === ArrayInterface.CPUPointer()
    @test @inferred(ArrayInterface.device(C2)) === ArrayInterface.CPUPointer()
    @test @inferred(ArrayInterface.device(C3)) === ArrayInterface.CPUPointer()

    @test @inferred(ArrayInterface.contiguous_batch_size(C1)) === ArrayInterface.StaticInt(0)
    @test @inferred(ArrayInterface.contiguous_batch_size(C2)) === ArrayInterface.StaticInt(0)
    @test @inferred(ArrayInterface.contiguous_batch_size(C3)) === ArrayInterface.StaticInt(0)

    @test @inferred(ArrayInterface.stride_rank(C1)) == (1,3,2,4)
    @test @inferred(ArrayInterface.stride_rank(C2)) == (2,1)
    @test @inferred(ArrayInterface.stride_rank(C3)) == (2,1)

    @test @inferred(ArrayInterface.contiguous_axis(C1)) === StaticInt(1)
    @test @inferred(ArrayInterface.contiguous_axis(C2)) === StaticInt(0)
    @test @inferred(ArrayInterface.contiguous_axis(C3)) === StaticInt(2)

    @test @inferred(ArrayInterface.contiguous_axis_indicator(C1)) == (true,false,false,false)
    @test @inferred(ArrayInterface.contiguous_axis_indicator(C2)) == (false,false)
    @test @inferred(ArrayInterface.contiguous_axis_indicator(C3)) == (false,true)

    @test @inferred(ArrayInterface.is_column_major(C1)) === False()
    @test @inferred(ArrayInterface.is_column_major(C2)) === False()
    @test @inferred(ArrayInterface.is_column_major(C3)) === False()

    @test @inferred(ArrayInterface.dense_dims(C1)) == (true,true,true,true)
    @test @inferred(ArrayInterface.dense_dims(C2)) == (false,false)
    @test @inferred(ArrayInterface.dense_dims(C3)) == (true,true)
end

@testset "Reinterpreted reshaped views" begin
    u_base = randn(1, 4, 4, 5);
    u_reinterpreted = reinterpret(Int8, view(u_base, 1:1, 1:4, 1:4, 1:5))
    u_vectors = reshape(reinterpret(NTuple{1, eltype(u_base)}, u_base),
                        Base.tail(size(u_base))...)
    u_view = view(u_vectors, 2, :, 3)
    u_view_reinterpreted = reinterpret(eltype(u_base), u_view)
    u_view_reshaped = reshape(u_view_reinterpreted, 1, length(u_view))
    # See https://github.com/JuliaArrays/ArrayInterface.jl/issues/163
    @test @inferred(ArrayInterface.strides(u_base)) === (StaticInt(1), 1, 4, 16)
    @test @inferred(ArrayInterface.strides(u_reinterpreted)) === (StaticInt(1), 8, 32, 128)
    @test @inferred(ArrayInterface.strides(u_vectors)) === (StaticInt(1), 4, 16)
    @test @inferred(ArrayInterface.strides(u_view)) === (4,)
    @test @inferred(ArrayInterface.strides(u_view_reinterpreted)) === (4,)
    @test @inferred(ArrayInterface.strides(u_view_reshaped)) === (4, 4)
    @test @inferred(ArrayInterface.axes(u_vectors)) isa ArrayInterface.axes_types(u_vectors)
end

@testset "Reshaped strides" begin
    a = randn(10, 10)
    @test @inferred(ArrayInterface.strides(reshape(view(a, :, 1:2:9), 2, 5, 5))) === (1, 2, 20)
    @test @inferred(ArrayInterface.strides(vec(view(a, 1, 1)))) === (static(1),)
    @test_throws ArgumentError ArrayInterface.strides(reshape(view(a, :, 1:2:9), 5, 5, 2))
    a = MArray(randn(10, 10))
    @test @inferred(ArrayInterface.strides(reshape(view(a, 1:9, 1:10), 3, 3, 2, 5))) === (1, 3, 10, 20)
    @test @inferred(ArrayInterface.strides(reshape(view(a, static(1):static(9), 1:10), 3, 3, 2, 5))) === (static(1), 3, 10, 20)
    @test @inferred(ArrayInterface.strides(reshape(view(a, :, 1:2:9), 2, 5, 5))) === (static(1), 2, 20)
    a = randn(2, 5, 3)
    @test @inferred(ArrayInterface.strides(vec(view(a, :, 1:5, 1:3)))) === (1,)
    @test @inferred(ArrayInterface.strides(reshape(view(a, :, 1:5, 1:3), 5, 2, 3))) === (1,5,10)
    @test @inferred(ArrayInterface.strides(vec(view(a, static(1):static(2), 1:5, 1:3)))) === (static(1),)
    @test @inferred(ArrayInterface.strides(reshape(view(a, static(1):static(2), 1:5, 1:3), 5, 2, 3))) === (static(1),5,10)
    a = MArray(randn(3, 5, 3))
    @test @inferred(ArrayInterface.strides(vec(view(a, :, 1:5, 1:3)))) === (static(1),)
    @test @inferred(ArrayInterface.strides(reshape(view(a, static(1):static(3), static(1):static(4), 1:3), 2, 2, 3, 3))) === (static(1), 2, 4, 15)
    @test @inferred(ArrayInterface.strides(reshape(view(a, static(1):static(2), static(1):static(4), 1:3), 2, 2, 2, 3))) === (static(1), 3, 6, 15)
    @test @inferred(ArrayInterface.strides(reshape(view(a, static(1):static(2), static(1):static(1), 1:3), 2, 1, 3))) === (static(1), 2, 15)
end

@testset "Reshaped views" begin
    u_base = randn(10, 10)
    u_view = view(u_base, 3, :)
    u_reshaped_view1 = reshape(u_view, 1, :)
    u_reshaped_view2 = reshape(u_view, 2, :)

    @test @inferred(ArrayInterface.defines_strides(u_base))
    @test @inferred(ArrayInterface.defines_strides(u_view))
    @test @inferred(ArrayInterface.defines_strides(u_reshaped_view1))
    @test @inferred(ArrayInterface.defines_strides(u_reshaped_view2))

    # See https://github.com/JuliaArrays/ArrayInterface.jl/issues/160
    @test @inferred(ArrayInterface.strides(u_base)) == (StaticInt(1), 10)
    @test @inferred(ArrayInterface.strides(u_view)) == (10,)
    @test @inferred(ArrayInterface.strides(u_reshaped_view1)) == (10, 10)
    @test @inferred(ArrayInterface.strides(u_reshaped_view2)) == (10, 20)

    # See https://github.com/JuliaArrays/ArrayInterface.jl/issues/157
    @test @inferred(ArrayInterface.dense_dims(u_base)) == (True(), True())
    @test @inferred(ArrayInterface.dense_dims(u_view)) == (False(),)
    @test @inferred(ArrayInterface.dense_dims(u_reshaped_view1)) == (False(), False())
    @test @inferred(ArrayInterface.dense_dims(u_reshaped_view2)) == (False(), False())
end


text:
module MdHtml

using Genie
using Markdown
using YAML
import Genie.Renderer: vars

const MD_SEPARATOR_START = "---"
const MD_SEPARATOR_END   = "---"

function md_to_html(path::String; context::Module = @__MODULE__) :: String
  Genie.Renderer.build_module(
"""
<head></head>
<body>
<%
Base.include_string(context,
\"\"\"
\\\"\\\"\\\"
$(eval_markdown(read(path, String), context = context))
\\\"\\\"\\\"
\"\"\") |> Markdown.parse |> Markdown.html
%>
</body>
""",
    joinpath(Genie.config.path_build, Genie.Renderer.BUILD_NAME, path),
    string(hash(path), ".html"),
    output_path = false
  )
end

"""
    eval_markdown(md::String; context::Module = @__MODULE__) :: String

Converts the mardown `md` to HTML view code.
"""
function eval_markdown(md::String; context::Module = @__MODULE__) :: String
  if startswith(md, string(MD_SEPARATOR_START))
    close_sep_pos = findfirst(MD_SEPARATOR_END, md[length(MD_SEPARATOR_START)+1:end])
    metadata = md[length(MD_SEPARATOR_START)+1:close_sep_pos[end]] |> YAML.load

    isa(metadata, Dict) || (@warn "\nFound Markdown YAML metadata but it did not result in a `Dict` \nPlease check your markdown metadata \n$metadata")

    try
      for (k,v) in metadata
        vars()[Symbol(k)] = v
      end
    catch ex
      @error ex
    end

    md = replace((md[close_sep_pos[end]+length(MD_SEPARATOR_END)+1:end] |> strip), "\"\"\""=>"\\\"\\\"\\\"")
  end

  md
end

end


text:
#!/usr/bin/env julia

using Gtk
using Cairo
using GtkBuilderAid

# Test using canvas features of DrawingArea
type CanvasData
  surface::CairoSurface
end

# Clear the surface to white
function clear_surface(surf::CairoSurface)
  ctx = CairoContext(surf)
  set_source_rgb(ctx, 1, 1, 1)
  paint(ctx)
end

function draw_brush(
    canvas,
    x,
    y,
    surf)
  px = Int(round(x - 3))
  py = Int(round(y - 3))
  ctx = CairoContext(surf)
  rectangle(ctx, px, py, 6, 6)
  fill(ctx)
  reveal_area(canvas, px, py, 6, 6)
end

@GtkBuilderAid function_name(canvas_builder) begin

@guarded Cint(0) function configure_event_cb(
    canvas,
    configure_event,
    userdata)
  userdata.surface = create_similar_surface(canvas, Gtk.GEnum(Cairo.CONTENT_COLOR_ALPHA))
  clear_surface(userdata.surface)
  return Cint(1)
end

@guarded Cint(1) function draw_cb(
    canvas,
    ctx_ptr,
    userdata)

  ctx = CairoContext(ctx_ptr)

  set_source_surface(ctx, userdata.surface, 0, 0)
  paint(ctx)
  return Cint(0)
end

@guarded Cint(0) function button_press_event_cb(
    canvas,
    event_ptr,
    userdata)

  event = Gtk.GdkEvent(event_ptr)

  # Make sure the surface is valid
  if userdata.surface.ptr == C_NULL
    return Cint(0)
  end

  if event.button == 1
    draw_brush(canvas, event.x, event.y, userdata.surface)
  elseif event.button == 3
    clear_surface(userdata.surface)
    reveal(canvas, false)
  end

  return Cint(1)
end

@guarded Cint(0) function motion_notify_event_cb(
    canvas,
    event_ptr,
    userdata)

  event = Gtk.GdkEvent(event_ptr)

  # Make sure the surface is valid
  if userdata.surface.ptr == C_NULL
    return Cint(0)
  end

  if (event.state & Gtk.GdkModifierType.BUTTON1) != 0
    draw_brush(canvas, event.x, event.y, userdata.surface)
  end

  return Cint(1)

end

end

@guarded function activate_cb(
    app_ptr,
    userdata)
  app = GObject(app_ptr)
  # Start with an under-defined cairo surface
  built = canvas_builder(
    "resources/main.ui",
    CanvasData(CairoSurface(C_NULL, -1, -1)))

  w = Gtk.GAccessor.object(built, "drawing_window")

  push!(app, w)
  showall(w)

  return nothing
end

app = @GtkApplication("io.github.matt5sean3.GtkBuilderAid.second", 0)
signal_connect(activate_cb, app, "activate", Void, (), false)
println("Start application")
run(app)


text:
_css = "
<style>
body {
    background: #fafafa url(https://jackrugile.com/images/misc/noise-diagonal.png);
    color: #444;
    font: 100%/30px 'Helvetica Neue', helvetica, arial, sans-serif;
    text-shadow: 0 1px 0 #fff;
}

strong {
    font-weight: bold;
}

em {
    font-style: italic;
}

table {
    background: #f5f5f5;
    border-collapse: separate;
    box-shadow: inset 0 1px 0 #fff;
    font-size: 12px;
    line-height: 24px;
    margin: 30px auto;
    text-align: left;
    width: 800px;
}

th {
    background: url(https://jackrugile.com/images/misc/noise-diagonal.png), linear-gradient(#777, #444);
    border-left: 1px solid #555;
    border-right: 1px solid #777;
    border-top: 1px solid #555;
    border-bottom: 1px solid #333;
    box-shadow: inset 0 1px 0 #999;
    color: #fff;
  font-weight: bold;
    padding: 10px 15px;
    position: relative;
    text-shadow: 0 1px 0 #000;
}

th:after {
    background: linear-gradient(rgba(255,255,255,0), rgba(255,255,255,.08));
    content: '';
    display: block;
    height: 25%;
    left: 0;
    margin: 1px 0 0 0;
    position: absolute;
    top: 25%;
    width: 100%;
}

th:first-child {
    border-left: .5px solid #777;
    box-shadow: inset 1px 1px 0 #999;
}

th:last-child {
    box-shadow: inset -1px 1px 0 #999;
}

td {
    border-right: 1px solid #fff;
    border-left: 1px solid #e8e8e8;
    border-top: 1px solid #fff;
    border-bottom: 1px solid #e8e8e8;
    padding: 10px 15px;
    position: relative;
    transition: all 300ms;
}

td:first-child {
    box-shadow: inset 1px 0 0 #fff;
}

td:last-child {
    border-right: 1px solid #e8e8e8;
    box-shadow: inset -1px 0 0 #fff;
}

tr {
    background: url(https://jackrugile.com/images/misc/noise-diagonal.png);
}

tr:nth-child(odd) td {
    background: #f1f1f1 url(https://jackrugile.com/images/misc/noise-diagonal.png);
}

tr:last-of-type td {
    box-shadow: inset 0 -1px 0 #fff;
}

tr:last-of-type td:first-child {
    box-shadow: inset 1px -1px 0 #fff;
}

tr:last-of-type td:last-child {
    box-shadow: inset -1px -1px 0 #fff;
}

tbody:hover td {
    color: transparent;
    text-shadow: 0 0 3px #aaa;
}

tbody:hover tr:hover td {
    color: #444;
    text-shadow: 0 1px 0 #fff;
}
</style>
"


text:
using JSXGraph

brd = board("brd",xlim=[-15,15], ylim=[-15,15],axis=true)

a = slider("a", [[8, 7], [12, 7], [-3, 0.1, 10]])
b = slider("b", [[8, 6], [12, 6], [-1, 1, 5]])
c = slider("c", [[8, 5], [12, 5], [-10, -5, 2]])
[a,b,c] |> brd

@jsf f(x) = val(a) * x^2 + val(b) * x + val(c)

brd ++ plot(f)
brd.style="width:500px;height:500px;margin:0 auto;"
show(brd)

max_stars_repo_name: terasakisatoshi/jsexample

max_issues_repo_name: terasakisatoshi/jsexample

max_forks_repo_name: terasakisatoshi/jsexample


text:
module LatticeVis

__precompile__(true)

using StaticArrays.FixedSizeArrays
import Base: size, in, ==, +, show, merge

include("UnitCell.jl")
export UnitCell, get_pos, dims, merge
export square, triangle, honeycomb
export sc, bcc, fcc, diamond

include("Neighbors.jl")
export get_neighbors

include("LatticeGraph.jl")
export Lattice
export SimpleSite, SimpleBond, SimpleGraph
export AbstractNode, AbstractEdge, AbstractGraph

using PyPlot
include("Pyplot.jl")
export plot

include("GLVisualize/Visualize.jl")

end # module


text:
function get_iau_pole(::Type{PredefinedIAUPoles}, arg0::JPLEphemeridesLoader_EphemerisType, arg1::TimeScales)
    return jcall(PredefinedIAUPoles, "getIAUPole", PredefinedIAUPoles, (JPLEphemeridesLoader_EphemerisType, TimeScales), arg0, arg1)
end


text:
g = IntAdjacencyList(4,3)
add_edge!(g, 1, 2, 1)
add_edge!(g, 2, 3, 1)
add_edge!(g, 1, 4, 2)

@test num_edges(g) == 3

@test num_timestamps(g) == 3

@test num_nodes(g) == 4

@test forward_neighbors(g, 1, 1) == [(2,1), (1,2)]

@test forward_neighbors(g, 2, 1) == [(3,1)]

@test adjacency_matrix(g, 1) ≈ [0 1 0 0; 0 0 1 0; 0 0 0 0; 0 0 0 0]

@test adjacency_matrix(g, 2) ≈ [0 0 0 1; 0 0 0 0; 0 0 0 0; 0 0 0 0]

@test full(sparse_adjacency_matrix(g, 1)) ≈ adjacency_matrix(g, 1)
@test full(sparse_adjacency_matrix(g, 2)) ≈ adjacency_matrix(g, 2)

add_edge!(g, 2, 4, 3)

@test num_edges(g) == 4

@test forward_neighbors(g, 2, 1) == [(3,1), (2,3)]

@test backward_neighbors(g, 4, 3) == [(4,2), (2,3)]

A = block_adjacency_matrix(g)

@test full(A)[1:4,1:4] ≈ adjacency_matrix(g, 1)
@test full(A)[5:8,5:8] ≈ adjacency_matrix(g, 2)
@test A[1, 5] ≈ 1.
@test A[2, 10] ≈ 0.5
@test A[8, 12] ≈ 1.


text:
# Given the log likelihood (LL) of each seed,
# calculate the log of the mean likelihood,
# i.e. LL = log (sum(LL_seeds) / nseeds)
# INSTEAD OF the mean of the log likelihoods
# i.e. sum (log (LL_seeds)) / nseeds
# Thanks to  Alireza Modirshanechi!
function get_meanLL_acrossseeds(LL_seeds)
    LL_max, seed_idx = findmax(LL_seeds)
    tempsum = 0.
    for i in 1:length(LL_seeds)
        tempsum += exp(LL_seeds[i] - LL_max)
    end
    LL = LL_max + log(tempsum) - log(length(LL_seeds))
end