text:
@inline function writefixed(buf, pos, v::T,
    precision=-1, plus=false, space=false, hash=false,
    decchar=UInt8('.'), trimtrailingzeros=false) where {T <: Base.IEEEFloat}
    @assert 0 < pos <= length(buf)
    startpos = pos
    x = Float64(v)
    neg = signbit(x)
    # special cases
    if x == 0
        if neg
            buf[pos] = UInt8('-')
            pos += 1
        elseif plus
            buf[pos] = UInt8('+')
            pos += 1
        elseif space
            buf[pos] = UInt8(' ')
            pos += 1
        end
        buf[pos] = UInt8('0')
        pos += 1
        if precision > 0
            buf[pos] = decchar
            pos += 1
            if trimtrailingzeros
                precision = 1
            end
            for _ = 1:precision
                buf[pos] = UInt8('0')
                pos += 1
            end
        elseif hash
            buf[pos] = decchar
            pos += 1
        end
        return pos
    elseif isnan(x)
        buf[pos] = UInt8('N')
        buf[pos + 1] = UInt8('a')
        buf[pos + 2] = UInt8('N')
        return pos + 3
    elseif !isfinite(x)
        if neg
            buf[pos] = UInt8('-')
            pos += 1
        elseif plus
            buf[pos] = UInt8('+')
            pos += 1
        elseif space
            buf[pos] = UInt8(' ')
            pos += 1
        end
        buf[pos] = UInt8('I')
        buf[pos + 1] = UInt8('n')
        buf[pos + 2] = UInt8('f')
        return pos + 3
    end

    bits = Core.bitcast(UInt64, x)
    mant = bits & MANTISSA_MASK
    exp = Int((bits >> 52) & EXP_MASK)

    if exp == 0
        e2 = 1 - 1023 - 52
        m2 = mant
    else
        e2 = exp - 1023 - 52
        m2 = (Int64(1) << 52) | mant
    end
    nonzero = false
    if neg
        buf[pos] = UInt8('-')
        pos += 1
    elseif plus
        buf[pos] = UInt8('+')
        pos += 1
    elseif space
        buf[pos] = UInt8(' ')
        pos += 1
    end
    if e2 >= -52
        idx = e2 < 0 ? 0 : indexforexp(e2)
        p10bits = pow10bitsforindex(idx)
        len = lengthforindex(idx)
        i = len - 1
        while i >= 0
            j = p10bits - e2
            #=@inbounds=# mula, mulb, mulc = POW10_SPLIT[POW10_OFFSET[idx + 1] + i + 1]
            digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8)
            if nonzero
                pos = append_nine_digits(digits, buf, pos)
            elseif digits != 0
                olength = decimallength(digits)
                pos = append_n_digits(olength, digits, buf, pos)
                nonzero = true
            end
            i -= 1
        end
    end
    if !nonzero
        buf[pos] = UInt8('0')
        pos += 1
    end
    if precision > 0 || hash
        buf[pos] = decchar
        pos += 1
    end
    if e2 < 0
        idx = div(-e2, 16)
        blocks = div(precision, 9) + 1
        roundUp = 0
        i = 0
        if blocks <= MIN_BLOCK_2[idx + 1]
            i = blocks
            for _ = 1:precision
                buf[pos] = UInt8('0')
                pos += 1
            end
        elseif i < MIN_BLOCK_2[idx + 1]
            i = MIN_BLOCK_2[idx + 1]
            for _ = 1:(9 * i)
                buf[pos] = UInt8('0')
                pos += 1
            end
        end
        while i < blocks
            j = 120 + (-e2 - 16 * idx)
            p = POW10_OFFSET_2[idx + 1] + UInt32(i) - MIN_BLOCK_2[idx + 1]
            if p >= POW10_OFFSET_2[idx + 2]
                for _ = 1:(precision - 9 * i)
                    buf[pos] = UInt8('0')
                    pos += 1
                end
                break
            end
            #=@inbounds=# mula, mulb, mulc = POW10_SPLIT_2[p + 1]
            digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8)
            if i < blocks - 1
                pos = append_nine_digits(digits, buf, pos)
            else
                maximum = precision - 9 * i
                lastDigit = 0
                k = 0
                while k < 9 - maximum
                    # global digits, lastDigit, k
                    lastDigit = digits % 10
                    digits = div(digits, 10)
                    k += 1
                end
                if lastDigit != 5
                    roundUp = lastDigit > 5
                else
                    requiredTwos = -e2 - precision - 1
                    trailingZeros = requiredTwos <= 0 || (requiredTwos < 60 && pow2(m2, requiredTwos))
                    roundUp = trailingZeros ? 2 : 1
                end
                if maximum > 0
                    pos = append_c_digits(maximum, digits, buf, pos)
                end
                break
            end
            i += 1
        end
        if roundUp != 0
            roundPos = pos
            dotPos = 1
            while true
                roundPos -= 1
                if roundPos == (startpos - 1) || (buf[roundPos] == UInt8('-')) || (plus && buf[roundPos] == UInt8('+')) || (space && buf[roundPos] == UInt8(' '))
                    buf[roundPos + 1] = UInt8('1')
                    if dotPos > 1
                        buf[dotPos] = UInt8('0')
                        buf[dotPos + 1] = decchar
                    end
                    buf[pos] = UInt8('0')
                    pos += 1
                    break
                end
                c = roundPos > 0 ? buf[roundPos] : 0x00
                if c == decchar
                    dotPos = roundPos
                    continue
                elseif c == UInt8('9')
                    buf[roundPos] = UInt8('0')
                    roundUp = 1
                    continue
                else
                    if roundUp == 2 && UInt8(c) % 2 == 0
                        break
                    end
                    buf[roundPos] = c + 1
                    break
                end
            end
        end
    else
        for _ = 1:precision
            buf[pos] = UInt8('0')
            pos += 1
        end
    end
    if trimtrailingzeros
        while buf[pos - 1] == UInt8('0')
            pos -= 1
        end
        if buf[pos - 1] == decchar && !hash
            pos -= 1
        end
    end
    return pos
end

max_stars_repo_name: jmkuhn/julia

max_issues_repo_name: jmkuhn/julia

max_forks_repo_name: jmkuhn/julia


text:
function post(alg::TMJets21a{N}, ivp::IVP{<:AbstractContinuousSystem}, timespan;
              Δt0::TimeInterval=zeroI,
              kwargs...) where {N}

    @unpack orderQ, orderT, abstol, maxsteps, adaptive, minabstol, absorb, disjointness = alg

    # initial time and final time
    t0 = tstart(timespan)
    T = tend(timespan)

    # vector field
    if islinear(ivp) || isaffine(ivp) # TODO: refactor with inplace_field!
        f! = inplace_field!(ivp)
    else
        f! = VectorField(ivp)
    end

    # algorithm optional arguments
    parse_eqs = get(kwargs, :parse_eqs, false)
    params = get(kwargs, :params, nothing)
    check_property = get(kwargs, :check_property, (t, x) -> true)

    n = statedim(ivp)
    ivp_norm = _normalize(ivp)
    X = stateset(ivp_norm)

    # fix the working variables and maximum order in the global
    # parameters struct (_params_TaylorN_)
    set_variables("x", numvars=n, order=2*orderQ)

    # initial set
    X0 = initial_state(ivp_norm)
    X0tm = _initialize(X0, orderQ, orderT)

    # optionally absorb initial remainder
    shrink_wrapping = get(kwargs, :shrink_wrapping, true)
    if shrink_wrapping && isa(X0tm, TaylorModelReachSet)
        if !all(iszero, remainder(X0tm))
            X0tm = _shrink_wrapping(X0tm)
        end
    end

    # call external solver
    TMSol = TaylorModels.validated_integ(f!, X0tm, t0, T, orderQ, orderT,
                                                 abstol, params;
                                                 maxsteps=maxsteps,
                                                 parse_eqs=parse_eqs,
                                                 adaptive=adaptive,
                                                 minabstol=minabstol,
                                                 absorb=absorb,
                                                 check_property=check_property)
    tv = TMSol.time
    xv = TMSol.fp
    xTM1v = TMSol.xTM

    # preallocate flowpipe
    F = Vector{TaylorModelReachSet{N}}()
    sizehint!(F, maxsteps)

    # loop over reach-sets (the first reach-set at the initial time point is ignored)
    @inbounds for i in 2:length(tv)

        # create Taylor model reach-set
        δt = TimeInterval(tv[i] + domain(xTM1v[1, i]))
        Ri = TaylorModelReachSet(xTM1v[:, i], δt + Δt0)

        # check intersection with invariant
        _is_intersection_empty(Ri, X, disjointness) && break

        push!(F, Ri)
    end

    ext = Dict{Symbol, Any}(:tv => tv, :xv => xv, :xTM1v => xTM1v)
    return Flowpipe(F, ext)
end

ext: jl


text:
module MinimaxAI

using DelimitedFiles

include("AI.jl")
include("TicTacToe_game.jl")
include("Bones_game.jl")
include("Connect4_game.jl")

export playTicTacToe, playBones, playConnect4

end


text:
include("../interfaces.jl")

import PhysicalConstants.CODATA2018 as Constants # this must be moved at a high level in the package to standardize use

### Boundary conditions
function Temperature(;name,T=273.15)
    val = T
    @parameters T # how to give default parameters. how to give default to variables

    @named port = HeatPort()

    eqs =
    [
        port.T ~ T
    ]
    ODESystem(eqs, t, [], [T], systems=[port], name=name)
end

### Resistance Models
# how to have the similar effect as extend PartialResistance to avoid code rewrite
function Sphere(;name, r_inner = 0.01, r_outer=1.0, lambda=5.0)
    val0 = r_inner # don't understand why there "val*" are required for parameters with default values
    val1 = r_outer
    val2 = lambda
    @variables R(t)
    @parameters r_inner, r_outer, lambda

    @named port_a = HeatPort()
    @named port_b = HeatPort()

    eqs =
    [
        0 ~ port_a.Q_flow + port_b.Q_flow
        port_a.Q_flow ~ (port_a.T - port_b.T)/R
        R ~ 1/(4*3.14159*lambda)*(1/r_inner - 1/r_outer)
    ]
    ODESystem(eqs, t, [R], [r_inner, r_outer, lambda], systems=[port_a, port_b], name=name)
end

function Convection(;name, surfaceArea = 0.01, alpha=1000.0)
    val0 = surfaceArea
    val1 = alpha
    @variables R(t)
    @parameters surfaceArea, alpha

    @named port_a = HeatPort()
    @named port_b = HeatPort()

    eqs =
    [
        0 ~ port_a.Q_flow + port_b.Q_flow
        port_a.Q_flow ~ (port_a.T - port_b.T)/R
        R ~ 1/(alpha*max(eps(),surfaceArea))
    ]
    ODESystem(eqs, t, [R], [surfaceArea, alpha], systems=[port_a, port_b], name=name)
end

function Contact(;name, surfaceArea = 0.01, Rc_pp=1.0)
    val0 = surfaceArea
    val1 = Rc_pp
    @variables R(t)
    @parameters surfaceArea, Rc_pp

    @named port_a = HeatPort()
    @named port_b = HeatPort()

    eqs =
    [
        0 ~ port_a.Q_flow + port_b.Q_flow
        port_a.Q_flow ~ (port_a.T - port_b.T)/R
        R ~ Rc_pp/max(eps(),surfaceArea)
    ]
    ODESystem(eqs, t, [R], [surfaceArea, Rc_pp], systems=[port_a, port_b], name=name)
end

function Radiation(;name, surfaceArea = 0.01, epsilon=1.0)
    val0 = surfaceArea
    val1 = epsilon
    @variables R(t)
    @parameters surfaceArea, epsilon

    @named port_a = HeatPort()
    @named port_b = HeatPort()

    eqs =
    [
        0 ~ port_a.Q_flow + port_b.Q_flow
        port_a.Q_flow ~ (port_a.T - port_b.T)/R
        R ~ 1/(surfaceArea*5.6703744E-8*epsilon*(port_a.T^2+port_b.T^2)*(port_a.T + port_b.T))
    ]
 # exact: R ~ 1/(surfaceArea*5.6703744E-8*epsilon*(port_a.T^2+port_b.T^2)*(port_a.T + port_b.T))
 # approximate: R ~ 1/(4*surfaceArea*5.6703744E-8*epsilon*(0.5*(port_a.T + port_b.T))^3)
    # seems to be having issues :(

    # using "Constants.StefanBoltzmannConstant" causes Unitful error
    ODESystem(eqs, t, [R], [surfaceArea, epsilon], systems=[port_a, port_b], name=name)
end


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

const UDPRespBits = (
    req_counter = Bit{RW}(7),       # Request counter, toggles with each request
    prot_error = Bit{RW}(6),        # Protocol error (request command packet error)
    access_timeout = Bit{RW}(5),    # SIS3316 access timeout (fifo empty)
    no_eth_grant = Bit{RW}(4),      # Ethernet interface has no grant
    pkg_counter = BitRange{RW}(0:3),     # Packet counter
)

function check_udp_response_status(status::UInt8)
    getval(status, UDPRespBits.prot_error) && throw(ErrorException("Protocol error bit set in response status"))
    getval(status, UDPRespBits.access_timeout) && throw(ErrorException("Access timeout bit set in response status"))
    getval(status, UDPRespBits.no_eth_grant) && throw(ErrorException("Ethernet interface has no grant set in response status"))
    nothing
end

is_link_interface_register(addr::Integer) = addr < 0x20

function check_vme_datalen_mode(nbytes::Integer, mode::VMEMode)
    if mode == VME_A32_D32_SCT || mode == VME_A32_D32_BLT
        nbytes % sizeof(UInt32) == 0 || throw(ArgumentError("Invalid data size $nbytes for mode $mode"))
    elseif mode == VME_A32_D64_MBLT || mode == VME_A32_D64_2eVME
        nbytes % sizeof(UInt64) == 0 || throw(ArgumentError("Invalid data size $nbytes for mode $mode"))
    else
        throw(ArgumentError("VME mode $mode not supported"))
    end
    nothing
end

"""
    mutable struct SIS3316Gateway <: AbstractVMEGateway

Struck SIS3316 VME Ethernet gateway implementation.

Constructor:

    SIS3316Gateway(host::Union{AbstractString,IPv4}, port::Integer = 0xE000)

Finalizer:

    Base.close(gw::SIS3316Gateway)

Relevant functions:

    * [`read_registers!`](@ref)
    * [`write_registers!`](@ref)
"""
mutable struct SIS3316Gateway <: AbstractVMEGateway
    hostname::String
    ipaddr::IPv4
    port::UInt16
    socket::UDPSocket
    getsocket_lock::ReentrantLock
    socketio_lock::ReentrantLock
    socket_timeout::Atomic{UInt64}
    socket_mon::Channel{Nothing}
    last_pkgid::UInt8
    default_timeout::Float64
end

export SIS3316Gateway

function SIS3316Gateway(hostname::AbstractString, ipaddr::IPv4, port::Integer = 0xE000; default_timeout = 0.5)
    socket = UDPSocket()
    getsocket_lock = ReentrantLock()
    socketio_lock = ReentrantLock()
    socket_timeout = Atomic{UInt64}(0)
    socket_mon = Channel{Nothing}(); close(socket_mon)
    last_pkgid = UInt8(0)

    gw = SIS3316Gateway(
        hostname, ipaddr, port,
        socket, getsocket_lock, socketio_lock, socket_timeout, socket_mon,
        last_pkgid, default_timeout
    )
    gw.socket_mon = socketmon(gw)

    finalizer(x -> close(x), gw)

    check_connection(gw)
    gw
 end

SIS3316Gateway(hostname::AbstractString, port::Integer = 0xE000; default_timeout = 0.5) =
    SIS3316Gateway(hostname, getaddrinfo(hostname, IPv4), port, default_timeout = default_timeout)

SIS3316Gateway(ipaddr::IPv4, port::Integer = 0xE000; default_timeout = 0.5) =
    SIS3316Gateway(getnameinfo(ipaddr), ipaddr, port, default_timeout = default_timeout)

Base.show(io::IO, gw::SIS3316Gateway) = print(io, "SIS3316Gateway($(gw.hostname), $(gw.ipaddr), $(repr(gw.port)))")

Base.deepcopy(gw::SIS3316Gateway) = SIS3316Gateway(gw.hostname, gw.ipaddr, gw.port, default_timeout = gw.default_timeout)

Base.isopen(gw::SIS3316Gateway) = isopen(gw.socket_mon)

function Base.close(gw::SIS3316Gateway)
    if isopen(gw.socket_mon)
        # @info "Closing $gw" # logging causes trouble in finalizers
        lock(gw.socketio_lock) do
            close(gw.socket_mon)
        end
        # Just to make sure:
        lock(gw.socketio_lock) do
            close(getsocket(gw))
        end
        # @info "Closed $gw" # logging causes trouble in finalizers
    end
end

function getsocket(gw::SIS3316Gateway)
    lock(gw.getsocket_lock) do
        gw.socket
    end
end

function socketmon(gw::SIS3316Gateway; sleep_interval_ns::Integer = 50 * 10^6)
    Channel{Nothing}(0; taskref = nothing, spawn = true) do ch
        try
            @debug "UDP Socket monitor started"
            while isopen(ch)
                t_ns = time_ns()::UInt64
                timeout = gw.socket_timeout[]
                # Note: timeout == 0 means watchdog disabled
                if timeout != 0 && signed(t_ns - timeout) > 0
                    @info "UDP socket watchdog timeout, opening new UDP Socket"
                    lock(gw.getsocket_lock) do
                        new_socket = UDPSocket()
                        close(gw.socket)
                        gw.socket = new_socket
                        gw.socket_timeout[] = 0
                    end
                end
                sleep(sleep_interval_ns / 10^9)
            end
            @debug "UDP Socket monitor closed and terminating"
        catch err
            @error err
        finally
            close(gw.socket)
        end
    end
end

function timeout_after!(gw::SIS3316Gateway, timeout_s::Real)
    timeout_ns = trunc(UInt64, timeout_s * 10^9)
    timeout_at = time_ns() + timeout_ns
    if timeout_at == 0
        # Note: timeout == 0 means watchdog disabled, so add 1
        timeout_at += 1
    end
    gw.socket_timeout[] = timeout_at
end

function disable_timeout!(gw::SIS3316Gateway)
    # Note: timeout == 0 means watchdog disabled
    gw.socket_timeout[] = 0
end

function Sockets.send(gw::SIS3316Gateway, req::Vector{UInt8})
    socket = getsocket(gw)
    lock(gw.socketio_lock) do
        # @debug "Sending UDP request to $(gw.hostname): $req"
        send(socket, gw.ipaddr, gw.port, req)
    end
end

function Sockets.recv(gw::SIS3316Gateway, len::Missing = missing; timeout::Real = gw.default_timeout)
    socket = getsocket(gw)
    resp_from, resp = lock(gw.socketio_lock) do
        # @debug "Waiting for UDP data from $(gw.hostname)"
        timeout_after!(gw, timeout)
        resp_from, resp = recvfrom(socket)
        disable_timeout!(gw)
        resp_from, resp
    end
    resp_from_expected = Sockets.InetAddr{IPv4}(gw.ipaddr, gw.port)
    resp_from == resp_from_expected || throw(ErrorException("Received UDP data from $resp_from instead from expected $resp_from_expected"))
    # @debug "Received $(length(resp)) UDP data bytes $(gw.hostname) "
    resp
end

function Sockets.recv(gw::SIS3316Gateway, len::Integer; timeout::Real = gw.default_timeout)
    resp = recv(gw, missing, timeout = timeout)
    resp_len = length(resp)
    length(resp) == len || throw(ErrorException("Received UDP response with $resp_len bytes, but expected $len bytes"))
    resp
end

function check_resp_pkg!(cmd::UInt8, pkgid::UInt8, recvbuf::IO)
    recv_cmd, recv_pkgid = read(recvbuf, UInt8), read(recvbuf, UInt8)
    recv_cmd == cmd || throw(ErrorException("Received response to command $recv_cmd, but expected $cmd"))
    recv_pkgid == pkgid || throw(ErrorException("Received with package id $recv_pkgid, but expected $pkgid"))
    recvbuf
end

function send_req_recv_resp!(gw::SIS3316Gateway, reqbuf::IO, recv_len::Union{Missing,Integer}; timeout::Real = gw.default_timeout)
    req = take!(reqbuf)
    cmd, pkgid = req[1], req[2]

    resp_pkg = lock(gw.socketio_lock) do
        send(gw, req)
        recv(gw, recv_len, timeout = timeout)
    end

    resp = IOBuffer(resp_pkg)
    check_resp_pkg!(cmd, pkgid, resp)

    resp
end

function check_connection(gw::SIS3316Gateway)
    lock(gw.socketio_lock) do
        req = UInt8[0x10, 0x42, 0x00, 0x00, 0x00, 0x04]
        send(gw, UInt8[0x10, 0x42, 0x00, 0x00, 0x00, 0x04])
        resp = try
            recv(gw, 10, timeout = 1.0)
        catch err
            if err isa EOFError
                throw(ErrorException("Can't connect to $gw"))
            else
                rethrow()
            end
        end
        inbuf = IOBuffer(resp)
        read(inbuf, 6) == req[1:6] || throw(ErrorException("Invalid UDP response $resp during connection test"))
        devinfo = read(inbuf, UInt32)
        @assert eof(inbuf)
        @info "Tested connection to $(gw.hostname), received device info $(repr(devinfo))"
    nothing
    end
end

function next_pkgid!(gw::SIS3316Gateway)
    lock(gw.socketio_lock) do
        next_id::UInt8 = gw.last_pkgid::UInt8 + UInt8(1)
        gw.last_pkgid = next_id
        next_id
    end
end

function read_link_interface_register(gw::SIS3316Gateway, addr::Integer; timeout::Real = gw.default_timeout)
    conv_addr = UInt32(addr)

    req = IOBuffer()
    write(req, UInt8(0x10))
    write(req, UInt8(next_pkgid!(gw)))
    write(req, htol(conv_addr))

    resp = send_req_recv_resp!(gw, req, 10, timeout = timeout)

    recv_addr = read(resp, UInt32)
    recv_value = read(resp, UInt32)
    @assert eof(resp)

    recv_addr == addr || throw(ErrorException("Received interface register value for address $(repr(recv_addr)), but expected address $(repr(conv_addr))"))

    recv_value
end

function write_link_interface_register!(gw::SIS3316Gateway, addr::Integer, value::Integer; timeout::Real = gw.default_timeout)
    conv_addr = UInt32(addr)
    conv_value = UInt32(value)

    req = IOBuffer()
    write(req, UInt8(0x11))
    write(req, htol(conv_addr))
    write(req, htol(conv_value))

    send(gw, take!(req))

    nothing
end

function _read_register_space_req(gw::SIS3316Gateway, addrs::Vector{UInt32}; timeout::Float64 = gw.default_timeout)
    n = length(eachindex(addrs))
    n == 0 && return Vector{UInt32}()
    n > 64 && throw(ArgumentError("Cannot read more than 64 registers with a single request"))

    req = IOBuffer()
    write(req, UInt8(0x20))
    write(req, UInt8(next_pkgid!(gw)))
    write(req, htol(UInt16(n - 1)))
    write(req, htol.(addrs::Vector{UInt32}))

    len_resp = 3 + n * sizeof(UInt32)
    resp = send_req_recv_resp!(gw, req, len_resp, timeout = timeout)

    status = read(resp, UInt8)
    data = read!(resp, Vector{UInt32}(undef, n))
    data .= htol.(data)
    @assert eof(resp)

    check_udp_response_status(status)
    pkg_counter = getval(status, UDPRespBits.pkg_counter)
    pkg_counter == 0 || throw(ErrorException("Packet counter in response is $pkg_counter, but expected 0"))

    data
end

function read_register_space(gw::SIS3316Gateway, addrs::AbstractVector{<:Integer}; timeout::Real = gw.default_timeout)
    data = Vector{UInt32}()
    sizehint!(data, length(eachindex(addrs)))
    for addrs_part in Base.Iterators.partition(addrs, 64)
        conv_addrs = convert(Vector{UInt32}, addrs_part)
        append!(data, _read_register_space_req(gw, conv_addrs, timeout = Float64(timeout)))
    end
    data
end

function read_registers!(gw::SIS3316Gateway, addrs::Vector{UInt32}, values::Vector{UInt32}; timeout::Float64 = gw.default_timeout)
    @debug "Reading registers $(repr(addrs)) on $gw"
    length(addrs) == length(values) || throw(ArgumentError("Number of addresses does not match number of values"))

    if !(isempty(addrs))
        lock(gw.socketio_lock) do
            li_idxs = findall(is_link_interface_register, addrs)
            re_idxs = findall(!is_link_interface_register, addrs)

            for i in li_idxs
                values[i] = read_link_interface_register(gw, addrs[i], timeout = timeout)
            end

            values[re_idxs] = read_register_space(gw, addrs[re_idxs], timeout = timeout)
        end
    end

    values
end

read_registers!(gw::SIS3316Gateway, addrs::AbstractVector{<:Integer}, values::AbstractVector{<:Integer}; timeout::Real = gw.default_timeout) =
    read_registers!(gw, convert(Vector{UInt32}, addrs), convert(Vector{UInt32}, values), timeout = Float64(timeout))

function _write_register_space_req!(gw::SIS3316Gateway, addrs_vals::Vector{Pair{UInt32,UInt32}}; timeout::Float64 = gw.default_timeout)
    n = length(addrs_vals)
    n == 0 && return Vector{UInt32}()
    n > 64 && throw(ArgumentError("Cannot write more than 64 registers with a single request"))

    addrs_data_flat = collect(Iterators.flatten(addrs_vals))

    req = IOBuffer()
    write(req, UInt8(0x21))
    write(req, UInt8(next_pkgid!(gw)))
    write(req, htol(UInt16(n - 1)))
    write(req, htol.(addrs_data_flat::Vector{UInt32}))

    resp = send_req_recv_resp!(gw, req, 3, timeout = timeout)

    status = read(resp, UInt8)
    @assert eof(resp)

    check_udp_response_status(status)
    pkg_counter = getval(status, UDPRespBits.pkg_counter)
    pkg_counter == 0 || throw(ErrorException("Packet counter in response is $pkg_counter, but expected 0"))
end

function write_register_space!(gw::SIS3316Gateway, addrs_vals::AbstractVector{<:Pair{<:Integer,<:Integer}}; timeout::Real = gw.default_timeout)
    for addr_vals_part in Base.Iterators.partition(addrs_vals, 64)
        conv_addr_vals = convert(Vector{Pair{UInt32,UInt32}}, addr_vals_part)
        _write_register_space_req!(gw, conv_addr_vals, timeout = Float64(timeout))
    end
    nothing
end

write_register_space!(gw::SIS3316Gateway, addrs::AbstractVector{<:Integer}, values::AbstractVector{<:Integer}; timeout::Real = gw.default_timeout) =
    write_register_space!(gw, addrs .=> values, timeout = timeout)

function write_registers!(gw::SIS3316Gateway, addrs::AbstractVector{UInt32}, values::AbstractVector{UInt32}; timeout::Float64 = gw.default_timeout)
    @debug "Writing registers $(repr(addrs)) on $gw"
    length(addrs) == length(values) || throw(ArgumentError("Number of addresses does not match number of values"))

    if !isempty(addrs)
        lock(gw.socketio_lock) do
            li_idxs = findall(is_link_interface_register, addrs)
            re_idxs = findall(!is_link_interface_register, addrs)

            # Since there's no acknowledgement when writing link interface
            # registers, make a short pause between each request and a
            # longer pause after all requests.
            for i in li_idxs
                write_link_interface_register!(gw, addrs[i], values[i], timeout = timeout)

                sleep(0.005) # or yield() for a shorter pause
            end
            sleep(0.015) # or yield() for a shorter pause

            write_register_space!(gw, addrs[re_idxs], values[re_idxs], timeout = timeout)
        end
    end

    nothing
end

write_registers!(gw::SIS3316Gateway, addrs::AbstractVector{<:Integer}, values::AbstractVector{<:Integer}; timeout::Real = gw.default_timeout) =
    write_registers!(gw, convert(Vector{UInt32}, addrs), convert(Vector{UInt32}, values), timeout = Float64(timeout))

"""
    read_bulk!(
        gw::SIS3316Gateway,
        addr::Unsigned,
        data::AbstractVector{UInt32};
        timeout::Float64 = gw.default_timeout
    )

SIS3316 FIFO read transfer.
"""
function read_bulk!(gw::SIS3316Gateway, addr::UInt32, data::AbstractVector{UInt32}; timeout::Float64 = gw.default_timeout)
    # @debug "read_bulk!(::SIS3316Gateway, $(repr(addr)), data)"
    n = length(data)
    n == 0 && return data
    max_n = typemax(UInt16) + 1
    n > max_n && throw(ArgumentError("Cannot read more than $max_n 32-bit values with a single request"))

    chunks = Vector{Vector{UInt32}}(undef, 15)

    req = IOBuffer()
    cmd = UInt8(0x30)
    pkgid = next_pkgid!(gw)
    write(req, cmd::UInt8)
    write(req, pkgid::UInt8)
    write(req, htol(UInt16(n - 1)))
    write(req, htol(addr::UInt32))

    chunks_i0 = firstindex(chunks)
    n_words_recv::Int = 0
    n_chunks_recv::Int = 0

    lock(gw.socketio_lock) do
        send(gw, take!(req))

        while n_words_recv < n
            resp_pkg = recv(gw, timeout = timeout)
            resp = IOBuffer(resp_pkg)
            check_resp_pkg!(cmd, pkgid, resp)

            status = read(resp, UInt8)
            check_udp_response_status(status)
            pkg_counter = getval(status, UDPRespBits.pkg_counter)
            pkg_counter > 14 && throw(ErrorException("Received FIFO read response packet with packet counter value $pkg_counter, should be kept <= 14 to prevent overflow"))

            chunk_len = bytesavailable(resp)
            chunk_len % sizeof(UInt32) == 0 || throw(ErrorException("Received FIFO read response with packet counter value $pkg_counter, but data length is not a multiple of $(sizeof(UInt32)) bytes"))
            chunk_n = div(chunk_len, sizeof(UInt32))
            # @debug "Received FIFO read response with packet counter value $pkg_counter and $chunk_n data words"

            chunk = read!(resp, Vector{UInt32}(undef, chunk_n))
            chunk .= ltoh.(chunk)
            @assert eof(resp)

            if isassigned(chunks, pkg_counter + chunks_i0)
                throw(ErrorException("Received FIFO read response packet with duplicate packet counter value $pkg_counter"))
            else
                chunks[pkg_counter + chunks_i0] = chunk
                n_chunks_recv += 1
                n_words_recv += chunk_n
            end
        end
    end

    # @info "Received $n_chunks_recv FIFO data chunks"

    filled_chunk_idxs = chunks_i0:(chunks_i0 + n_chunks_recv - 1)

    data_pos::Int = firstindex(data)
    for i in filled_chunk_idxs
        isassigned(chunks, i) || throw(ErrorException("Missing data chunk $i"))
        chunk = chunks[i]
        n = length(chunk)
        copyto!(data, data_pos, chunk, firstindex(chunk), n)
        data_pos += n
    end
    @assert data_pos == lastindex(data) + 1

    return data
end


text:
# using ..CovidSurvey
#include("../data.jl")

const regions = ["capital", "zealand", "south", "central", "north"]
const nregions = length(regions)
const populations = [1_855_084, 838_840, 1_223_634, 1_332_048, 590_439] # from statistics denmark

dan2eng = Dict(
    "Dato"          => "date",
    "Total"         => "total",
    "Hovedstaden"   => "capital",
    "Sj\xe6lland"   => "zealand",
    "Sjælland"      => "zealand",
    "Syddanmark"    => "south",
    "Midtjylland"   => "central",
    "Nordjylland"   => "north",
    "Ukendt Region" => "unknown"
)

function regions!(data)
    data["regions"] = regions
    data["num_regions"] = nregions
    data["populations"] = populations
    return nothing
end

# ===========================================================================
#                     SSI DATA
# ===========================================================================

function process_ssi(df)
    df = @_ DataFrames.select(df, 1=>:region, 2=>:date, 3=>:counts) |>
        filter(:region => x->!ismissing(x), __)
    df.region = replace(df.region, dan2eng...)
    df = filter(:region => x -> x ∈ regions, df)
    df = unstack(df, :date, :region, :counts)
    CovidSurvey.insert_missing_dates!(df)
    CovidSurvey.replace_missing!(df)
    df[:, ["date", regions...]]
    disallowmissing!(df)
    return df
end

function observables!(data,
    fname_cases  = normpath( homedir(), "data/Statens-Serum-Institut/dashboard/Regionalt_DB/08_bekraeftede_tilfaelde_pr_dag_pr_regions.csv" ),
    fname_hospit = normpath( homedir(), "data/Statens-Serum-Institut/dashboard/Regionalt_DB/06_nye_indlaeggelser_pr_region_pr_dag.csv" ),
    fname_deaths = normpath( homedir(), "data/Statens-Serum-Institut/dashboard/Regionalt_DB/07_antal_doede_pr_dag_pr_region.csv" )
)

    data["cases"] = CSV.File( fname_cases ) |>
        DataFrame |> process_ssi

    data["hospit"] = CSV.File( fname_hospit ) |>
        DataFrame |> process_ssi

    data["deaths"] = CSV.File( fname_deaths ) |>
        DataFrame |> process_ssi

    return nothing
end

# ============================================================================
#                        SERO-PREVALENCE
# ============================================================================

struct SeroParams{M,S,C,I,A,Y,P,N}
    means::M
    std::S
    CIs::C
    # dstd::D
    indexs::I
    dates::A
    delay::Y
    populations::P
    num_regions::N
end

function SeroParams!(data, sero=SeroInit(); later_dataset = false)
    @unpack dates_turing, dates, regions, populations = data

    df = readseroprev(;later_dataset)
    s  = isempty(sero.obs_start) ? dates[1] : Date(sero.obs_start)
    e  = isempty(sero.obs_stop) ? dates[end] : Date(sero.obs_stop)
    df = df[ s .<= df.date .<= e, :]
    data["seroprev"] = df

    means  = Vector{Vector{Float64}}()
    CIs    = Vector{Vector{Tuple{Float64,Float64}}}()
    indexs = Vector{Vector{Int64}}()
    for (i,r) in enumerate(regions)
        index = [findfirst(==(d), dates_turing[i]) for d in df.date]
        push!( indexs, index )
        push!( means, df[:, "$(r)_mean"] )
        push!( CIs, df[:, "$(r)_CI"] )
    end

    params = (; means,
        std  = sero.std, CIs, indexs,
        dates = df.date,
        delay = sero.delay,
        populations = populations,
        num_regions = length(regions)
    )
    data["sero"] = means
    data["seromodel"] = SeroParams(params...)

    return nothing
end

function parsepercent(x::AbstractString)
    x = strip(x, ['%',')','('])
    x = replace(x, "," => ".")
    parse(Float64, x) / 100
end

function parseweek(w)
    y = w > 40 ? Date("2020") : Date("2021")
    y + Week(w) - Day(3) #center at day 4
end

parse_mean(s) = parsepercent(first(split(s)))

function parse_CI(s)
    s_m, s_cil, _, s_cir = split(s)
    μ = parsepercent(s_m)
    cil = parsepercent(s_cil)
    cir = parsepercent(s_cir)
    # σ = max( abs(μ - cil), abs(μ - cir)) / 1.96
    return (cil, cir) #σ
end

function readseroprev(; later_dataset = false)
    df = CSV.File(projectdir("data/seroprevalence/", "seroprev_early.csv"), delim="\t") |> DataFrame
    for r in regions
        col = df[:,r]
        df[!,"$(r)_CI"] = parse_CI.(col)
        df[!,"$(r)_mean"] = parse_mean.(col)
    end
    return df
end

# ============================================================================
# observation model defaults
# ============================================================================

@kwdef struct CaseInit <: CovidSurvey.ObservationInit
    obs_start        ::String  = ""
    obs_stop         ::String  = ""
    delay_dist                 = NegativeBinomial2
    delay_length     ::Int64   = 40
    delay_dispersion ::Float64 = 5.41
    populations      ::Vector{Int64} = Regional.populations
end

@kwdef struct HospitInit <: CovidSurvey.ObservationInit
    obs_start        ::String  = ""
    obs_stop         ::String  = ""
    delay_dist                 = NegativeBinomial2
    delay_length     ::Int64   = 40
    delay_dispersion ::Float64 = 5.41
    populations      ::Vector{Int64} = Regional.populations
end

@kwdef struct DeathInit <: CovidSurvey.ObservationInit
    obs_start        ::String  = ""
    obs_stop         ::String  = ""
    delay_dist                 = NegativeBinomial2
    delay_length     ::Int64   = 60
    delay_dispersion ::Float64 = 14.26
    populations      ::Vector{Int64} = Regional.populations
end

@kwdef struct SeroInit# <: ObservationInit
    obs_start        ::String  = ""
    obs_stop         ::String  = "2020-07-01"
    delay            ::Int64   = 0
    std              ::Float64 = 1.5
    # population       ::Int64   = National.populations
    # dist                       = InverseGamma2
    # cv               ::Float64 = 0.1
end

ObservParams!(data, o::CaseInit)   = data["casemodel"]   = CovidSurvey.ObservParams(data, o)
ObservParams!(data, o::HospitInit) = data["hospitmodel"] = CovidSurvey.ObservParams(data, o)
ObservParams!(data, o::DeathInit)  = data["deathmodel"]  = CovidSurvey.ObservParams(data, o)

# ============================================================================

function lockdown!(data, lockdown, include_early_dynamic)
    @unpack dates_turing, num_regions = data
    include_early_dynamic && (data["lockdown_indices"] = fill(0, num_regions); return)
    li = Vector{Int64}()
    for d in dates_turing
        i = findfirst(==(Date(lockdown)), d)
        push!(li, i)
    end
    data["lockdown_indices"] = li
    return nothing
end

# ===============================================================================
#                           main function
# ===============================================================================

function load_data(;
    observationsend  = nothing,
    predictors       = nothing,
    rwstep           = 1,
    epidemicstart    = 30,
    epidemic_threshold = 30,
    numimpute        = 6,
    casemodel         = CaseInit(),
    hospitmodel       = HospitInit(),
    deathmodel        = DeathInit(),
    seromodel         = SeroInit(),
    link              = KLogistic(3.),
    invlink           = KLogit(3.),
    lockdown          = "2020-03-18",
    predictive        = false,
    include_early_dynamic = false,
    covariates_kwargs = Dict(
        :semiparametric => false,
        :fname => normpath( homedir(), "data/covidsurvey/smoothed_contacts.csv" ),
        :shift => 0,
        :startdate => nothing,
        :enddate => nothing,
        :aggregation => 1,
        :mobility => nothing,
        :standardize => false,
        :normalize => true,
    )
)

    data = Dict{String, Any}()
    regions!(data)
    observables!(data)
    CovidSurvey.consistent!(data; epidemicstart, epidemic_threshold)
    CovidSurvey.turingformat!(data, observationsend, predictive)
    # cases_start_date!(data, cases_start)
    CovidSurvey.covariates!(data, predictors; covariates_kwargs...)
    lockdown!(data, lockdown, include_early_dynamic)
    CovidSurvey.randomwalk!(data, rwstep, covariates_kwargs)
    ObservParams!(data, casemodel)
    ObservParams!(data, hospitmodel)
    ObservParams!(data, deathmodel)
    SeroParams!(data, seromodel)

    # iar_idx = get_iar_idx(num_tot, num_observations, cases_start_idx, iar_step)
    # num_iar_steps = length(unique(iar_idx))

    turing_data = (
        num_impute                  = numimpute,
        num_total_days              = data["num_tot"],
        dates                       = data["dates_turing"],
        cases                       = data["cases_turing"],
        casemodel                   = data["casemodel"],
        deaths                      = data["deaths_turing"],
        deathmodel                  = data["deathmodel"],
        epidemic_start              = epidemicstart,
        populations                 = populations,
        serial_interval             = CovidSurvey.serialinterval(15),#padzeros(serialinterval(30), 0, num_tot), #data_usa.turing_data.serial_intervals[1:50],
        num_si                      = 15,
        lockdown_indices            = data["lockdown_indices"],
        # cases_start_indices         = data["cases_start_idx"],
        # num_case_observations       = data["num_case_obs"],
        hospits                     = data["hospit_turing"],
        hospitmodel                 = data["hospitmodel"],
        num_rt_steps                = data["num_rt_steps"],
        rt_step_indices             = data["rt_step_index"],
        num_regions                 = length(regions),
        num_observations            = data["num_observations"],
        link                        = link,
        invlink                     = invlink,
        seromodel                   = data["seromodel"],
        sero                        = data["sero"],
        num_covariates              = data["num_covariates"],
        covariates                  = data["covariates"],
        covariates_starts           = data["covariates_starts"],
        semiparametric              = covariates_kwargs[:semiparametric],
        include_early_dynamic       = include_early_dynamic,
        rwscale                     = sqrt(rwstep),
        # num_i2h                     = 40,
        # ϕ_i2h                       = 5.41,
        # num_i2d                     = 60,
        # ϕ_i2d                       = 14.26,
        # num_i2c                     = 40,
        # ϕ_i2c                       = 5.41,
    )

    dates = [ collect(d:Day(1):d+Day(n-1)) for (d, n) in zip(data["startdates"], data["num_tot"])]
    startdates = [d[epidemicstart] for d in data["dates_turing"]]

    CovidSurvey.Data(
        turing_data,
        data["cases"],
        data["deaths"],
        data["hospit"],
        data["num_observations"], #num_observations
        data["num_tot"],
        dates,
        populations,
        predictors,
        data["seroprev"],
        regions,
        lockdown,
        data["observationsend"],
        nothing,
        startdates
    )
end

max_stars_repo_name: andreaskoher/CovidSurvey

max_issues_repo_name: andreaskoher/CovidSurvey

max_forks_repo_name: andreaskoher/CovidSurvey


text:
module SingleFieldFESpacesTests

using Test
using Gridap.Arrays
using Gridap.TensorValues
using Gridap.ReferenceFEs
using Gridap.Geometry
using Gridap.CellData
using Gridap.FESpaces

domain =(0,1,0,1,0,1)
partition = (3,3,3)
model = CartesianDiscreteModel(domain,partition)
Ω = Triangulation(model)

order = 2
reffe = ReferenceFE(lagrangian,Float64,order)
V0 = FESpace(model,reffe,dirichlet_tags=["tag_24","tag_25"])

cellmat = [rand(4,4) for cell in 1:num_cells(model)]
cellvec = [rand(4) for cell in 1:num_cells(model)]
cellmatvec = pair_arrays(cellmat,cellvec)
test_single_field_fe_space(V0,cellmatvec,cellmat,cellvec,Ω)

f(x) = sin(4*pi*(x[1]-x[2]^2))+1

fh = interpolate_everywhere(f, V0)

fh = interpolate_dirichlet(f, V0)

dirichlet_values = compute_dirichlet_values_for_tags(V0,[1,2])

r=[2,2,1,1,2,2,1,1,2,2,2,2,1,2,1,1,1,1,2,2,2,2,1,2,1,1,1,1,2,2,1,1,2,2,1,2,1,1,2,2,1,2,1,1,2,2,1,2,1,1]

@test dirichlet_values == r

dirichlet_values = compute_dirichlet_values_for_tags(V0,2)
@test dirichlet_values == fill(2,num_dirichlet_dofs(V0))

free_values = zero_free_values(V0)
uh = FEFunction(V0,free_values,dirichlet_values)

# With complex dofs values
V0 = FESpace(model,reffe,dirichlet_tags=["tag_24","tag_25"],vector_type=Vector{ComplexF64})
@test get_vector_type(V0) == Vector{ComplexF64}
f(x) = x[1] + x[2]*im
fh = interpolate(f, V0)
fh = interpolate_dirichlet(f, V0)
fh = interpolate_everywhere(f, V0)

f_real(x) = real(f(x))
f_imag(x) = imag(f(x))
f_conj(x) = conj(f(x))

dΩ = Measure(Ω,2)

tol = 1e-9
@test sqrt(sum(∫( abs2(f - fh) )*dΩ)) < tol
@test sqrt(sum(∫( abs2(f_imag - imag(fh)) )*dΩ)) < tol
@test sqrt(sum(∫( abs2(f_real - real(fh)) )*dΩ)) < tol
@test sqrt(sum(∫( abs2(f_conj - conj(fh)) )*dΩ)) < tol
@test sqrt(sum(∫( abs2(real(f - fh)) )*dΩ)) < tol
@test sqrt(sum(∫( abs2(imag(f - fh)) )*dΩ)) < tol
@test sqrt(sum(∫( abs2(conj(f - fh)) )*dΩ)) < tol

zh = zero(V0)
@test isa(get_free_dof_values(zh),Vector{ComplexF64})

#using Gridap.Visualization
#trian = get_triangulation(model)
#writevtk(trian,"trian",cellfields=["fh_real"=>real(fh),"fh_imag"=>imag(fh)])

#using Gridap.Visualization
#
#trian = get_triangulation(model)
#
#writevtk(model,"model")
#
#writevtk(trian,"trian",cellfields=["fh" => fh, "uh" => uh])

#
#
#strian = SkeletonTriangulation(model)
#
#fh_Γ = restrict(get_array(fh),strian)
#
#writevtk(strian,"strian",nsubcells=10,cellfields=["jump_fh" => jump(fh_Γ)])

end # module


text:
info("Test read bam file")
data = HTSLIB.readlines("data/100.bam")
bios = HTSLIB.open("data/100.bam","rb","bam")
while !HTSLIB.eof(bios)
    line = HTSLIB.readline(bios)
end
#@show data
info("Read bam successfully")

info("Test write bam file")

fw = HTSLIB.open("data/test_write.bam","wb","bam")
fr = HTSLIB.open("data/100.bam","rb","bam")

fw.phdr = HTSLIB.sam_hdr_parse(HTSLIB.strptr(fr.phdr))

HTSLIB.writelines(fw,data)

HTSLIB.close(fw)
HTSLIB.close(fr)

info("writelines success")


text:
Base.length(op::SimpleOP) = length(op.r)
Base.length(op::OPCSP) = length(op.r)
distance(op, path::Vector{Int}) = sum([op.distances[path[i],path[i+1]] for i in 1:length(path)-1])

function find_distances(positions)
    distances = Array(Float64, length(positions), length(positions))
    for i in 1:length(positions)
        for j in 1:length(positions)
            distances[i,j] = norm(positions[i]-positions[j])
        end
    end
    return distances
end

within_range(op, start, stop, j, dist) = op.distances[start,j] + op.distances[j,stop] <= dist
within_range(op, j) = within_range(op, op.start, op.stop, j, op.distance_limit)

# code for quickly finding similar states
type SimilarStateStruct{T}
    feasible::BitArray{1}
    exact::Dict{Vector{Float64}, Int}
    vec::Vector{T}
    tree::KDTree
end

function SimilarStateStruct{T}(op, s, object::T)
    feasible = falses(length(op))
    for j in s.open
        if j != op.stop && within_range(op, s.i, op.stop, j, s.remaining)
            feasible[j] = true
        end
    end
    relevant = get_d(s)[feasible]
    tree = KDTree(reshape(relevant,sum(feasible),1),Cityblock())
    return SimilarStateStruct{T}(feasible,
                                 Dict{Vector{Float64}, Int}(relevant=>1),
                                 T[object],
                                 tree)
end

typealias SimilarStateDict{T} Dict{Tuple{Int,Float64,IntSet}, SimilarStateStruct{T}}

type NearestStateMap{T}
    data::SimilarStateDict{T}
    op::Union{OPCSP, OPCSPBeliefMDP}

    NearestStateMap(d, op) = new(d,op)
    NearestStateMap() = new()
end
NearestStateMap{T}(t::Type{T}, op) = NearestStateMap{T}(SimilarStateDict{T}(), op)

function has_similar(m::NearestStateMap, s)
    return haskey(m.data, (s.i, s.remaining, s.open))
end

function get_nearest(m::NearestStateMap, s)
    similar = m.data[(s.i, s.remaining, s.open)]
    relevant = get_d(s)[similar.feasible]
    ind = get(similar.exact, relevant, -1)
    if ind > 0
        return (similar.vec[ind],0.0)
    end
    idxs, dists = knn(similar.tree, relevant, 1)
    return (similar.vec[idxs[1]], dists[1])
end

function Base.insert!{T}(m::NearestStateMap{T}, s, object::T)
    key = (s.i, s.remaining, s.open)
    if haskey(m.data, key)
        similar = m.data[key]
    else
        similar = m.data[key] = SimilarStateStruct(m.op, s, object)
        return true
    end
    relevant = get_d(s)[similar.feasible]
    if haskey(similar.exact, relevant)
        @assert similar.exact[relevant] == object
        return false
    end
    push!(similar.vec, object)
    similar.exact[relevant] = length(similar.vec)
    newdata = hcat(similar.tree.data, relevant)
    similar.tree = KDTree(newdata, Cityblock())
    return true
end


text:
module FizzBuzzMultipleDispatch

export fizzbuzz

# Define traits

abstract type Divisibility end

struct IsDivBy3 <: Divisibility end
struct NotDivBy3 <: Divisibility end

struct IsDivBy5 <: Divisibility end
struct NotDivBy5 <: Divisibility end

# Determine divisibility traits

div3trait(x) = x % 3 == 0 ? IsDivBy3() : NotDivBy3()
div5trait(x) = x % 5 == 0 ? IsDivBy5() : NotDivBy5()

# Dispatch

fizzbuzz(x) = fizzbuzz(div3trait(x), div5trait(x), x)

fizzbuzz(::IsDivBy3, ::NotDivBy5, x)  = "Fizz"
fizzbuzz(::NotDivBy3, ::IsDivBy5, x)  = "Buzz"
fizzbuzz(::IsDivBy3, ::IsDivBy5, x)   = "FizzBuzz"
fizzbuzz(::NotDivBy3, ::NotDivBy5, x) = string(x)

end