text:
# Stocks/Fundamentals

"""
    balance_sheet(conn::Connection, symbol::String; period::String="quarter",
        last::Int=1)

Pulls balance sheet data. Available quarterly
or annually with the default being the last available quarter.
"""
function balance_sheet(
    conn::Connection,
    symbol::String;
    period::String = "quarter",
    last::Int = 1,
)

    path_params = [:symbol]
    return get_stocks(
        conn,
        "balance-sheet",
        path_params;
        symbol = symbol,
        period = period,
        last = last
    )
end

"""
    cashflow(conn::Connection, symbol::String; period::String="quarter",
        last::Int=1)

Pulls cash flow data. Available quarterly
    or annually, with the default being the last available quarter.
"""
function cashflow(
    conn::Connection,
    symbol::String;
    period::String = "quarter",
    last::Int = 1,
)
    path_params = [:symbol]
    return get_stocks(
        conn,
        "cash-flow",
        path_params;
        symbol = symbol,
        period = period,
        last = last
    )
end

"""
    dividends(conn::Connection, symbol::String; daterange::String="1m")

Provides basic dividend data for US equities, ETFs,
and Mutual Funds for the last 5 years. For 13+ years
of history and comprehensive data, use the
[Advanced Dividends](https://iexcloud.io/docs/api/#dividends)
endpoint.
"""
function dividends(conn::Connection, symbol::String; range::String = "1m")
    path_params = [:symbol, :range]
    return get_stocks(
        conn,
        "dividends",
        path_params;
        symbol = symbol,
        range=range
    )
end

"""
    earnings(conn::Connection, symbol::String; period::String="quarter",
        field::String="actualEPS", last::Int=1)

Earnings data for a given company including the actual EPS,
consensus, and fiscal period. Earnings are available quarterly
(last 4 quarters) and annually (last 4 years).
"""
function earnings(
    conn::Connection,
    symbol::String;
    period::String = "quarter",
    field::String = "actualEPS",
    last::Int = 1
)
    path_params = [:symbol, :last, :field]
    return get_stocks(
        conn,
        "earnings",
        path_params;
        symbol = symbol,
        last = last,
        field = field,
        period = period
    )
end

"""
    income(conn::Connection, symbol::String; period::String="quarter",
        last::Int=1)

Earnings data for a given company including the actual EPS,
consensus, and fiscal period. Earnings are available quarterly
(last 4 quarters) and annually (last 4 years).
"""
function income(
    conn::Connection,
    symbol::String;
    period::String = "quarter",
    last::Int = 1,
)
    path_params = [:symbol]
    return get_stocks(
        conn,
        "financials",
        path_params;
        symbol = symbol,
        period = period,
        last = last
    )
end

"""
    splits(conn::Connection, symbol::String; daterange="1m")

Return split information about symbol. Response attributes are:

| Key          | Type   | Description                                                                                |
|--------------|--------|--------------------------------------------------|
| exDate       | string | refers to the split ex-date                                                                                |
| declaredDate | string | refers to the split declaration date                                                                                |
| ratio        | number | refers to the split ratio.  The split ratio is an
|              |        | inverse of the  number of shares that a holder of
|              |        | the stock would have after the split  divided by the
|              |        | number of shares that the holder had before.
|              |        | For example:  Split ratio of .5 = 2 for 1 split.
| toFactor     | string | To factor of the split. Used to calculate the split
|              |        | ratio fromfactor/tofactor = ratio (eg ½ = 0.5)                                                                                |
| fromFactor   | string | From factor of the split. Used to calculate the split
|              |        | ratio fromfactor/tofactor = ratio (eg ½ = 0.5)                                                                                |
| description  | string | Description of the split event.                                                                                |

"""
function splits(conn::Connection, symbol::String; range="next")
    path_params = [:symbol, :range]
    return get_stocks(
        conn,
        "splits",
        path_params;
        symbol = symbol,
        range = range
    )
end

max_stars_repo_name: GodolphinCapitalManagement/Iex

max_issues_repo_name: GodolphinCapitalManagement/Iex

max_forks_repo_name: GodolphinCapitalManagement/Iex


text:
function Fract_PT_G_reduce_SLS()
    dt = 0.001
    # NaN at 0.0 due to InverseLaplace so start at dt
    t = collect(dt:dt:10.0)

    fracpt = Fract_PT.Ga(t, [2.0, 1.0, 1.0, 0.0, 0.5, 0.0])
    stanSLS = SLS_PT.Ga(t, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracpt[i], stanSLS[i]), eachindex(t))
end
@test Fract_PT_G_reduce_SLS()

function FractSLS_PT_G_reduce_SLS()
    dt = 0.001
    t = collect(0.0:dt:10.0)

    fracSLS = FractSLS_PT.Ga(t, [2.0, 1.0, 1.0, 0.5])
    stanSLS = SLS_PT.Ga(t, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracSLS[i], stanSLS[i]), eachindex(t))
end
@test FractSLS_PT_G_reduce_SLS()

function Fract_PT_J_reduce_SLS(tol)
    dt = 0.001
    t = collect(0.0:dt:10.0)

    fracpt = Fract_PT.Ja(t, [2.0, 0.999, 1.0, 0.0, 0.5, 0.0])
    stanSLS = SLS_PT.Ja(t, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracpt[i], stanSLS[i], atol=tol), eachindex(t))
end
@test Fract_PT_J_reduce_SLS(tol)

function FractSLS_PT_J_reduce_SLS(tol)
    dt = 0.001
    t = collect(0.0:dt:10.0)

    fracSLS = FractSLS_PT.Ja(t, [2.0, 0.999, 1.0, 0.5])
    stanSLS = SLS_PT.Ja(t, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracSLS[i], stanSLS[i], atol=tol), eachindex(t))
end
@test FractSLS_PT_J_reduce_SLS(tol)

function Fract_PT_Gp_reduce_SLS()
    ω_step = 0.01
    chirp = collect(0.0:ω_step:10000.0)

    fracpt = Fract_PT.Gpa(chirp, [2.0, 1.0, 1.0, 0.0, 0.5, 0.0])
    stanSLS = SLS_PT.Gpa(chirp, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracpt[i], stanSLS[i]), eachindex(chirp))
end
@test Fract_PT_Gp_reduce_SLS()

function FractSLS_PT_Gp_reduce_SLS()
    ω_step = 0.01
    chirp = collect(0.0:ω_step:10000.0)

    fracSLS = FractSLS_PT.Gpa(chirp, [2.0, 1.0, 1.0, 0.5])
    stanSLS = SLS_PT.Gpa(chirp, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracSLS[i], stanSLS[i]), eachindex(chirp))
end
@test FractSLS_PT_Gp_reduce_SLS()

function Fract_PT_Gpp_reduce_SLS()
    ω_step = 0.01
    chirp = collect(0.0:ω_step:10000.0)

    fracpt = Fract_PT.Gppa(chirp, [2.0, 1.0, 1.0, 0.0, 0.5, 0.0])
    stanSLS = SLS_PT.Gppa(chirp, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracpt[i], stanSLS[i]), eachindex(chirp))
end
@test Fract_PT_Gpp_reduce_SLS()

function FractSLS_PT_Gpp_reduce_SLS()
    ω_step = 0.01
    chirp = collect(0.0:ω_step:10000.0)

    fracSLS = FractSLS_PT.Gppa(chirp, [2.0, 1.0, 1.0, 0.5])
    stanSLS = SLS_PT.Gppa(chirp, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracSLS[i], stanSLS[i]), eachindex(chirp))
end
@test FractSLS_PT_Gpp_reduce_SLS()

function Fract_PT_G_reduce_Jeffreys(tol)
    dt = 0.001
    # NaN at 0.0 due to Gamma function so start at dt
    t = collect(dt:dt:10.0)

    fracpt = Fract_PT.Ga(t, [2.0, 1.0, 1.0, 0.0, 0.5, 1.0])
    stanJef = Jeffreys_PT.Ga(t, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracpt[i], stanJef[i], atol=tol), eachindex(t))
end
@test Fract_PT_G_reduce_Jeffreys(tol)

function FractJeffreys_PT_G_reduce_Jeffreys()
    dt = 0.001
    t = collect(0.0:dt:10.0)

    fracJef = FractJeffreys_PT.Ga(t, [2.0, 1.0, 0.0, 0.5])
    stanJef = Jeffreys_PT.Ga(t, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracJef[i], stanJef[i]), eachindex(t))
end
@test FractJeffreys_PT_G_reduce_Jeffreys()

function Fract_PT_J_reduce_Jeffreys(tol)
    dt = 0.001
    t = collect(0.0:dt:10.0)

    fracpt = Fract_PT.Ja(t, [2.0, 0.999, 1.0, 0.0, 0.5, 1.0])
    stanJef = Jeffreys_PT.Ja(t, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracpt[i], stanJef[i], atol=tol), eachindex(t))
end
@test Fract_PT_J_reduce_Jeffreys(tol)

function FractJeffreys_PT_J_reduce_Jeffreys(tol)
    dt = 0.001
    t = collect(0.0:dt:10.0)

    fracJef = FractJeffreys_PT.Ja(t, [2.0, 1.0, 0.001, 0.5])
    stanJef = Jeffreys_PT.Ja(t, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracJef[i], stanJef[i], atol=tol), eachindex(t))
end
@test FractJeffreys_PT_J_reduce_Jeffreys(tol)

function Fract_PT_Gp_reduce_Jeffreys()
    ω_step = 0.01
    chirp = collect(0.0:ω_step:10000.0)

    fracpt = Fract_PT.Gpa(chirp, [2.0, 1.0, 1.0, 0.0, 0.5, 1.0])
    stanJef = Jeffreys_PT.Gpa(chirp, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracpt[i], stanJef[i]), eachindex(chirp))
end
@test Fract_PT_Gp_reduce_Jeffreys()

function FractJeffreys_PT_Gp_reduce_Jeffreys()
    ω_step = 0.01
    chirp = collect(0.0:ω_step:10000.0)

    fracJef = FractJeffreys_PT.Gpa(chirp, [2.0, 1.0, 0.0, 0.5])
    stanJef = Jeffreys_PT.Gpa(chirp, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracJef[i], stanJef[i]), eachindex(chirp))
end
@test FractJeffreys_PT_Gp_reduce_Jeffreys()

function Fract_PT_Gpp_reduce_Jeffreys()
    ω_step = 0.01
    chirp = collect(0.0:ω_step:10000.0)

    fracpt = Fract_PT.Gppa(chirp, [2.0, 1.0, 1.0, 0.0, 0.5, 1.0])
    stanJef = Jeffreys_PT.Gppa(chirp, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracpt[i], stanJef[i]), eachindex(chirp))
end
@test Fract_PT_Gpp_reduce_Jeffreys()

function FractJeffreys_PT_Gpp_reduce_Jeffreys()
    ω_step = 0.01
    chirp = collect(0.0:ω_step:10000.0)

    fracJef = FractJeffreys_PT.Gppa(chirp, [2.0, 1.0, 0.0, 0.5])
    stanJef = Jeffreys_PT.Gppa(chirp, [2.0, 1.0, 0.5])

    all(i -> isapprox(fracJef[i], stanJef[i]), eachindex(chirp))
end
@test FractJeffreys_PT_Gpp_reduce_Jeffreys()

ext: jl


text:
using Elvur
using Test

@testset "Elvur.jl" begin
end


text:
using IntelGEMM
using LinearAlgebra
using Test

for T in (Float32, Float64)
    A = T[2 1; 4 3]
    Bv = T[1; 2]
    B = reshape(Bv, :, 1)
    Cv = T[4; 10]
    C = reshape(Cv, :, 1)
    @test A * Bv  == Cv
    @test A * B == C
    eigen(Symmetric(A))
    eigen(A)
end


text:
# unroll
@inline Base.Broadcast.broadcastable(u::Unroll) = (u,)

"""
Returns a vector of expressions for a set of unrolled indices.

"""
function unrolled_indicies(D::Int, AU::Int, F::Int, N::Int, AV::Int, W::Int, X::Int)
    baseind = Expr(:tuple)
    for d in 1:D
        i = Expr(:call, :Zero)
        if d == AV && W > 1
            i = Expr(:call, Expr(:curly, :MM, W, X), i)
        end
        push!(baseind.args, i)
    end
    inds = Vector{Expr}(undef, N)
    inds[1] = baseind
    for n in 1:N-1
        ind = copy(baseind)
        i = Expr(:call, Expr(:curly, :StaticInt, n*F))
        if AU == AV && W > 1
            i = Expr(:call, Expr(:curly, :MM, W, X), i)
        end
        ind.args[AU] = i
        inds[n+1] = ind
    end
    inds
end

# This creates a generic expression that simply calls `vload` for each of the specified `Unroll`s without any fanciness.
function vload_unroll_quote(
    D::Int, AU::Int, F::Int, N::Int, AV::Int, W::Int, M::UInt, X::Int, mask::Bool, align::Bool, rs::Int, vecunrollmask::Bool
)
    t = Expr(:tuple)
    inds = unrolled_indicies(D, AU, F, N, AV, W, X)
    # TODO: Consider doing some alignment checks before accepting user's `align`?
    alignval = Expr(:call, align ? :True : :False)
    rsexpr = Expr(:call, Expr(:curly, :StaticInt, rs))
    q = quote
        $(Expr(:meta, :inline))
        gptr = similar_no_offset(sptr, gep(pointer(sptr), data(u)))
    end
    if vecunrollmask
        push!(q.args, :(masktup = data(vm)))
    end
    gf = GlobalRef(Core, :getfield)
    for n in 1:N
        l = Expr(:call, :_vload, :gptr, inds[n])
        if vecunrollmask
            push!(l.args, :($gf(masktup, $n, false)))
        elseif mask && (M % Bool)
            push!(l.args, :sm)
        end
        push!(l.args, alignval, rsexpr)
        M >>= 1
        push!(t.args, l)
    end
    push!(q.args, :(VecUnroll($t)))
    q
end
# so I could call `linear_index`, then
# `IT, ind_type, W, X, M, O = index_summary(I)`
# `gesp` to set offset multiplier (`M`) and offset (`O`) to `0`.
# call, to build extended load quote (`W` below is `W*N`):
# vload_quote_llvmcall(
#     T_sym::Symbol, I_sym::Symbol, ind_type::Symbol, W::Int, X::Int, M::Int, O::Int, mask::Bool, align::Bool, rs::Int, ret::Expr
# )

function interleave_memory_access(AU, C, F, X, UN, size_T, B)
    ((((AU == C) && (C > 0)) && (F == 1)) && (X == (UN*size_T)) && (B < 1))
end

# if either
# x = rand(3,L);
# foo(x[1,i],x[2,i],x[3,i])
# `(AU == 1) & (AV == 2) & (F == 1) & (stride(p,2) == N)`
# or
# x = rand(3L);
# foo(x[3i - 2], x[3i - 1], x[3i   ])
# Index would be `(MM{W,3}(1),)`
# so we have `AU == AV == 1`, but also `X == N == F`.
function shuffle_load_quote(
    ::Type{T}, N, C, B, AU, F, UN, AV, W, X, ::Type{I}, align::Bool, rs::Int, MASKFLAG::UInt
) where {T,I}
    IT, ind_type, _W, _X, M, O = index_summary(I)
    # we don't require vector indices for `Unroll`s...
    # @assert _W == W "W from index $(_W) didn't equal W from Unroll $W."
    mask = MASKFLAG ≠ zero(UInt)
    if mask && ((MASKFLAG & ((one(UInt) << UN) - one(UInt))) ≠ ((one(UInt) << UN) - one(UInt)))
        return nothing
        # throw(ArgumentError("`shuffle_load_quote` currently requires masking either all or none of the unrolled loads."))
    end
    size_T = sizeof(T)
    # We need to unroll in a contiguous dimension for this to be a shuffle store, and we need the step between the start of the vectors to be `1`
    ((AV > 0) && interleave_memory_access(AU, C, F, X, UN, size_T, B)) || return nothing
    # `X` is stride between indices, e.g. `X = 3` means our final vectors should be `<x[0], x[3], x[6], x[9]>`
    # We need `X` to equal the steps (the unrolling factor)
    Wfull = W * UN
    T_sym = JULIA_TYPES[T]
    I_sym = JULIA_TYPES[IT]
    vloadexpr = vload_quote_llvmcall(
        T_sym, I_sym, ind_type, Wfull, size_T, M, O, mask, align, rs, :(_Vec{$Wfull,$T_sym})
    )
    q = quote
        $(Expr(:meta,:inline))
        ptr = pointer(sptr)
        i = data(u)
    end
    mask && push!(q.args, :(m = mask(StaticInt{$Wfull}(), $UN * getfield(sm, :evl))))
    push!(q.args, :(v = $vloadexpr))
    vut = Expr(:tuple)
    for n ∈ 0:UN-1
        shufftup = Expr(:tuple)
        for w ∈ 0:W-1
            push!(shufftup.args, n + UN*w)
        end
        push!(vut.args, :(shufflevector(v, Val{$shufftup}())))
    end
    push!(q.args, Expr(:call, :VecUnroll, vut))
    q
end
function init_transpose_memop_masking!(q::Expr, M::UInt, N::Int, evl::Bool)
    domask = M ≠ zero(UInt)
    if domask
        if (M & ((one(UInt) << N) - one(UInt))) ≠ ((one(UInt) << N) - one(UInt))
            throw(ArgumentError("`vload_transpose_quote` currently requires masking either all or none of the unrolled loads."))
        end
        if evl
            push!(q.args, :(_evl = getfield(sm, :evl)))
        else
            push!(q.args, :(u_1 = getfield(sm, :u)))
        end
    end
    domask
end
function push_transpose_mask!(q::Expr, mq::Expr, domask::Bool, n::Int, npartial::Int, w::Int, W::Int, evl::Bool, RS::Int, mask::UInt)
    Utyp = mask_type_symbol(n)
    if domask
        mw_w = Symbol(:mw_,w)
        if evl
            mm_evl_cmp = Symbol(:mm_evl_cmp_,n)
            if w == 1
                isym = integer_of_bytes_symbol(min(4, RS ÷ n))
                vmmtyp = :(VectorizationBase._vrange(Val{$n}(), $isym, Val{0}(), Val{1}()))
                push!(q.args, :($mm_evl_cmp = $vmmtyp))
                push!(q.args, :($mw_w = _evl*$(UInt32(n)) > $mm_evl_cmp))
            else
                push!(q.args, :($mw_w = ((_evl*$(UInt32(n)) - $(UInt32(n*(w-1))))%Int32) > ($mm_evl_cmp)))
            end
            if n == npartial
                push!(mq.args, mw_w )
            else
                push!(mq.args, :(Mask{$n}($mask % $Utyp) & $mw_w ))
            end
        else
            push!(q.args, :($mw_w = Core.ifelse($(Symbol(:u_,w)) % Bool, $mask % $Utyp, zero($Utyp))))
            if w < W
                push!(q.args, Expr(:(=), Symbol(:u_,w+1), Expr(:call, :(>>>), Symbol(:u_,w), 1)))
            end
            push!(mq.args, :(Mask{$n}( $mw_w )))
        end
    elseif n ≠ npartial
        push!(mq.args, :(Mask{$n}( $mask % $Utyp )))
    end
    nothing
end

function vload_transpose_quote(D::Int,AU::Int,F::Int,N::Int,AV::Int,W::Int,X::Int,align::Bool,RS::Int,st::Int,M::UInt,evl::Bool)
    ispow2(W) || throw(ArgumentError("Vector width must be a power of 2, but recieved $W."))
    isone(F) || throw(ArgumentError("No point to doing a transposed store if unroll step factor $F != 1"))
    C = AU # the point of tranposing
    q = Expr(:block, Expr(:meta,:inline), :(gptr = similar_no_offset(sptr, gep(pointer(sptr), data(u)))))
    domask = init_transpose_memop_masking!(q, M, N, evl)
    alignval = Expr(:call, align ? :True : :False)
    rsexpr = Expr(:call, Expr(:curly, :StaticInt, RS))
    vut = Expr(:tuple)
    # vds = Vector{Symbol}(undef, N)
    # for n ∈ 1:N
    #     vds[n] = vdn = Symbol(:vd_,n)
    #     push!(q.args, :($vdn = getfield(vd, $n, false)))
    # end
    # AU = 1, AV = 2, N = 3, W = 8, M = 0x7 (<1,1,1,0>), mask unknown, hypothetically 0x1f <1 1 1 1 1 0 0 0>
    # load 5 vetors of length 3, replace last 3 with undef
    i = 0
    Wmax = RS ÷ st
    while N > 0
        # for store, we do smaller unrolls first.
        # for load, we start with larger unrolls.
        # The idea is that this is probably a better order to make use of execution resources.
        # For load, we want to begin immediately issuing loads, but then we'd also like to do other work, e.g. shuffles at the same time.
        # Before issueing the loads, register pressure is also probably low, and it will be higher towards the end. Another reason to do transposes early.
        # For stores, we also want to begin immediately issueing stores, so we start with smaller unrolls so that there is less work
        # to do beforehand. This also immediately frees up registers for use while transposing, in case of register pressure.
        if N ≥ Wmax
            npartial = n = Wmax
            mask = -1 % UInt
        else
            npartial = N
            n = nextpow2(npartial)
            # if npartial == n
            #     mask = -1 % UInt
            # else
            mask = (one(UInt) << (npartial)) - one(UInt)
            # end
        end
        N -= npartial
        if n == 1
            # this can only happen on the first iter, so `StaticInt{0}()` is fine
            ind = Expr(:tuple)
            for d ∈ 1:D
                if AV == d
                    push!(ind.args, :(MM{$W,$X}(StaticInt{0}())))
                elseif AU == d
                    push!(ind.args, :(StaticInt{$i}()))
                else
                    push!(ind.args, :(StaticInt{0}()))
                end
            end
            loadq = :(_vload(gptr, $ind))
            # we're not shuffling when `n == 1`, so we just push the mask
            domask && push!(loadq.args, :sm)
            push!(loadq.args, alignval, rsexpr)
            push!(q.args, :(vl_1 = $loadq))
            push!(vut.args, :vl_1)
        elseif W == 1
            loadq = loadq_expr!(q,D,AU,AV,n,i,X,W,W,domask,npartial,evl,RS,mask,alignval,rsexpr)
            loadsym = Symbol(:vloadw1_, i, :_, n)
            push!(q.args, Expr(:(=), loadsym, loadq))
            for nn ∈ 1:npartial
                push!(vut.args, :(extractelement($loadsym, $(nn-1))))
            end
        else
            # dname is a `VecUnroll{(W-1),N}`
            t = Expr(:tuple)
            dname = Symbol(:vud_,i,:_,n)
            for w ∈ 1:W
                # if domask, these get masked
                loadq = loadq_expr!(q,D,AU,AV,n,i,X,w,W,domask,npartial,evl,RS,mask,alignval,rsexpr)
                push!(t.args, loadq)
            end
            push!(q.args, :($dname = data(transpose_vecunroll(VecUnroll($t)))))
            for nn ∈ 1:npartial
                extract = :(getfield($dname, $nn, false))
                push!(vut.args, extract)
            end
        end
        # M >>>= 1
        i += npartial
    end
    push!(q.args, :(VecUnroll($vut)))
    q
end
function loadq_expr!(q,D,AU,AV,n,i,X,w,W,domask,npartial,evl,RS,mask,alignval,rsexpr)
    ind = Expr(:tuple)
    for d ∈ 1:D
        if AU == d
            push!(ind.args, :(MM{$n}(StaticInt{$i}())))
        elseif AV == d
            push!(ind.args, :(StaticInt{$(X*(w-1))}()))
        else
            push!(ind.args, :(StaticInt{0}()))
        end
    end
    # transposing mask does what?
    loadq = :(_vload(gptr, $ind))
    push_transpose_mask!(q, loadq, domask, n, npartial, w, W, evl, RS, mask)
    push!(loadq.args, alignval, rsexpr)
    loadq
end

# @inline staticunrolledvectorstride(_, __) = nothing
# @inline staticunrolledvectorstride(::StaticInt{M}, ::StaticInt{X}) where {M,X} = StaticInt{M}() * StaticInt{X}()
# @inline staticunrolledvectorstride(sp::AbstractStridedPointer, ::Unroll{AU,F,UN,AV,W,M,X}) where {AU,F,UN,AV,W,M,X} = staticunrolledvectorstride(strides(ptr)[AV], StaticInt{X}())

@generated function staticunrolledvectorstride(sptr::T, ::Unroll{AU,F,UN,AV,W,M,X}) where {T,AU,F,UN,AV,W,M,X}
    AV > 0 || return nothing
    SM = T.parameters[AV]
    if SM <: StaticInt
        return Expr(:block, Expr(:meta,:inline), Expr(:call, *, Expr(:call, SM), Expr(:call, Expr(:curly, :StaticInt, X))))
    else
        return nothing
    end
end

function should_transpose_memop(F,C,AU,AV,UN,M)
    (F == 1) & (C == AU) & (C ≠ AV) || return false
    max_mask = (one(UInt) << UN) - one(UInt)
    (M == zero(UInt)) | ((max_mask & M) == max_mask)
end

function bitload(AU::Int,W::Int,AV::Int,F::Int,UN::Int,RS::Int,mask::Bool)
    if AU ≠ AV
        1 < W < 8 && throw(ArgumentError("Must unroll in vectorized direction for `Bit` loads with W < 8."))
        return
    end
    if (1 < W < 8) && F ≠ W
        throw(ArgumentError("Must take steps of size $W along unrolled and vectorized axis when loading from bits."))
    end
    loadq = :(__vload(pointer(sptr), MM{$(W*UN)}(ind)))
    mask && push!(loadq.args, :sm)
    push!(loadq.args, :(False()), :(StaticInt{$RS}()))
    quote
        $(Expr(:meta,:inline))
        ind = getfield(u,:i)
        VecUnroll(splitvectortotuple(StaticInt{$UN}(),StaticInt{$W}(), $loadq))
    end
end

@generated function _vload_unroll(
    sptr::AbstractStridedPointer{T,N,C,B}, u::Unroll{AU,F,UN,AV,W,M,UX,I}, ::A, ::StaticInt{RS}, ::StaticInt{X}
) where {T<:NativeTypes,N,C,B,AU,F,UN,AV,W,M,UX,I<:IndexNoUnroll,A<:StaticBool,RS,X}
    1+2
    if T === Bit
        bitlq = bitload(AU,W,AV,F,UN,RS,false)
        bitlq === nothing || return bitlq
    end
    align = A === True
    should_transpose = should_transpose_memop(F,C,AU,AV,UN,M)
    if (W == N) & ((sizeof(T)*W) == RS) & should_transpose
        return vload_transpose_quote(N,AU,F,UN,AV,W,UX,align,RS,sizeof(T),zero(UInt),false)
    end
    maybeshufflequote = shuffle_load_quote(T, N, C, B, AU, F, UN, AV, W, X, I, align, RS, zero(UInt))
    # `maybeshufflequote` for now requires `mask` to be `false`
    maybeshufflequote === nothing || return maybeshufflequote
    if should_transpose
        vload_transpose_quote(N,AU,F,UN,AV,W,UX,align,RS,sizeof(T),zero(UInt),false)
    else
        vload_unroll_quote(N, AU, F, UN, AV, W, M, UX, false, align, RS, false)
    end
end
@generated function _vload_unroll(
    sptr::AbstractStridedPointer{T,N,C,B}, u::Unroll{AU,F,UN,AV,W,M,UX,I}, ::A, ::StaticInt{RS}, ::Nothing
) where {T<:NativeTypes,N,C,B,AU,F,UN,AV,W,M,UX,I<:IndexNoUnroll,A<:StaticBool,RS}
    # 1+2
    # @show AU,F,UN,AV,W,M,UX,I
    if T === Bit
        bitlq = bitload(AU,W,AV,F,UN,RS,false)
        bitlq === nothing || return bitlq
    end
    should_transpose = should_transpose_memop(F,C,AU,AV,UN,M)
    if should_transpose
        vload_transpose_quote(N,AU,F,UN,AV,W,UX,A === True,RS,sizeof(T),zero(UInt),false)
    else
        vload_unroll_quote(N, AU, F, UN, AV, W, M, UX, false, A === True, RS, false)
    end
end
@generated function _vload_unroll(
    sptr::AbstractStridedPointer{T,D,C,B}, u::Unroll{AU,F,N,AV,W,M,UX,I}, sm::EVLMask{W}, ::A, ::StaticInt{RS}, ::StaticInt{X}
) where {A<:StaticBool,AU,F,N,AV,W,M,I<:IndexNoUnroll,T,D,RS,UX,X,C,B}
    if T === Bit
        bitlq = bitload(AU,W,AV,F,N,RS,true)
        bitlq === nothing || return bitlq
    end
    # 1+2
    should_transpose = should_transpose_memop(F,C,AU,AV,N,M)
    align = A === True
    if (W == N) & ((sizeof(T)*W) == RS) & should_transpose
        return vload_transpose_quote(D,AU,F,N,AV,W,UX,align,RS,sizeof(T),M,true)
    end
    maybeshufflequote = shuffle_load_quote(T, D, C, B, AU, F, N, AV, W, X, I, align, RS, M)
    maybeshufflequote === nothing || return maybeshufflequote
    if should_transpose
        return vload_transpose_quote(D,AU,F,N,AV,W,UX,align,RS,sizeof(T),M,true)
    end
    vload_unroll_quote(D, AU, F, N, AV, W, M, UX, true, align, RS, false)
end
@generated function _vload_unroll(
    sptr::AbstractStridedPointer{T,D,C}, u::Unroll{AU,F,N,AV,W,M,UX,I}, sm::AbstractMask{W}, ::A, ::StaticInt{RS}, ::Any
) where {A<:StaticBool,AU,F,N,AV,W,M,I<:IndexNoUnroll,T,D,RS,UX,C}
    1+2
    if T === Bit
        bitlq = bitload(AU,W,AV,F,N,RS,true)
        bitlq === nothing || return bitlq
    end
    align = A === True
    if should_transpose_memop(F,C,AU,AV,N,M)
        isevl = sm <: EVLMask
        return vload_transpose_quote(D,AU,F,N,AV,W,UX,align,RS,sizeof(T),M,isevl)
    end
    vload_unroll_quote(D, AU, F, N, AV, W, M, UX, true, align, RS, false)
end
# @generated function _vload_unroll(
#     sptr::AbstractStridedPointer{T,D}, u::Unroll{AU,F,N,AV,W,M,UX,I}, vm::VecUnroll{Nm1,W,B}, ::A, ::StaticInt{RS}, ::StaticInt{X}
# ) where {A<:StaticBool,AU,F,N,AV,W,M,I<:IndexNoUnroll,T,D,RS,UX,Nm1,B<:Union{Bool,Bit},X}
#     Nm1+1 == N || throw(ArgumentError("Nm1 + 1 = $(Nm1 + 1) ≠ $N = N"))
#     maybeshufflequote = shuffle_load_quote(T, N, C, B, AU, F, UN, AV, W, X, I, align, RS, 2)
#     maybeshufflequote === nothing || return maybeshufflequote
#     vload_unroll_quote(D, AU, F, N, AV, W, M, UX, true, A === True, RS, true)
# end
@generated function _vload_unroll(
    sptr::AbstractStridedPointer{T,D}, u::Unroll{AU,F,N,AV,W,M,UX,I}, vm::VecUnroll{Nm1,<:Any,B}, ::A, ::StaticInt{RS}, ::Any
) where {A<:StaticBool,AU,F,N,AV,W,M,I<:IndexNoUnroll,T,D,RS,UX,Nm1,B<:Union{Bool,Bit}}
    Nm1+1 == N || throw(ArgumentError("Nm1 + 1 = $(Nm1 + 1) ≠ $N = N"))
    vload_unroll_quote(D, AU, F, N, AV, W, M, UX, true, A === True, RS, true)
end

@inline function _vload(ptr::AbstractStridedPointer, u::Unroll, ::A, ::StaticInt{RS}) where {A<:StaticBool,RS}
    p, li = linear_index(ptr, u)
    sptr = similar_no_offset(ptr, p)
    _vload_unroll(sptr, li, A(), StaticInt{RS}(), staticunrolledvectorstride(strides(ptr), u))
end
@inline function _vload(ptr::AbstractStridedPointer, u::Unroll, m::AbstractMask, ::A, ::StaticInt{RS}) where {A<:StaticBool,RS}
    p, li = linear_index(ptr, u)
    sptr = similar_no_offset(ptr, p)
    _vload_unroll(sptr, li, m, A(), StaticInt{RS}(), staticunrolledvectorstride(strides(ptr), u))
end
@inline function _vload(
    ptr::AbstractStridedPointer, u::Unroll, m::VecUnroll{Nm1,W,B}, ::A, ::StaticInt{RS}
) where {A<:StaticBool,RS,Nm1,W,B<:Union{Bool,Bit}}
    p, li = linear_index(ptr, u)
    sptr = similar_no_offset(ptr, p)
    _vload_unroll(sptr, li, m, A(), StaticInt{RS}(), staticunrolledvectorstride(strides(ptr), u))
end
@inline function _vload(ptr::AbstractStridedPointer{T}, u::Unroll{AU,F,N,AV,W}, m::Bool, ::A, ::StaticInt{RS}) where {A<:StaticBool,RS,AU,F,N,AV,W,T}
    if m
        _vload(ptr, u, A(), StaticInt{RS}())
    else
        zero_vecunroll(StaticInt{N}(), StaticInt{W}(), T, StaticInt{RS}())
    end
end

function vstore_unroll_quote(
    D::Int, AU::Int, F::Int, N::Int, AV::Int, W::Int, M::UInt, X::Int, mask::Bool, align::Bool, noalias::Bool, nontemporal::Bool, rs::Int, vecunrollmask::Bool
)
    t = Expr(:tuple)
    inds = unrolled_indicies(D, AU, F, N, AV, W, X)
    q = quote
        $(Expr(:meta, :inline))
        gptr = similar_no_offset(sptr, gep(pointer(sptr), data(u)))
        # gptr = gesp(sptr, getfield(u, :i))
        t = data(vu)
    end
    alignval = Expr(:call, align ? :True : :False)
    noaliasval = Expr(:call, noalias ? :True : :False)
    nontemporalval = Expr(:call, nontemporal ? :True : :False)
    rsexpr = Expr(:call, Expr(:curly, :StaticInt, rs))
    if vecunrollmask
        push!(q.args, :(masktup = data(vm)))
    end
    gf = GlobalRef(Core, :getfield)
    for n in 1:N
        l = Expr(:call, :_vstore!, :gptr, Expr(:call, gf, :t, n, false), inds[n])
        if vecunrollmask
            push!(l.args, :($gf(masktup, $n, false)))
        elseif mask && (M % Bool)
            push!(l.args, :sm)
        end
        push!(l.args, alignval, noaliasval, nontemporalval, rsexpr)
        M >>= 1
        push!(q.args, l)
    end
    q
end

function shuffle_store_quote(
    ::Type{T}, N, C, B, AU, F, UN, AV, W, X, ::Type{I}, align::Bool, alias::Bool, notmp::Bool, rs::Int, mask::Bool
) where {T,I}
    IT, ind_type, _W, _X, M, O = index_summary(I)
    # we don't require vector indices for `Unroll`s...
    # @assert _W == W "W from index $(_W) didn't equal W from Unroll $W."
    size_T = sizeof(T)
    # We need to unroll in a contiguous dimension for this to be a shuffle store, and we need the step between the start of the vectors to be `1`
    interleave_memory_access(AU, C, F, X, UN, size_T, B) || return nothing
    # `X` is stride between indices, e.g. `X = 3` means our final vectors should be `<x[0], x[3], x[6], x[9]>`
    # We need `X` to equal the steps (the unrolling factor)
    Wfull = W * UN
    # the approach for combining is to keep concatenating vectors to double their length
    # until we hit ≥ half Wfull, then we `vresize` the remainder, and shuffle in the final combination before storing.

    T_sym = JULIA_TYPES[T]
    I_sym = JULIA_TYPES[IT]
    # mask = false
    vstoreexpr = vstore_quote(
        T_sym, I_sym, ind_type, Wfull, size_T, M, O,
        mask, align, alias, notmp, rs
    )
    q = quote
        $(Expr(:meta,:inline))
        ptr = pointer(sptr)
        t = data(vu)
        i = data(u)
    end
    syms = Vector{Symbol}(undef, UN)
    gf = GlobalRef(Core, :getfield)
    for n ∈ 1:UN
        syms[n] = vs = Symbol(:v_,n)
        push!(q.args, Expr(:(=), vs, Expr(:call, gf, :t, n, false)))
    end
    Wtemp = W
    Nvec = UN
    # first, we start concatenating vectors
    while 2Wtemp < Wfull
        Wnext = 2Wtemp
        Nvecnext = (Nvec >>> 1)
        for n ∈ 1:Nvecnext
            v1 = syms[2n-1]
            v2 = syms[2n  ]
            vc = Symbol(v1, :_, v2)
            push!(q.args, Expr(:(=), vc, Expr(:call, :vcat, v1, v2)))
            syms[n] = vc
        end
        if isodd(Nvec)
            syms[Nvecnext+1] = syms[Nvec]
            Nvec = Nvecnext + 1
        else
            Nvec = Nvecnext
        end
        Wtemp = Wnext
    end
    shufftup = Expr(:tuple)
    for w ∈ 0:W-1
        for n ∈ 0:UN-1
            push!(shufftup.args, W*n + w)
        end
    end
    mask && push!(q.args, :(m = mask(StaticInt{$Wfull}(), $UN * $gf(sm, :evl))))
    push!(q.args, Expr(:(=), :v, Expr(:call, :shufflevector, syms[1], syms[2], Expr(:call, Expr(:curly, :Val, shufftup)))))
    push!(q.args, vstoreexpr)
    q
end
function sparse_index_tuple(N, d, o)
    t = Expr(:tuple)
    for n ∈ 1:N
        if n == d
            push!(t.args, :(StaticInt{$o}()))
        else
            push!(t.args, :(StaticInt{0}()))
        end
    end
    t
end
function vstore_transpose_quote(D,AU,F,N,AV,W,X,align,alias,notmp,RS,st,Tsym,M,evl)
    ispow2(W) || throw(ArgumentError("Vector width must be a power of 2, but recieved $W."))
    isone(F) || throw(ArgumentError("No point to doing a transposed store if unroll step factor $F != 1"))
    C = AU # the point of tranposing
    q = Expr(
        :block, Expr(:meta,:inline), :(vd = data(vu)),
        :(gptr = similar_no_offset(sptr, gep(pointer(sptr), data(u))))
    )
    alignval = Expr(:call, align ? :True : :False)
    aliasval = Expr(:call, alias ? :True : :False)
    notmpval = Expr(:call, notmp ? :True : :False)
    rsexpr = Expr(:call, Expr(:curly, :StaticInt, RS))
    domask = init_transpose_memop_masking!(q, M, N, evl)

    vds = Vector{Symbol}(undef, N)
    for n ∈ 1:N
        vds[n] = vdn = Symbol(:vd_,n)
        push!(q.args, :($vdn = getfield(vd, $n, false)))
    end
    i = 0
    # Use `trailing_zeros` to decompose unroll amount, `N`, into a sum of powers-of-2
    Wmax = RS ÷ st
    while N > 0
        r = N % Wmax
        if r == 0
            npartial = n = Wmax
            mask = ~zero(UInt)
        else
            npartial = r
            n = nextpow2(npartial)
            if npartial == n
                mask = ~zero(UInt)
            else
                mask = (one(UInt) << (npartial)) - one(UInt)
            end
        end
        N -= npartial
        if n == 1
            # this can only happen on the first iter, so `StaticInt{0}()` is fine
            ind = Expr(:tuple)
            for d ∈ 1:D
                if AV == d
                    push!(ind.args, :(MM{$W,$X}(StaticInt{0}())))
                else
                    push!(ind.args, :(StaticInt{0}()))
                end
            end
            storeq = :(_vstore!(gptr, $(vds[1]), $ind))
            domask && push!(storeq.args, :sm)
            push!(storeq.args, alignval, aliasval, notmpval, rsexpr)
            push!(q.args, storeq)
        # elseif n < W
        # elseif n == W
        else
            t = Expr(:tuple)
            for nn ∈ 1:npartial
                push!(t.args, vds[i+nn])
            end
            for nn ∈ npartial+1:n
                # if W == 1
                #     push!(t.args, :(zero($Tsym)))
                # else
                push!(t.args, :(_vundef(StaticInt{$W}(), $Tsym)))
                # end
            end
            dname = Symbol(:vud_,i,:_,n)
            if W == 1
                push!(q.args, :($dname = transpose_vecunroll(VecUnroll($t))))
            else
                push!(q.args, :($dname = data(transpose_vecunroll(VecUnroll($t)))))
            end
            # dname is a `VecUnroll{(W-1),N}`
            for w ∈ 1:W
                ind = Expr(:tuple)
                for d ∈ 1:D
                    if AU == d
                        push!(ind.args, :(MM{$n}(StaticInt{$i}())))
                    elseif AV == d
                        push!(ind.args, :(StaticInt{$(X*(w-1))}()))
                    else
                        push!(ind.args, :(StaticInt{0}()))
                    end
                end
                # transposing mask does what?
                storeq = if W == 1
                    :(_vstore!(gptr, $dname, $ind))
                else
                    :(_vstore!(gptr, getfield($dname, $w, false), $ind))
                end
                push_transpose_mask!(q, storeq, domask, n, npartial, w, W, evl, RS, mask)
                push!(storeq.args, alignval, aliasval, notmpval, rsexpr)
                push!(q.args, storeq)

            end
        end
        # M >>>= 1
        i += npartial
    end
    # @show
    q
end

@generated function _vstore_unroll!(
    sptr::AbstractStridedPointer{T,D,C,B}, vu::VecUnroll{Nm1,W}, u::Unroll{AU,F,N,AV,W,M,UX,I}, ::A, ::S, ::NT, ::StaticInt{RS}, ::StaticInt{X}
) where {AU,F,N,AV,W,M,I<:IndexNoUnroll,T,D,Nm1,S<:StaticBool,A<:StaticBool,NT<:StaticBool,RS,C,B,UX,X}
    N == Nm1 + 1 || throw(ArgumentError("The unrolled index specifies unrolling by $N, but sored `VecUnroll` is unrolled by $(Nm1+1)."))
    # 1+1
    align =  A === True
    alias =  S === True
    notmp = NT === True
    should_transpose = should_transpose_memop(F,C,AU,AV,N,M)
    1+1
    if (W == N) & ((sizeof(T)*W) == RS) & should_transpose
        # should transpose means we'll transpose, but we'll only prefer it over the
        # `shuffle_store_quote` implementation if W == N, and we're using the entire register.
        # Otherwise, llvm's shuffling is probably more clever/efficient when the conditions for
        # `shuffle_store_quote` actually hold.
        return vstore_transpose_quote(D,AU,F,N,AV,W,UX,align,alias,notmp,RS,sizeof(T),JULIA_TYPES[T],zero(UInt),false)
    end
    maybeshufflequote = shuffle_store_quote(T,D,C,B,AU,F,N,AV,W,X, I, align, alias, notmp, RS, false)
    maybeshufflequote === nothing || return maybeshufflequote
    if should_transpose
        vstore_transpose_quote(D,AU,F,N,AV,W,UX,align,alias,notmp,RS,sizeof(T),JULIA_TYPES[T],zero(UInt),false)
    else
        vstore_unroll_quote(D, AU, F, N, AV, W, M, UX, false, align, alias, notmp, RS, false)
    end
end
@generated function _vstore_unroll!(
    sptr::AbstractStridedPointer{T,D,C,B}, vu::VecUnroll{Nm1,W}, u::Unroll{AU,F,N,AV,W,M,UX,I}, ::A, ::S, ::NT, ::StaticInt{RS}, ::Nothing
) where {AU,F,N,AV,W,M,I<:IndexNoUnroll,T,D,Nm1,S<:StaticBool,A<:StaticBool,NT<:StaticBool,RS,C,B,UX}
    align =  A === True
    alias =  S === True
    notmp = NT === True
    1+1
    N == Nm1 + 1 || throw(ArgumentError("The unrolled index specifies unrolling by $N, but sored `VecUnroll` is unrolled by $(Nm1+1)."))
    if should_transpose_memop(F,C,AU,AV,N,M)
        vstore_transpose_quote(D,AU,F,N,AV,W,UX,align,alias,notmp,RS,sizeof(T),JULIA_TYPES[T],zero(UInt),false)
    else
        vstore_unroll_quote(D, AU, F, N, AV, W, M, UX, false, align, alias, notmp, RS, false)
    end
end
@generated function _vstore_unroll!(
    sptr::AbstractStridedPointer{T,D,C,B}, vu::VecUnroll{Nm1,W,VUT,VUV}, u::Unroll{AU,F,N,AV,W,M,UX,I}, sm::EVLMask{W},
    ::A, ::S, ::NT, ::StaticInt{RS},::StaticInt{X}
) where {AU,F,N,AV,W,M,I<:IndexNoUnroll,T,D,Nm1,S<:StaticBool,A<:StaticBool,NT<:StaticBool,RS,UX,VUT,VUV<:VecOrScalar,X,B,C}
    N == Nm1 + 1 || throw(ArgumentError("The unrolled index specifies unrolling by $N, but sored `VecUnroll` is unrolled by $(Nm1+1)."))
    align =  A === True
    alias =  S === True
    notmp = NT === True
    should_transpose = should_transpose_memop(F,C,AU,AV,N,M)
    if (W == N) & ((sizeof(T)*W) == RS) & should_transpose
        vstore_transpose_quote(D,AU,F,N,AV,W,UX,align,alias,notmp,RS,sizeof(T),JULIA_TYPES[T],M,true)
    end
    maybeshufflequote = shuffle_store_quote(T,D,C,B,AU,F,N,AV,W,X, I, align, alias, notmp, RS, true)
    maybeshufflequote === nothing || return maybeshufflequote
    if should_transpose
        vstore_transpose_quote(D,AU,F,N,AV,W,UX,align,alias,notmp,RS,sizeof(T),JULIA_TYPES[T],M,true)
    else
        vstore_unroll_quote(D, AU, F, N, AV, W, M, UX, true, align, alias, notmp, RS, false)
    end
end
@generated function _vstore_unroll!(
    sptr::AbstractStridedPointer{T,D,C}, vu::VecUnroll{Nm1,W,VUT,VUV}, u::Unroll{AU,F,N,AV,W,M,UX,I}, sm::AbstractMask{W},
    ::A, ::S, ::NT, ::StaticInt{RS}, ::Any
) where {AU,F,N,AV,W,M,I<:IndexNoUnroll,T,D,Nm1,S<:StaticBool,A<:StaticBool,NT<:StaticBool,RS,UX,VUT,VUV<:VecOrScalar,C}
    N == Nm1 + 1 || throw(ArgumentError("The unrolled index specifies unrolling by $N, but sored `VecUnroll` is unrolled by $(Nm1+1)."))
    align =  A === True
    alias =  S === True
    notmp = NT === True
    if should_transpose_memop(F,C,AU,AV,N,M)
        vstore_transpose_quote(D,AU,F,N,AV,W,UX,align,alias,notmp,RS,sizeof(T),JULIA_TYPES[T],M,sm<:EVLMask)
    else
        vstore_unroll_quote(D, AU, F, N, AV, W, M, UX, true, A===True, S===True, NT===True, RS, false)
    end
end
# TODO: add `m::VecUnroll{Nm1,W,Bool}`
@generated function _vstore_unroll!(
    sptr::AbstractStridedPointer{T,D}, vu::VecUnroll{Nm1,W,VUT,VUV}, u::Unroll{AU,F,N,AV,W,M,UX,I}, vm::VecUnroll{Nm1,<:Any,B}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {AU,F,N,AV,W,M,I<:IndexNoUnroll,T,D,Nm1,S<:StaticBool,A<:StaticBool,NT<:StaticBool,RS,UX,B<:Union{Bit,Bool},VUT,VUV<:VecOrScalar}
    N == Nm1 + 1 || throw(ArgumentError("The unrolled index specifies unrolling by $N, but sored `VecUnroll` is unrolled by $(Nm1+1)."))
    vstore_unroll_quote(D, AU, F, N, AV, W, M, UX, true, A===True, S===True, NT===True, RS, true)
end

@inline function _vstore!(
    ptr::AbstractStridedPointer, vu::VecUnroll{Nm1,W}, u::Unroll{AU,F,N,AV,W}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS,AU,F,N,AV,W,Nm1}
    p, li = linear_index(ptr, u)
    sptr = similar_no_offset(ptr, p)
    _vstore_unroll!(sptr, vu, li, A(), S(), NT(), StaticInt{RS}(), staticunrolledvectorstride(strides(sptr), u))
end
@inline function _vstore!(
    ptr::AbstractStridedPointer, vu::VecUnroll{Nm1,W}, u::Unroll{AU,F,N,AV,W}, m::AbstractMask{W}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS,AU,F,N,AV,W,Nm1}
    p, li = linear_index(ptr, u)
    sptr = similar_no_offset(ptr, p)
    _vstore_unroll!(sptr, vu, li, m, A(), S(), NT(), StaticInt{RS}(), staticunrolledvectorstride(strides(sptr), u))
end
@inline function _vstore!(
    ptr::AbstractStridedPointer, vu::VecUnroll{Nm1,W}, u::Unroll{AU,F,N,AV,W}, m::VecUnroll{Nm1,<:Any,B}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS,Nm1,W,B<:Union{Bool,Bit},AU,F,N,AV}
    p, li = linear_index(ptr, u)
    sptr = similar_no_offset(ptr, p)
    # @show vu u m
    _vstore_unroll!(sptr, vu, li, m, A(), S(), NT(), StaticInt{RS}())
end
@inline function _vstore!(
    ptr::AbstractStridedPointer, vu::VecUnroll{Nm1,W}, u::Unroll{AU,F,N,AV,W}, m::Bool, ::A, ::S, ::NT, ::StaticInt{RS}
) where {A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS,AU,F,N,AV,W,Nm1}
    m && _vstore!(ptr, vu, u, A(), S(), NT(), StaticInt{RS}())
    nothing
end

@inline function _vstore!(
    sptr::AbstractStridedPointer, v::V, u::Unroll{AU,F,N,AV,W}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS,W,T,V<:AbstractSIMDVector{W,T},AU,F,N,AV}
    _vstore!(sptr, vconvert(VecUnroll{Int(StaticInt{N}()-One()),W,T,Vec{W,T}}, v), u, A(), S(), NT(), StaticInt{RS}())
end
@inline function _vstore!(
    sptr::AbstractStridedPointer, v::V, u::Unroll{AU,F,N,AV,W}, m::Union{Bool,AbstractMask,VecUnroll}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS,W,T,V<:AbstractSIMDVector{W,T},AU,F,N,AV}
    _vstore!(sptr, vconvert(VecUnroll{Int(StaticInt{N}()-One()),W,T,Vec{W,T}}, v), u, m, A(), S(), NT(), StaticInt{RS}())
end
@inline function _vstore!(
    sptr::AbstractStridedPointer{T}, x::NativeTypes, u::Unroll{AU,F,N,AV,W}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS,W,T<:NativeTypes,AU,F,N,AV}
    # @show typeof(x), VecUnroll{Int(StaticInt{N}()-One()),W,T,Vec{W,T}}
    _vstore!(sptr, vconvert(VecUnroll{Int(StaticInt{N}()-One()),W,T,Vec{W,T}}, x), u, A(), S(), NT(), StaticInt{RS}())
end
@inline function _vstore!(
    sptr::AbstractStridedPointer{T}, x::NativeTypes, u::Unroll{AU,F,N,AV,W}, m::Union{Bool,AbstractMask,VecUnroll}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS,W,T<:NativeTypes,AU,F,N,AV}
    _vstore!(sptr, vconvert(VecUnroll{Int(StaticInt{N}()-One()),W,T,Vec{W,T}}, x), u, m, A(), S(), NT(), StaticInt{RS}())
end
@inline function _vstore!(
    sptr::AbstractStridedPointer{T}, v::NativeTypes, u::Unroll{AU,F,N,-1,1}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS,T<:NativeTypes,AU,F,N}
    _vstore!(sptr, vconvert(VecUnroll{Int(StaticInt{N}()-One()),1,T,T}, v), u, A(), S(), NT(), StaticInt{RS}())
end
@inline function _vstore!(
    sptr::AbstractStridedPointer{T}, v::NativeTypes, u::Unroll{AU,F,N,-1,1}, m::Union{Bool,AbstractMask,VecUnroll}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS,T<:NativeTypes,AU,F,N}
    _vstore!(sptr, vconvert(VecUnroll{Int(StaticInt{N}()-One()),1,T,T}, v), u, m, A(), S(), NT(), StaticInt{RS}())
end

function vload_double_unroll_quote(
    D::Int, NO::Int, NI::Int, AUO::Int, FO::Int, AV::Int, W::Int, MO::UInt,
    X::Int, C::Int, AUI::Int, FI::Int, MI::UInt, mask::Bool, A::Bool, RS::Int, svus::Int
)
    # UO + 1 ≠ NO && throw(ArgumentError("Outer unroll being stores is unrolled $(UO+1) times, but index indicates it was unrolled $NO times."))
    # UI + 1 ≠ NI && throw(ArgumentError("Inner unroll being stores is unrolled $(UI+1) times, but index indicates it was unrolled $NI times."))
    q = Expr(
        :block, Expr(:meta,:inline),
        :(id = getfield(getfield(u, :i), :i)),
        :(gptr = similar_no_offset(sptr, gep(pointer(sptr), id)))
    )
    aexpr = Expr(:call, A ? :True : :False)
    rsexpr = Expr(:call, Expr(:curly, :StaticInt, RS))
    if (AUO == C) & ((AV ≠ C) | ((AV == C) & (X == NO))) # outer unroll is along contiguous axis, so we swap outer and inner
        # we loop over `UI+1`, constructing VecUnrolls "slices", and store those
        unroll = :(Unroll{$AUO,$FO,$NO,$AV,$W,$MO,$X}(Zero()))
        # tupvec = Vector{Expr}(undef, NI)
        vds = Vector{Symbol}(undef, NI)
        for ui ∈ 0:NI-1
            if ui == 0
                loadq = :(_vload_unroll(gptr, $unroll)) # VecUnroll($tup)
            else
                inds = sparse_index_tuple(D, AUI, ui*FI)
                loadq = :(_vload_unroll(gesp(gptr, $inds), $unroll)) # VecUnroll($tup)
            end
            if mask & (MI % Bool)
                push!(loadq.args, :m)
            end
            MI >>>= 1
            push!(loadq.args, aexpr, rsexpr)
            if svus == typemax(Int)
                push!(loadq.args, nothing)
            else
                push!(loadq.args, :(StaticInt{$svus}()))
            end
            vds[ui+1] = vul = Symbol(:vul_, ui)
            push!(q.args, Expr(:(=), vul, :(getfield($loadq, 1))))
        end
        otup = Expr(:tuple)
        for t ∈ 1:NO # transpose them
            tup = Expr(:tuple)
            # tup = ui == 0 ? Expr(:tuple) : tupvec[ui+1]
            for ui ∈ 1:NI
                # push!(tup.args, :(getfield($(vds[t]), $(ui+1), false)))
                push!(tup.args, :(getfield($(vds[ui]), $t, false)))
            end
            push!(otup.args, :(VecUnroll($tup)))
        end
        push!(q.args, :(VecUnroll($otup)))
    else # we loop over `UO+1` and do the loads
        unroll = :(Unroll{$AUI,$FI,$NI,$AV,$W,$MI,$X}(Zero()))
        tup = Expr(:tuple)
        for uo ∈ 0:NO-1
            if uo == 0
                loadq = :(_vload_unroll(gptr, $unroll))
            else
                inds = sparse_index_tuple(D, AUO, uo*FO)
                loadq = :(_vload_unroll(gesp(gptr, $inds), $unroll))
            end
            if mask & (MO % Bool)
                push!(loadq.args, :m)
            end
            MO >>>= 1
            push!(loadq.args, aexpr, rsexpr)
            if svus == typemax(Int)
                push!(loadq.args, nothing)
            else
                push!(loadq.args, :(StaticInt{$svus}()))
            end
            push!(tup.args, loadq)
        end
        push!(q.args, :(VecUnroll($tup)))
    end
    return q
end
@generated function _vload_unroll(
    sptr::AbstractStridedPointer{T,D,C}, u::UU, ::A, ::StaticInt{RS}, ::StaticInt{SVUS}
) where {T, A<:StaticBool,RS, D,C, SVUS, UU <: NestedUnroll}
    # 1+1
    AUO,FO,NO,AV,W,MO,X,U = unroll_params(UU)
    AUI,FI,NI,AV,W,MI,X,I = unroll_params( U)
    vload_double_unroll_quote(D, NO, NI, AUO, FO, AV, W, MO, X, C, AUI, FI, MI, false, A === True, RS, SVUS)
end

@generated function _vload_unroll(
    sptr::AbstractStridedPointer{T,D,C}, u::UU, ::A, ::StaticInt{RS}, ::Nothing
) where {T, A<:StaticBool,RS, D,C, UU <: NestedUnroll}
    # 1+1
    AUO,FO,NO,AV,W,MO,X,U = unroll_params(UU)
    AUI,FI,NI,AV,W,MI,X,I = unroll_params( U)
    vload_double_unroll_quote(D, NO, NI, AUO, FO, AV, W, MO, X, C, AUI, FI, MI, false, A === True, RS, typemax(Int))
end
@generated function _vload_unroll(
    sptr::AbstractStridedPointer{T,D,C}, u::UU, m::AbstractMask{W}, ::A, ::StaticInt{RS}, ::StaticInt{SVUS}
) where {W, T, A<:StaticBool,RS, D,C, SVUS, UU <: NestedUnroll{W}}
    1+1
    AUO,FO,NO,AV,_W,MO,X,U = unroll_params(UU)
    AUI,FI,NI,AV,_W,MI,X,I = unroll_params( U)
    vload_double_unroll_quote(D, NO, NI, AUO, FO, AV, W, MO, X, C, AUI, FI, MI, true, A === True, RS, SVUS)
end
@generated function _vload_unroll(
    sptr::AbstractStridedPointer{T,D,C}, u::UU, m::AbstractMask{W}, ::A, ::StaticInt{RS}, ::Nothing
) where {W, T, A<:StaticBool,RS, D,C, UU <: NestedUnroll{W}}
    # 1+1
    AUO,FO,NO,AV,_W,MO,X,U = unroll_params(UU)
    AUI,FI,NI,AV,_W,MI,X,I = unroll_params( U)
    vload_double_unroll_quote(D, NO, NI, AUO, FO, AV, W, MO, X, C, AUI, FI, MI, true, A === True, RS, typemax(Int))
end

# Unroll{AU,F,N,AV,W,M,X,I}
function vstore_double_unroll_quote(
    D::Int, NO::Int, NI::Int, AUO::Int, FO::Int, AV::Int, W::Int, MO::UInt,
    X::Int, C::Int, AUI::Int, FI::Int, MI::UInt, mask::Bool, A::Bool, S::Bool, NT::Bool, RS::Int, svus::Int
)
    # UO + 1 ≠ NO && throw(ArgumentError("Outer unroll being stores is unrolled $(UO+1) times, but index indicates it was unrolled $NO times."))
    # UI + 1 ≠ NI && throw(ArgumentError("Inner unroll being stores is unrolled $(UI+1) times, but index indicates it was unrolled $NI times."))
    q = Expr(
        :block, Expr(:meta,:inline),
        :(vd = getfield(v, :data)), :(id = getfield(getfield(u, :i), :i)),
        :(gptr = similar_no_offset(sptr, gep(pointer(sptr), id)))
    )
    aexpr = Expr(:call, A ? :True : :False)
    sexpr = Expr(:call, S ? :True : :False)
    ntexpr = Expr(:call, NT ? :True : :False)
    rsexpr = Expr(:call, Expr(:curly, :StaticInt, RS))
    if (AUO == C) & (AV ≠ C)  # outer unroll is along contiguous axis
        # so we loop over `UI+1`, constructing VecUnrolls "slices", and store those
        unroll = :(Unroll{$AUO,$FO,$NO,$AV,$W,$MO,$X}(Zero()))
        vds = Vector{Symbol}(undef, NO)
        for t ∈ 1:NO
            vds[t] = vdt = Symbol(:vd_,t)
            push!(q.args, :($vdt = getfield(getfield(vd, $t, false), 1)))
        end
        # tupvec = Vector{Expr}(undef, NI)
        for ui ∈ 0:NI-1
            tup = Expr(:tuple)
            # tup = ui == 0 ? Expr(:tuple) : tupvec[ui+1]
            for t ∈ 1:NO
                # push!(tup.args, :(getfield($(vds[t]), $(ui+1), false)))
                push!(tup.args, :(getfield($(vds[t]), $(ui+1), false)))
            end
            # tupvec[ui+1] = tup
            if ui == 0
                storeq = :(_vstore_unroll!(gptr, VecUnroll($tup), $unroll))
            else
                inds = sparse_index_tuple(D, AUI, ui*FI)
                storeq = :(_vstore_unroll!(gesp(gptr, $inds), VecUnroll($tup), $unroll))
            end
            if mask & (MI % Bool)
                push!(storeq.args, :m)
            end
            MI >>>= 1
            push!(storeq.args, aexpr, sexpr, ntexpr, rsexpr)
            if svus == typemax(Int)
                push!(storeq.args, nothing)
            else
                push!(storeq.args, :(StaticInt{$svus}()))
            end
            push!(q.args, storeq)
        end
    else # we loop over `UO+1` and do the stores
        unroll = :(Unroll{$AUI,$FI,$NI,$AV,$W,$MI,$X}(Zero()))
        for uo ∈ 0:NO-1
            if uo == 0
                storeq = :(_vstore_unroll!(gptr, getfield(vd, 1, false), $unroll))
            else
                inds = sparse_index_tuple(D, AUO, uo*FO)
                storeq = :(_vstore_unroll!(gesp(gptr, $inds), getfield(vd, $(uo+1), false), $unroll))
            end
            if mask & (MO % Bool)
                push!(storeq.args, :m)
            end
            MO >>>= 1
            push!(storeq.args, aexpr, sexpr, ntexpr, rsexpr)
            if svus == typemax(Int)
                push!(storeq.args, nothing)
            else
                push!(storeq.args, :(StaticInt{$svus}()))
            end
            push!(q.args, storeq)
        end
    end
    return q
end

@generated function _vstore_unroll!(
    sptr::AbstractStridedPointer{T,D,C}, v::VecUnroll{<:Any,W,T,<:VecUnroll{<:Any,W,T,Vec{W,T}}},
    u::UU, ::A, ::S, ::NT, ::StaticInt{RS}, ::StaticInt{SVUS}
) where {W, T, A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS, D,C, SVUS, UU <: NestedUnroll{W}}
    1+1
    AUO,FO,NO,AV,_W,MO,X,U = unroll_params(UU)
    AUI,FI,NI,AV,_W,MI,X,I = unroll_params( U)
    vstore_double_unroll_quote(D, NO, NI, AUO, FO, AV, W, MO, X, C, AUI, FI, MI, false, A === True, S === True, NT === True, RS, SVUS)
end

@generated function _vstore_unroll!(
    sptr::AbstractStridedPointer{T,D,C}, v::VecUnroll{<:Any,W,T,<:VecUnroll{<:Any,W,T,Vec{W,T}}},
    u::UU, ::A, ::S, ::NT, ::StaticInt{RS}, ::Nothing
) where {W, T, A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS, D,C, UU <: NestedUnroll{W}}
    1+1
    AUO,FO,NO,AV,_W,MO,X,U = unroll_params(UU)
    AUI,FI,NI,AV,_W,MI,X,I = unroll_params( U)
    vstore_double_unroll_quote(D, NO, NI, AUO, FO, AV, W, MO, X, C, AUI, FI, MI, false, A === True, S === True, NT === True, RS, typemax(Int))
end
@generated function _vstore_unroll!(
    sptr::AbstractStridedPointer{T,D,C}, v::VecUnroll{<:Any,W,T,<:VecUnroll{<:Any,W,T,Vec{W,T}}},
    u::UU, m::AbstractMask{W}, ::A, ::S, ::NT, ::StaticInt{RS}, ::StaticInt{SVUS}
) where {W, T, A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS, D,C, SVUS, UU <: NestedUnroll{W}}
    # 1+1
    AUO,FO,NO,AV,_W,MO,X,U = unroll_params(UU)
    AUI,FI,NI,AV,_W,MI,X,I = unroll_params( U)
    vstore_double_unroll_quote(D, NO, NI, AUO, FO, AV, W, MO, X, C, AUI, FI, MI, true, A === True, S === True, NT === True, RS, SVUS)
end
@generated function _vstore_unroll!(
    sptr::AbstractStridedPointer{T,D,C}, v::VecUnroll{<:Any,W,T,<:VecUnroll{<:Any,W,T,Vec{W,T}}},
    u::UU, m::AbstractMask{W}, ::A, ::S, ::NT, ::StaticInt{RS}, ::Nothing
) where {W, T, A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS, D,C, UU <: NestedUnroll{W}}
    # 1+1
    AUO,FO,NO,AV,_W,MO,X,U = unroll_params(UU)
    AUI,FI,NI,AV,_W,MI,X,I = unroll_params( U)
    vstore_double_unroll_quote(D, NO, NI, AUO, FO, AV, W, MO, X, C, AUI, FI, MI, true, A === True, S === True, NT === True, RS, typemax(Int))
end

function vstore_unroll_i_quote(Nm1, Wsplit, W, A, S, NT, rs::Int, mask::Bool)
    N = Nm1 + 1
    N*Wsplit == W || throw(ArgumentError("Vector of length $W can't be split into $N pieces of size $Wsplit."))
    q = Expr(:block, Expr(:meta, :inline), :(vt = data(v)), :(im = _materialize(i)))
    if mask
        let U = mask_type_symbol(Wsplit)
            push!(q.args, :(mt = data(vconvert(VecUnroll{$Nm1,$Wsplit,Bit,Mask{$Wsplit,$U}}, m))))
        end
    end
    j = 0
    alignval = Expr(:call, A ? :True : :False)
    aliasval = Expr(:call, S ? :True : :False)
    notmpval = Expr(:call, NT ? :True : :False)
    rsexpr = Expr(:call, Expr(:curly, :StaticInt, rs))
    for n ∈ 1:N
        shufflemask = Expr(:tuple)
        for w ∈ 1:Wsplit
            push!(shufflemask.args, j)
            j += 1
        end
        ex = :(__vstore!(ptr, vt[$n], shufflevector(im, Val{$shufflemask}())))
        mask && push!(ex.args, Expr(:call, GlobalRef(Core, :getfield), :mt, n, false))
        push!(ex.args, alignval, aliasval, notmpval, rsexpr)
        push!(q.args, ex)
    end
    q
end
@generated function __vstore!(
    ptr::Ptr{T}, v::VecUnroll{Nm1,Wsplit}, i::VectorIndex{W}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {T,Nm1,Wsplit,W,S<:StaticBool,A<:StaticBool,NT<:StaticBool,RS}
    vstore_unroll_i_quote(Nm1, Wsplit, W, A===True, S===True, NT===True, RS, false)
end
@generated function __vstore!(
    ptr::Ptr{T}, v::VecUnroll{Nm1,Wsplit}, i::VectorIndex{W}, m::AbstractMask{W}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {T,Nm1,Wsplit,W,S<:StaticBool,A<:StaticBool,NT<:StaticBool,RS}
    vstore_unroll_i_quote(Nm1, Wsplit, W, A===True, S===True, NT===True, RS, true)
end
function vstorebit_unroll_i_quote(Nm1::Int, Wsplit::Int, W::Int, A::Bool, S::Bool, NT::Bool, rs::Int, mask::Bool)
    N = Nm1 + 1
    N*Wsplit == W || throw(ArgumentError("Vector of length $W can't be split into $N pieces of size $Wsplit."))
    W == 8 || throw(ArgumentError("There is only a need for splitting a mask of size 8, but the mask is of size $W."))
    # q = Expr(:block, Expr(:meta, :inline), :(vt = data(v)), :(im = _materialize(i)), :(u = 0x00))
    q = Expr(:block, Expr(:meta, :inline), :(vt = data(v)), :(u = 0x00))
    j = 0
    gf = GlobalRef(Core, :getfield)
    while true
        push!(q.args, :(u |= data($(Expr(:call, gf, :vt, (N-j), false)))))
        j += 1
        j == N && break
        push!(q.args, :(u <<= $Wsplit))
    end
    alignval = Expr(:call, A ? :True : :False)
    aliasval = Expr(:call, A ? :True : :False)
    notmpval = Expr(:call, A ? :True : :False)
    rsexpr = Expr(:call, Expr(:curly, :StaticInt, rs))
    mask && push!(q.args, :(u = bitselect(data(m), __vload(Base.unsafe_convert(Ptr{$(mask_type_symbol(W))}, ptr), (data(i) >> 3), $alignval, $rsexpr), u)))
    call = Expr(:call, :__vstore!, :(reinterpret(Ptr{UInt8}, ptr)), :u, :(data(i) >> 3))
    push!(call.args, alignval, aliasval, notmpval, rsexpr)
    push!(q.args, call)
    q
end
@generated function __vstore!(
    ptr::Ptr{Bit}, v::VecUnroll{Nm1,Wsplit,Bit,M}, i::MM{W}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {Nm1,Wsplit,W,S<:StaticBool,A<:StaticBool,NT<:StaticBool, RS, M <: AbstractMask{Wsplit}}
    # 1 + 1
    vstorebit_unroll_i_quote(Nm1, Wsplit, W, A===True, S===True, NT===True, RS, false)
end
@generated function __vstore!(
    ptr::Ptr{Bit}, v::VecUnroll{Nm1,Wsplit,Bit,M}, i::MM{W}, m::Mask{W}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {Nm1,Wsplit,W,S<:StaticBool,A<:StaticBool,NT<:StaticBool, RS, M <: AbstractMask{Wsplit}}
    vstorebit_unroll_i_quote(Nm1, Wsplit, W, A===True, S===True, NT===True, RS, true)
end

# If `::Function` vectorization is masked, then it must not be reduced by `::Function`.
@generated function _vstore!(
    ::Function, ptr::AbstractStridedPointer{T,D,C}, vu::VecUnroll{U,W}, u::Unroll{AU,F,N,AV,W,M,X,I}, m, ::A, ::S, ::NT, ::StaticInt{RS}
) where {T,D,C,U,AU,F,N,W,M,I,AV,A<:StaticBool,S<:StaticBool,NT<:StaticBool,RS,X}
    N == U + 1 || throw(ArgumentError("The unrolled index specifies unrolling by $N, but sored `VecUnroll` is unrolled by $(U+1)."))
    # mask means it isn't vectorized
    AV > 0 || throw(ArgumentError("AV ≤ 0, but masking what, exactly?"))
    Expr(:block, Expr(:meta, :inline), :(_vstore!(ptr, vu, u, m, $(A()), $(S()), $(NT()), StaticInt{$RS}())))
end

function transposeshuffle(split, W, offset::Bool)
    tup = Expr(:tuple)
    w = 0
    S = 1 << split
    i = offset ? S : 0
    while w < W
        for s ∈ 0:S-1
            push!(tup.args, w + s + i)
        end
        for s ∈ 0:S-1
            # push!(tup.args, w + W + s)
            push!(tup.args, w + W + s + i)
        end
        w += 2S
    end
    Expr(:call, Expr(:curly, :Val, tup))
end

function horizontal_reduce_store_expr(W, Ntotal, (C,D,AU,F), op::Symbol, reduct::Symbol, noalias::Bool, RS::Int)
    N = ((C == AU) && isone(F)) ? prevpow2(Ntotal) : 0
    q = Expr(:block, Expr(:meta, :inline), :(v = data(vu)))
    # store = noalias ? :vnoaliasstore! : :vstore!
    falseexpr = Expr(:call, :False)
    aliasexpr = noalias ? Expr(:call, :True) : falseexpr
    rsexpr = Expr(:call, Expr(:curly, :StaticInt, RS))
    ispow2(W) || throw(ArgumentError("Horizontal store requires power-of-2 vector widths."))
    gf = GlobalRef(Core, :getfield)
    if N > 1
        if N < Ntotal
            push!(q.args, :(gptr = gesp(ptr, $gf(u, :i))))
            push!(q.args, :(bptr = pointer(gptr)))
        else
            push!(q.args, :(bptr = gep(ptr, $gf(u, :i))))
        end
        extractblock = Expr(:block)
        vectors = [Symbol(:v_, n) for n ∈ 0:N-1]
        for n ∈ 1:N
            push!(extractblock.args, Expr(:(=), vectors[n], Expr(:call, gf, :v, n, false)))
        end
        push!(q.args, extractblock)
        ncomp = 0
        minWN = min(W,N)
        while ncomp < N
            Nt = minWN;
            Wt = W
            splits = 0
            while Nt > 1
                Nt >>>= 1
                shuffle0 = transposeshuffle(splits, Wt, false)
                shuffle1 = transposeshuffle(splits, Wt, true)
                splits += 1
                for nh ∈ 1:Nt
                    n1 = 2nh
                    n0 = n1 - 1
                    v0 = vectors[n0 + ncomp]; v1 = vectors[n1 + ncomp]; vh = vectors[nh + ncomp];
                    # combine n0 and n1
                    push!(q.args, Expr(
                        :(=), vh, Expr(
                            :call, op,
                            Expr(:call, :shufflevector, v0, v1, shuffle0),
                            Expr(:call, :shufflevector, v0, v1, shuffle1))
                    ))
                end
            end
            # v0 is now the only vector
            v0 = vectors[ncomp + 1]
            while Wt > minWN
                Wh = Wt >>> 1
                v0new = Symbol(v0, Wt)
                push!(q.args, Expr(
                    :(=), v0new, Expr(
                        :call, op,
                        Expr(:call, :shufflevector, v0, Expr(:call, Expr(:curly, :Val, Expr(:tuple, [w for w ∈ 0:Wh-1]...)))),
                        Expr(:call, :shufflevector, v0, Expr(:call, Expr(:curly, :Val, Expr(:tuple, [w for w ∈ Wh:Wt-1]...)))))
                )
                      )
                v0 = v0new
                Wt = Wh
            end
            push!(q.args, Expr(:call, :__vstore!, :bptr, v0, falseexpr, aliasexpr, falseexpr, rsexpr))
            ncomp += minWN
        end
    else
        push!(q.args, :(gptr = gesp(ptr, $gf(u, :i))))
    end
    if N < Ntotal
        zeroexpr = Expr(:call, Expr(:curly, :StaticInt, 0))
        ind = Expr(:tuple); foreach(_ -> push!(ind.args, zeroexpr), 1:D)
        for n ∈ N+1:Ntotal
            (n > N+1) && (ind = copy(ind)) # copy to avoid overwriting old
            ind.args[AU] = Expr(:call, Expr(:curly, :StaticInt, F*(n-1)))
            scalar = Expr(:call, reduct, Expr(:call, gf, :v, n, false))
            push!(q.args, Expr(:call, :_vstore!, :gptr, scalar, ind, falseexpr, aliasexpr, falseexpr, rsexpr))
        end
    end
    q
end
@inline function _vstore!(
    ::G, ptr::AbstractStridedPointer{T,D,C}, vu::VecUnroll{U,W}, u::Unroll{AU,F,N,AV,W,M,X,I}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {T,D,C,U,AU,F,N,W,M,I,G<:Function,AV,A<:StaticBool, S<:StaticBool, NT<:StaticBool, RS,X}
    _vstore!(ptr, vu, u, A(), S(), NT(), StaticInt{RS}())
end
@generated function _vstore!(
    ::G, ptr::AbstractStridedPointer{T,D,C}, vu::VecUnroll{U,W}, u::Unroll{AU,F,N,AV,1,M,X,I}, ::A, ::S, ::NT, ::StaticInt{RS}
) where {T,D,C,U,AU,F,N,W,M,I,G<:Function,AV,A<:StaticBool, S<:StaticBool, NT<:StaticBool, RS,X}
    N == U + 1 || throw(ArgumentError("The unrolled index specifies unrolling by $N, but sored `VecUnroll` is unrolled by $(U+1)."))
    if (G === typeof(identity)) || (AV > 0) || (W == 1)
        return Expr(:block, Expr(:meta, :inline), :(_vstore!(ptr, vu, u, $A(), $S(), $NT(), StaticInt{$RS}())))
    elseif G === typeof(vsum)
        op = :+; reduct = :vsum
    elseif G === typeof(vprod)
        op = :*; reduct = :vprod
    elseif G === typeof(vmaximum)
        op = :max; reduct = :vmaximum
    elseif G === typeof(vminimum)
        op = :min; reduct = :vminimum
    elseif G === typeof(vall)
        op = :&; reduct = :vall
    elseif G === typeof(vany)
        op = :|; reduct = :vany
    else
        throw("Function $G not recognized.")
    end
    horizontal_reduce_store_expr(W, N, (C,D,AU,F), op, reduct, S === True, RS)
end

function lazymulunroll_load_quote(M,O,N,maskall,masklast,align,rs)
    t = Expr(:tuple)
    alignval = Expr(:call, align ? :True : :False)
    rsexpr = Expr(:call, Expr(:curly, :StaticInt, rs))
    gf = GlobalRef(Core, :getfield)
    for n in 1:N+1
        ind = if (M != 1) | (O != 0)
            :(LazyMulAdd{$M,$O}(u[$n]))
        else
            Expr(:call, gf, :u, n, false)
        end
        call = if maskall
            Expr(:call, :__vload, :ptr, ind, Expr(:call, gf, :mt, n, false), alignval, rsexpr)
        elseif masklast && n == N+1
            Expr(:call, :__vload, :ptr, ind, :m, alignval, rsexpr)
        else
            Expr(:call, :__vload, :ptr, ind, alignval, rsexpr)
        end
        push!(t.args, call)
    end
    q = Expr(:block, Expr(:meta, :inline), :(u = data(um)))
    maskall && push!(q.args, :(mt = data(m)))
    push!(q.args, Expr(:call, :VecUnroll, t))
    q
end
@generated function __vload(ptr::Ptr{T}, um::VecUnroll{N,W,I,V}, ::A, ::StaticInt{RS}) where {T,N,W,I,V,A<:StaticBool,RS}
    lazymulunroll_load_quote(1,0,N,false,false,A === True,RS)
end
@generated function __vload(
    ptr::Ptr{T}, um::VecUnroll{N,W,I,V}, m::VecUnroll{N,W,Bit,M}, ::A, ::StaticInt{RS}
) where {T,N,W,I,V,A<:StaticBool,U,RS,M<:AbstractMask{W,U}}
    lazymulunroll_load_quote(1,0,N,true,false,A===True,RS)
end
@generated function __vload(
    ptr::Ptr{T}, um::VecUnroll{N,W1,I,V}, m::AbstractMask{W2,U}, ::A, ::StaticInt{RS}
) where {T,N,W1,W2,I,V,A<:StaticBool,U,RS}
    if W1 == W2
        lazymulunroll_load_quote(1,0,N,false,true,A===True,RS)
    elseif W2 == (N+1)*W1
        quote
            $(Expr(:meta,:inline))
            __vload(ptr, um, VecUnroll(splitvectortotuple(StaticInt{$(N+1)}(), StaticInt{$W1}(), m)), $A(), StaticInt{$RS}())
        end
    else
        throw(ArgumentError("Trying to load using $(N+1) indices of length $W1, while applying a mask of length $W2."))
    end
end
@generated function __vload(ptr::Ptr{T}, um::LazyMulAdd{M,O,VecUnroll{N,W,I,V}}, ::A, ::StaticInt{RS}) where {T,M,O,N,W,I,V,A<:StaticBool,RS}
    lazymulunroll_load_quote(M,O,N,false,false,A===True,RS)
end
@generated function __vload(
    ptr::Ptr{T}, um::LazyMulAdd{M,O,VecUnroll{N,W,I,V}}, m::VecUnroll{N,W,Bit,MSK}, ::A, ::StaticInt{RS}
) where {T,M,O,N,W,I,V,A<:StaticBool,U,RS,MSK<:AbstractMask{W,U}}
    lazymulunroll_load_quote(M,O,N,true,false,A===True,RS)
end
@generated function __vload(
    ptr::Ptr{T}, um::LazyMulAdd{M,O,VecUnroll{N,W1,I,V}}, m::AbstractMask{W2}, ::A, ::StaticInt{RS}
) where {T,M,O,N,W1,W2,I,V,A<:StaticBool,RS}
    if W1 == W2
        lazymulunroll_load_quote(M,O,N,false,true,A===True,RS)
    elseif W1 * (N+1) == W2
        quote
            $(Expr(:meta,:inline))
            __vload(ptr, um, VecUnroll(splitvectortotuple(StaticInt{$(N+1)}(), StaticInt{$W1}(), m)), $A(), StaticInt{$RS}())
        end
    else
        throw(ArgumentError("Trying to load using $(N+1) indices of length $W1, while applying a mask of length $W2."))
    end
end
function lazymulunroll_store_quote(M,O,N,mask,align,noalias,nontemporal,rs)
    gf = GlobalRef(Core, :getfield)
    q = Expr(:block, Expr(:meta, :inline), :(u = $gf($gf(um, :data), :data)), :(v = $gf($gf(vm, :data), :data)))
    alignval = Expr(:call, align ? :True : :False)
    noaliasval = Expr(:call, noalias ? :True : :False)
    nontemporalval = Expr(:call, nontemporal ? :True : :False)
    rsexpr = Expr(:call, Expr(:curly, :StaticInt, rs))
    for n in 1:N+1
        push!(q.args, Expr(:call, :vstore!, :ptr, Expr(:call, gf, :v, n, false), :(LazyMulAdd{$M,$O}(u[$n])), alignval, noaliasval, nontemporalval, rsexpr))
    end
    q
end

@generated function vload(r::FastRange{T}, i::Unroll{1,W,N,1,W,M,X,Tuple{I}}) where {T,I,W,N,M,X}
    q = quote
        $(Expr(:meta,:inline))
        s = vload(r, data(i))
        step = getfield(r, :s)
        mm = Vec(MM{$W,$X}(Zero())) * step
        v = Base.FastMath.add_fast(s + mm)
    end
    t = Expr(:tuple, :v)
    for n ∈ 1:N-1
        # push!(t.args, :(MM{$W,$W}(Base.FastMath.add_fast(s, $(T(n*W))))))
        push!(t.args, :(Base.FastMath.add_fast(v, Base.FastMath.mul_fast($(T(n*W)), step))))
    end
    push!(q.args, :(VecUnroll($t)))
    q
end
@generated function vload(r::FastRange{T}, i::Unroll{1,W,N,1,W,M,X,Tuple{I}}, m::AbstractMask{W}) where {T,I,W,N,M,X}
    q = quote
        $(Expr(:meta,:inline))
        s = vload(r, data(i))
        step = getfield(r, :s)
        mm = Vec(MM{$W,$X}(Zero())) * step
        v = Base.FastMath.add_fast(s + mm)
        z = zero(v)
    end
    t = if M % Bool
        Expr(:tuple, :(ifelse(m, v, z)))
    else
        Expr(:tuple, :v)
    end
    for n ∈ 1:N-1
        M >>>= 1
        if M % Bool
            push!(t.args, :(ifelse(m, Base.FastMath.add_fast(v, Base.FastMath.mul_fast($(T(n*W)), step)), z)))
        else
            push!(t.args, :(Base.FastMath.add_fast(v, Base.FastMath.mul_fast($(T(n*W)), step))))
        end
    end
    push!(q.args, :(VecUnroll($t)))
    q
end
@generated function vload(r::FastRange{T}, i::Unroll{1,W,N,1,W,M,X,Tuple{I}}, m::VecUnroll{Nm1,W,B}) where {T,I,W,N,M,X,Nm1,B<:Union{Bit,Bool}}
    q = quote
        $(Expr(:meta,:inline))
        s = vload(r, data(i))
        step = getfield(r, :s)
        mm = Vec(MM{$W,$X}(Zero())) * step
        v = Base.FastMath.add_fast(s + mm)
        z = zero(v)
    end
    t = Expr(:tuple, :(ifelse(getfield(m,$1,false), v, z)))
    for n ∈ 1:N-1
        push!(t.args, :(ifelse(getfield(m,$(n+1),false), Base.FastMath.add_fast(v, Base.FastMath.mul_fast($(T(n*W)), step)), z)))
    end
    push!(q.args, :(VecUnroll($t)))
    q
end


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

__precompile__(true)

module BAT

using Base.Threads

# import Base.Math.JuliaLibm

using Distributed
using LinearAlgebra
using Markdown
using Printf
using Random
using Statistics

using ArgCheck
using ArraysOfArrays
using Clustering
using Colors
using Distributions
using DoubleFloats
using ElasticArrays
using IntervalSets
using PDMats
using ParallelProcessingTools
using Parameters
using PositiveFactorizations
using ProgressMeter
using RecipesBase
using Requires
using ValueShapes
using StatsBase
using StructArrays
using Tables
using UnsafeArrays
using KernelDensity

import Measurements

#for AHMI
using DataStructures
using QuadGK
using LaTeXStrings

import Random123
import TypedTables

include("utils/utils.jl")
include("rngs/rngs.jl")
include("distributions/distributions.jl")
include("parameters/parameters.jl")
include("statistics/statistics.jl")
include("densities/densities.jl")
include("samplers/samplers.jl")
include("integration/integration.jl")
include("io/io.jl")
include("plotting/plotting.jl")

const _PLOTS_MODULE = Ref{Union{Module,Nothing}}(nothing)
_plots_module() = _PLOTS_MODULE[]

function __init__()
    @require HDF5="f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" include("io/hdf5_specific.jl")
    @require Plots="91a5bcdd-55d7-5caf-9e0b-520d859cae80" _PLOTS_MODULE[] = Plots
end

end # module


text:
module TestCore

using Test
using Maybe

@testset "get/getindex" begin
    @testset for get in [Maybe.get, Maybe.getindex]
        @test get(Dict(:a => 1), :a) === Some(1)
        @test get(Dict(:a => 1), :b) === nothing
        @test get((11, 22), 1) === Some(11)
        @test get((11, 22), 3) === nothing
        @test get((11, 22), 0) === nothing
    end
end

@noinline safe_getindex0(xs) = Maybe.getindex(xs, 0)
unsafe_getindex0(xs) = @inbounds Maybe.getindex(xs, 0)

@testset "getindex" begin
    @test Maybe.getindex(ones(2, 3), 1, 1) === Some(1.0)
    @test Maybe.getindex(ones(2, 3), 1, 0) === nothing
    @test safe_getindex0(1:2) === nothing
    if Base.JLOptions().check_bounds != 1
        @test unsafe_getindex0(1:2) === Some(0)
    end
end

@testset "first" begin
    @test Maybe.first([]) === nothing
    @test Maybe.first([1]) === Some(1)
    @test Maybe.first(x for x in 1:10 if x < 0) === nothing
    @test Maybe.first(x for x in 1:10 if x > 0) === Some(1)
end

@testset "first" begin
    @test Maybe.last([]) === nothing
    @test Maybe.last([1]) === Some(1)
end

module Sample
const someproperty = 1
end  # module Sample

@testset "getproperty" begin
    @test Maybe.getproperty(Sample, :someproperty) === Some(1)
    @test Maybe.getproperty(Sample, :length) === Some(length)
    @test Maybe.getproperty(Sample, :__init__) === nothing
end

@testset "length" begin
    @test Maybe.length(1:2) === Some(2)
    @test Maybe.length(x for x in 1:0 if false) === nothing
end

end  # module


text:
using SparseArrays

function sparserecttest(lib::Polyhedra.Library)
    # Sparse H-representation, cartesian product
    # -1 ≤ x_1 ≤ 1
    # 2 ≤ x_2 ≤ 3
    h1 = HalfSpace(sparsevec([1], [ 1]),  1) ∩
         HalfSpace(sparsevec([1], [-1]),  1)
    h2 = HalfSpace(sparsevec([1], [ 1]),  3) ∩
         HalfSpace(sparsevec([1], [-1]), -2)
    h = h1 * h2
    v = convexhull([-1, 2],
                   [ 1, 2],
                   [-1, 3],
                   [ 1, 3])
    p = polyhedron(h, lib)
    @test !vrepiscomputed(p)
    inequality_fulltest(p, h)
    generator_fulltest(p, v)
    p1 = polyhedron(h1, lib)
    @test !vrepiscomputed(p1)
    p2 = polyhedron(h2, lib)
    @test !vrepiscomputed(p2)
    q = p1 * p2
    @test !vrepiscomputed(q)
    inequality_fulltest(q, h)
    generator_fulltest(q, v)
end


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

DocMeta.setdocmeta!(MusicProcessing, :DocTestSetup, :(using MusicProcessing); recursive=true)

makedocs(;
    modules=[MusicProcessing],
    authors="JuliaMusic",
    repo="github.com/JuliaMusic/MusicProcessing.jl/blob/{commit}{path}#{line}",
    sitename="MusicProcessing.jl",
    format=Documenter.HTML(;
        prettyurls=Base.get(ENV, "CI", "false") == "true",
        canonical="github.com/JuliaMusic/MusicProcessing.jl",
        assets=String[],
    ),
    pages=[
        "Home" => "index.md",
    ],
)

deploydocs(;
    repo="github.com/JuliaMusic/MusicProcessing.jl",
    devbranch="main",
    push_preview = true
)


text:
module SimpleFilters

using TinyModia
using DifferentialEquations
using ModiaPlot

SimpleModel = Model(
    T = 0.2,
    x = Var(init=0.5),
    equation = :[T * der(x) + x = 2]
)

# @showModel(SimpleModel)

model = @instantiateModel(SimpleModel)
simulate!(model, Tsit5(), stopTime = 5, requiredFinalStates = [1.9999996962023168])
plot(model, ["x"])

LowPassFilter = Model(
    T = 0.2,
    u = input,
    y = output | Var(:x),
    x = Var(init=0.0u"V"),
    equations = :[T * der(x) + x = u],
)

# @showModel(LowPassFilter)

HighPassFilter = LowPassFilter | Model(
        y = Var(:(-x + u)),
    )

# @showModel(HighPassFilter)

# setLogMerge(true)

LowAndHighPassFilter = LowPassFilter | Model(
    y = nothing,
    low = output | Var(:x),
    high = output | Var(:(-x + u)),
)

setLogMerge(false)

# @showModel(LowAndHighPassFilter)

TestLowAndHighPassFilter = LowAndHighPassFilter | Model(
    u = :(sin( (time+1u"s")*u"1/s/s" * time)*u"V"),
    x = Var(init=0.2u"V")
)

# @showModel(TestLowAndHighPassFilter)

model = @instantiateModel(TestLowAndHighPassFilter, log=false, logCode=false)
simulate!(model, Tsit5(), stopTime = 5, requiredFinalStates = [-0.22633046061014014])
plot(model, ["low", "high"])

TwoFilters = Model(
    high = HighPassFilter,
    low = LowPassFilter,
)

# @showModel(TwoFilters)

BandPassFilter = Model(
    u = input,
    y = output,
    high = HighPassFilter | Map(T=0.5, x=Var(init=0.1u"V")),
    low = LowPassFilter | Map(x=Var(init=0.2u"V")),
    equations = :[
        high.u = u,
        low.u = high.y,
        y = low.y]
)

# @showModel(BandPassFilter)

TestBandPassFilter = BandPassFilter | Model(
    u = :(sin( (0.05*time+1u"s")*u"1/s/s" * time)*u"V"),
)

# @showModel(TestBandPassFilter)

setLogMerge(false)

model = @instantiateModel(TestBandPassFilter, logDetails=false)
simulate!(model, Tsit5(), stopTime = 50, requiredFinalStates = [-0.25968254453053435, -0.6065869606784539])
plot(model, ["u", "y"])

end


text:
# https://www.redblobgames.com/grids/hexagons/implementation.html

module HexGreedTopologies

export HexGreedIndex, HexGreedTopology, hex_area_template, HexGreedIndexes, HexGreedNeighborhood, CELL_SIZE, cell_bounding_box, cell_center, cell_corners, manhattan_distance, ring_distance, euclidean_distance

using ...PCG.Types
using ...PCG.Geometry: Point, BoundingBox, Size
using ...PCG.Topologies
using ...PCG.Topologies: Topology, TopologyIndex, coordinates

const HexGreedSize = Int64

struct HexGreedIndex <: TopologyIndex
    q::HexGreedSize
    r::HexGreedSize
    s::HexGreedSize
end

Base.zero(::Type{HexGreedIndex}) = HexGreedIndex(0, 0, 0)
Base.zero(::HexGreedIndex) = HexGreedIndex(0, 0, 0)

Base.:+(a::HexGreedIndex, b::HexGreedIndex) = HexGreedIndex(a.q + b.q,
                                                            a.r + b.r,
                                                            a.s + b.s)

Base.:-(a::HexGreedIndex, b::HexGreedIndex) = HexGreedIndex(a.q - b.q,
                                                            a.r - b.r,
                                                            a.s - b.s)

Base.:*(a::HexGreedIndex, b::Number) = HexGreedIndex(a.q * b,
                                                     a.r * b,
                                                     a.s * b)

HexGreedIndexQR(q::HexGreedSize, r::HexGreedSize) = HexGreedIndex(q, r, -q-r)

const HexGreedIndexes = Vector{HexGreedIndex}

const DIRECTIONS = [HexGreedIndex(1, 0, -1),
                    HexGreedIndex(1, -1, 0),
                    HexGreedIndex(0, -1, 1),
                    HexGreedIndex(-1, 0, 1),
                    HexGreedIndex(-1, 1, 0),
                    HexGreedIndex(0, 1, -1)]

function neighborof(i::HexGreedIndex, n::Integer)
    # TODO: change to "n % 6"
    return i + DIRECTIONS[n]
end

function Types.neighborsof(i::HexGreedIndex)
    # TODO: rewrite to "." operation
    return [i + di for di in DIRECTIONS]
end

struct Orientation
    f0::Float64
    f1::Float64
    f2::Float64
    f3::Float64

    start_angle::Float64
end

# const LAYOUT_POINTY = Orientation(sqrt(3.0), sqrt(3.0) / 2.0, 0.0, 3.0 / 2.0, 0.5)

const LAYOUT_POINTY = Orientation(3.0 / 2.0, 0.0, sqrt(3.0) /2.0, sqrt(3.0), 0.0)

function cell_corner_offset(corner::Integer)
    angle = 2.0 * pi * (LAYOUT_POINTY.start_angle + (corner-1)) / 6
    return Point(cos(angle), sin(angle))
end

const CELL_CORNERS_OFFSETS = [cell_corner_offset(i) for i in 1:6]

function normal_cell_size()
    min_x, max_x = 0, 0
    min_y, max_y = 0, 0

    for corner in CELL_CORNERS_OFFSETS
        min_x = min(min_x, corner.x)
        max_x = max(max_x, corner.x)
        min_y = min(min_y, corner.y)
        max_y = max(max_y, corner.y)
    end

    return Size(max_x - min_x, max_y - min_y)
end

function cell_center(cell::HexGreedIndex)
    x = (LAYOUT_POINTY.f0 * cell.q + LAYOUT_POINTY.f1 * cell.r)
    y = (LAYOUT_POINTY.f2 * cell.q + LAYOUT_POINTY.f3 * cell.r)
    return Point(x, y)
end

function cell_corners(cell::HexGreedIndex)
    center = cell_center(cell)
    return [center + offset for offset in CELL_CORNERS_OFFSETS]
end

function cell_bounding_box(cell)
    corners = cell_corners(cell)
    return BoundingBox(corners)
end

# TODO: do not export or rename
const CELL_SIZE = normal_cell_size()

function cells_hexagon(radius)
    return (HexGreedIndexQR(q, r)
            for q in (-radius:radius)
            for r in max(-radius, -q - radius):min(radius, -q + radius))
end

struct HexGreedTopology <: Topology
    radius::HexGreedSize
end

Topologies.index_type(::HexGreedTopology) = HexGreedIndex

function Topologies.is_valid(topology::HexGreedTopology, index::HexGreedIndex)
    return hex_length(index) <= topology.radius
end

# TODO: check if indexes generated in column-first order
function Topologies.coordinates(topology::HexGreedTopology)
    return cells_hexagon(topology.radius)
end

function hex_length(cell::HexGreedIndex)
    return trunc(HexGreedSize, (abs(cell.q) + abs(cell.r) + abs(cell.s)) / 2)
end

function manhattan_distance(a::HexGreedIndex, b::HexGreedIndex=zero(HexGreedIndex))
    return hex_length(a - b)
end

function ring_distance(a::HexGreedIndex, b::HexGreedIndex=zero(HexGreedIndex))
    index = a - b
    return max(abs(index.q), abs(index.r), abs(index.s))
end

function euclidean_distance(a::HexGreedIndex, b::HexGreedIndex=zero(HexGreedIndex))
    p = cell_center(a)
    q = cell_center(b)
    return sqrt((p.x-q.x)^2 + abs(p.y-q.y)^2)
end

end

max_stars_repo_name: Tiendil/pcg-julia

max_issues_repo_name: Tiendil/pcg-julia

max_forks_repo_name: Tiendil/pcg-julia


text:
@testset "message passing" begin
    in_channel = 10
    out_channel = 5
    num_V = 6
    num_E = 14
    T = Float32

    adj =  [0 1 0 0 0 0
            1 0 0 1 1 1
            0 0 0 0 0 1
            0 1 0 0 1 0
            0 1 0 1 0 1
            0 1 1 0 1 0]

    X = rand(T, in_channel, num_V)
    E = rand(T, in_channel, num_E)

    g = GNNGraph(adj, graph_type=GRAPH_T)

    @testset "propagate" begin
        function message(xi, xj, e)
            @test xi === nothing
            @test e === nothing
            ones(T, out_channel, size(xj, 2))
        end

        m = propagate(message, g, +, xj=X)

        @test size(m) == (out_channel, num_V)

        @testset "isolated nodes" begin
            x1 = rand(1, 6)
            g1 = GNNGraph(collect(1:5), collect(1:5), num_nodes=6)
            y1 = propagate((xi,xj,e) -> xj, g, +, xj=x1)
            @test size(y1) == (1, 6)
        end
    end

    @testset "apply_edges" begin
        m = apply_edges(g, e=E) do xi, xj, e
                @test xi === nothing
                @test xj === nothing
                ones(out_channel, size(e, 2))
            end

        @test m == ones(out_channel, num_E)

        # With NamedTuple input
        m = apply_edges(g, xj=(;a=X, b=2X), e=E) do xi, xj, e
                @test xi === nothing
                @test xj.b == 2*xj.a
                @test size(xj.a, 2) == size(xj.b, 2) == size(e, 2)
                ones(out_channel, size(e, 2))
            end

        # NamedTuple output
        m = apply_edges(g, e=E) do xi, xj, e
            @test xi === nothing
            @test xj === nothing
            (; a=ones(out_channel, size(e, 2)))
        end

        @test m.a == ones(out_channel, num_E)
    end

    @testset "copy_xj" begin

        n = 128
        A = sprand(n, n, 0.1)
        Adj = map(x -> x > 0 ? 1 : 0, A)
        X = rand(10, n)

        g = GNNGraph(A, ndata=X, graph_type=GRAPH_T)

        function spmm_copyxj_fused(g)
            propagate(
                copy_xj,
                g, +; xj=g.ndata.x
                )
        end

        function spmm_copyxj_unfused(g)
            propagate(
                (xi, xj, e) -> xj,
                g, +; xj=g.ndata.x
                )
        end

        @test spmm_copyxj_unfused(g) ≈ X * Adj
        @test spmm_copyxj_fused(g) ≈ X * Adj
    end

    @testset "e_mul_xj and w_mul_xj for weighted conv" begin
        n = 128
        A = sprand(n, n, 0.1)
        Adj = map(x -> x > 0 ? 1 : 0, A)
        X = rand(10, n)

        g = GNNGraph(A, ndata=X, edata=A.nzval, graph_type=GRAPH_T)

        function spmm_unfused(g)
            propagate(
                (xi, xj, e) -> reshape(e, 1, :) .* xj ,
                g, +; xj=g.ndata.x, e=g.edata.e
                )
        end
        function spmm_fused(g)
            propagate(
                e_mul_xj,
                g, +; xj=g.ndata.x, e=g.edata.e
                )
        end

        function spmm_fused2(g)
            propagate(
                w_mul_xj,
                g, +; xj=g.ndata.x
                )
        end

        @test spmm_unfused(g) ≈ X * A
        @test spmm_fused(g) ≈ X * A
        @test spmm_fused2(g) ≈ X * A
    end
end


text:
iscontseq(n::Integer) = count_zeros(n) == leading_zeros(n) + trailing_zeros(n)
iscontseq(n::BigInt) = !ismatch(r"0", rstrip(bin(n), '0'))

function makeint2seq(n::Integer)
    const idex = collect(1:n)
    function int2seq(m::Integer)
        d = digits(m, 2, n)
        idex[d .== 1]
    end
    return int2seq
end

immutable NCSubSeq{T<:Integer}
    n::T
end

type NCSubState{T<:Integer}
    m::T
    m2s::Function
end

Base.length(a::NCSubSeq) = 2^a.n - div(a.n*(a.n+1), 2) - 1
Base.start(a::NCSubSeq) = NCSubState(5, makeint2seq(a.n))
Base.done(a::NCSubSeq, as::NCSubState) = 2^a.n-3 < as.m

function Base.next(a::NCSubSeq, as::NCSubState)
    s = as.m2s(as.m)
    as.m += 1
    while iscontseq(as.m)
        as.m += 1
    end
    return (s, as)
end

max_stars_repo_name: ethansaxenian/RosettaDecode

max_issues_repo_name: ethansaxenian/RosettaDecode

max_forks_repo_name: ethansaxenian/RosettaDecode


text:
#Function to check Wigner-6j conditions
#23/07/2017

function Wigner6jcheck(j1,j2,j3,J1,J2,J3)

  Triad = [j1 j2 j3;j1 J2 J3;J1 j2 J3;J1 J2 j3]

  tfvec = zeros(8)

  #Check integer condition
  for kk = 1:4
    tfvec[kk] = floor(sum(Triad[kk,:]))==sum(Triad[kk,:])
  end

  #Check triangle inequalities
  for kk = 1:4
    if abs(Triad[kk,1]-Triad[kk,2])<=Triad[kk,3]&&Triad[kk,3]<=(Triad[kk,1]+Triad[kk,2])
      tfvec[kk+4] = 1
    end
  end

  tfsum = sum(tfvec)

  if tfsum==8
    return check = 1
  else
    return check = 0
  end
end

max_stars_repo_name: CSChisholm/Rydberg

max_issues_repo_name: CSChisholm/Rydberg

max_forks_repo_name: CSChisholm/Rydberg


text:
import .AbstractPlotting

function implicit_plot(f, ::Val{3}; resolution = (800, 800), kwargs...)
    scene = AbstractPlotting.Scene(resolution = resolution, scale_plot = false)
    implicit_surface!(scene, f; kwargs...)
end
implicit_plot!(p, f, ::Val{3}; kwargs...) = implicit_surface!(p, f; kwargs...)

"""
    implicit_surface!(scene, f; xlims=(-3,3), ylims = xlims, zlims = xlims,
                      color=:steelblue, resolution=1000)

Visualize the implicit suface `f` in the box `[x_min, x_max] × [y_min, y_max] × [z_min, z_max]`.
"""
function implicit_surface!(
    scene,
    f;
    x_min = -3.0,
    xmin = x_min,
    x_max = 3.0,
    xmax = x_max,
    y_min = xmin,
    ymin = y_min,
    y_max = xmax,
    ymax = y_max,
    z_min = xmin,
    zmin = z_min,
    z_max = xmax,
    zmax = z_max,
    xlims = (xmin, xmax),
    ylims = (ymin, ymax),
    zlims = (zmin, zmax),
    color = :steelblue,
    show_axis = true,
    wireframe = false,
    mesh_resolution = 0.04,
    grid = true,
    kwargs...,
)
    g = make_function(f)
    box = GeometryTypes.HyperRectangle(
        AbstractPlotting.Vec(xlims[1], ylims[1], float(zlims[1])),
        AbstractPlotting.Vec(
            float(xlims[2] - xlims[1]),
            ylims[2] - ylims[1],
            zlims[2] - zlims[1],
        ),
    )
    sdf = Meshing.SignedDistanceField(v -> g(v...), box, mesh_resolution)
    m = Meshing.GLNormalMesh(sdf, Meshing.MarchingTetrahedra())
    if wireframe === nothing || !wireframe
        AbstractPlotting.mesh!(
            scene,
            m;
            show_axis = show_axis,
            color = color,
            kwargs...,
        )
    else
        AbstractPlotting.wireframe!(scene, m; show_axis = show_axis, kwargs...)
    end

    scene
end

max_issues_repo_name: saschatimme/ImplicitPlots

max_forks_repo_name: saschatimme/ImplicitPlots


text:
using HDF5,JLD,KUparser
@date d = load("conll07.tst.jld4")
@date ndeps = length(d["deprel"])
@date s1 = d["corpus"][1]
@date p1 = KUparser.Parser{:ArcHybrid13}(wcnt(s1), ndeps)
@date r0 = rparse(:ArcHybrid13, d["corpus"], ndeps)
@show evalparse(r0, d["corpus"])
@date f1 = Flist.tacl13hybrid
@date r1 = oparse(:ArcHybrid13, d["corpus"], f1, ndeps)
@show evalparse(r1[1], d["corpus"])
@date f2 = Flist.acl11eager
@date r2 = oparse(:ArcHybrid13, d["corpus"], f2, ndeps)
@show evalparse(r2[1], d["corpus"])
@date net = KUnet.newnet(KUnet.relu, KUparser.flen(p1,s1,f1), 20000, p1.nmove)
@date r3 = gparse(:ArcHybrid13, d["corpus"], net, f1, ndeps)
@show evalparse(r3[1], d["corpus"])
@date r4 = gparse(:ArcHybrid13, d["corpus"], net, f1, ndeps, 9999)
@show evalparse(r4[1], d["corpus"])
:ok


text:
using PtFEM

data = Dict(
  # Plane(ndim, nst, nxe, nye, nip, direction, finite_element(nod, nodof), axisymmetric)
  :struc_el => Plane(2, 3, 3, 2, 4, :y, Quadrilateral(4, 2), false),
  :properties => [1.0e6 0.3;],
  :x_coords => [0.0, 10.0, 20.0, 30.0],
  :y_coords => [0.0, -5.0, -10.0],
  :support => [
    (1, [0 1]),
    (2, [0 1]),
    (3, [0 0]),
    (6, [0 0]),
    (9, [0 0]),
    (10, [0 1]),
    (11, [0 1]),
    (12, [0 0])
    ],
  :fixed_freedoms => [
    (1, 2, -1.0e-5),
    (4, 2, -1.0e-5)
    ]
)

data |> display
println()

@time m, dis_df, fm_df = p51(data)
println()

dis_df |> display
println()

println("\nThe integration point (nip = $(data[:struc_el].nip)) stresses are:")
fm_df |> display
println()


text:
mutable struct SpaceTimeBasis{S,T}
    space::S
    time::T
end

spatialbasis(s::SpaceTimeBasis) = s.space
temporalbasis(s::SpaceTimeBasis) = s.time

⊗(a, b) = SpaceTimeBasis(a,b)
numfunctions(S::SpaceTimeBasis) = numfunctions(S.space) * numfunctions(S.time)
scalartype(st::SpaceTimeBasis) = promote_type(scalartype(st.space), scalartype(st.time))


text:
# SPD-License-Identifier: MIT

using ArgParse
using CUDA
using HDF5
using JLD2
using Knet
using ProgressMeter
using TOML

# Data loader to load batch of utterances.
include("dataloader.jl")

function main(args)
    use_gpu = args["use-gpu"]
    model = load(args["model"])["model"]
    getlengths_fn = s -> getlengths(TOML.parsefile(args["modelconfig"]), s)

    if args["use-gpu"]
        model = model |> gpu
    end

    testmode!(model)

    h5open(args["features"], "r") do fin
        h5open(args["out"], "w") do fout
            bl = DumpBatchLoader(fin, args["batch-size"])
            prog = Progress(length(bl))
            for (batch_data, inlengths, uttids) in bl
                if args["use-gpu"] batch_data = CuArray(batch_data) end

                seqlengths = getlengths_fn(inlengths)
                Y = Array(model(batch_data))
                for i in 1:size(batch_data, 3)
                    Yᵢ = Y[:, 1:seqlengths[i], i]
                    fout[uttids[i]] = Yᵢ
                end
                next!(prog)
            end
        end
    end
end

function parse_commandline()
    s = ArgParseSettings()
    @add_arg_table s begin
        "--use-gpu", "-g"
            action = :store_true
            help = "train the model on gpu"
        "--batch-size", "-b"
            arg_type = Int
            default = 50
            help = "batch size"
        "modelfile"
            required = true
            help = "model definition file"
        "modelconfig"
            required = true
            help = "model configuration file in TOML format"
        "model"
            required = true
            help = "input model saved in JLD2 format"
        "features"
            required = true
            help = "input features in an HDF5 archive"
        "out"
            required = true
            help = "output HDF5 archive"
    end
    s.description = """
    Dump the output of the neural network.
    """
    parse_args(s)
end

args = parse_commandline()
include(args["modelfile"])
main(args)

max_stars_repo_name: MartinKocour/SpeechLab

max_issues_repo_name: MartinKocour/SpeechLab

max_forks_repo_name: MartinKocour/SpeechLab


text:
extcode(v::PackedVector) = extcode(eltype(v.data))
extcode(::Type{UInt8}) = 0x12
extcode(::Type{Int32}) = 0x15
extcode(::Type{UInt32}) = 0x16
extcode(::Type{Float32}) = 0x17

pack(io::IO, v::PackedVector) = pack(io, Ext(extcode(v), reinterpret(UInt8, v.data, (sizeof(v.data),))))


text:
module PrimitiveBits

#   This file is part of PrimitiveBits.jl. It is licensed under the MIT license
#   Copyright (C) 2019 Michael Reed

function Declare(b::Int)
    sym = Symbol("PrimitiveBits$b")
    int = Symbol("UInt$b")
    def = Expr(:primitive,sym,b)
    exp = Expr(:export,sym)
    fun = quote
        $sym(b::$int) = reinterpret($sym,b)
        Base.$int(b::$sym) = reinterpret($int,b)
        function Base.getindex(b::$sym,i::I) where I<:Integer
            d = one($int) << (i-1)
            return (d & $int(b)) == d
        end
        Base.getindex(b::$sym,i::UnitRange{<:Integer}) = [getindex(b,j) for j ∈ i]
        Base.getindex(b::$sym,i::Colon) = [getindex(b,j) for j ∈ 1:$b]
        Base.firstindex(::$sym) = 1
        Base.lastindex(::$sym) = $b
        Base.length(::$sym) = $b
        function Base.iterate(b::$sym,i::Int=1)
            Base.@_inline_meta
            length(r) < i && return nothing
            Base.unsafe_getindex(b,i), i + 1
        end
        $sym(b::I) where I<:Integer = $sym(convert($int,b))
        function $sym(b::Union{BitArray{1},Vector{Bool}})
            $sym(parse($int,join([t ? '1' : '0' for t ∈ reverse(b)]),base=2))
        end
        Base.print(io::IO,b::$sym) = print(io,'[',[Int(b[i]) for i ∈ 1:$b]...,']')
        Base.show(io::IO,b::$sym) = print(io,b)
    end
    eval(Expr(:block,def,exp,fun.args...))
end

for b ∈ [8,16,32,64,128]
    Declare(b)
end

end # module


text:
# Pattern matching for clauses and nodes.

macro dissect(val, pat)
    esc(dissect(val, pat))
end

function dissect(@nospecialize(val), @nospecialize(pat))
    pat !== :_ || return :(true)
    scr = gensym(:scr)
    ex = dissect(scr, pat)
    :(local $scr = $val; $ex)
end

function dissect(scr::Symbol, @nospecialize(pat))
    if pat isa Symbol
        if pat === :_
            :(true)
        elseif pat === :nothing || pat === :missing
            :($scr === $pat)
        else
            :(local $pat = $scr; true)
        end
    elseif pat isa Bool
        :($scr === $pat)
    elseif pat isa QuoteNode && pat.value isa Symbol
        :($scr === $pat)
    elseif pat isa Expr
        nargs = length(pat.args)
        if pat.head === :(:=) && nargs == 2
            ex1 = dissect(scr, pat.args[1])
            ex2 = dissect(scr, pat.args[2])
            :($ex2 && $ex1)
        elseif pat.head === :&& || pat.head === :||
            Expr(pat.head, Any[dissect(scr, arg) for arg in pat.args]...)
        elseif pat.head === :kw && nargs == 2
            dissect(:($scr.$(pat.args[1])), pat.args[2])
        elseif pat.head === :call && nargs >= 1 &&
                                     (local f = pat.args[1]; f isa Symbol)
            dissect(scr, getfield(FunSQL, f), pat.args[2:end])
        else
            error("invalid pattern: $(repr(pat))")
        end
    else
        error("invalid pattern: $(repr(pat))")
    end
end

function dissect(scr::Symbol, ::typeof(|>), pats::Vector{Any})
    if length(pats) == 2 && (local call = pats[2]; call isa Expr)
        if call.head === :call
            pat = Expr(:call, call.args..., Expr(:kw, :over, pats[1]))
            return dissect(scr, pat)
        elseif call.head === :||
            return Expr(call.head, Any[dissect(scr, Expr(:call, :|>, pats[1], arg))
                                       for arg in call.args]...)
        end
    end
    error("invalid pattern: $(repr(pats))")
end


text:
# The recommended way to prallelize the simulation is to
# run the following bash command:
#seq 20 40 | xargs --max-args=1 --max-procs=4 julia sim_run.jl &>> runlog1.txt

#Parallelization for multiprocessing (optional)
#using Distributed
#w = 2
#Distributed.addprocs(w)
#@everywhere begin
begin
    using Random
    using Dates
    using Distributed
    using Printf
    using Nanocsv
    using OpenStreetMapX
    using DataFrames
    using OpenStreetMapXSim
    sim_mode = "flows";
    version="002"
    resultspath="/home/ubuntu/results/"
    datapath = "/home/ubuntu/datasets/"

    N = 100;
    max_jobs_worker=1
end

include("simparams/_loadparams.jl")
sim_data = get_sim_data(datapath);

OpenStreetMapXSim.run_dist_sim(resultspath,version,1,N,max_jobs_worker,sim_data, sim_mode,demographic_profile,additional_activity,nothing,s3action=nothing)


text:
ENV["GKSwstype"] = "nul"  # For StatsPlots
println("pid: ", getpid())

# Read command line args.
if length(ARGS) > 1
  resultsdir = ARGS[1]
  awsbucket = ARGS[2]
  snum = parse(Int, ARGS[3])
  println("ARGS: ", ARGS)
else
  resultsdir = "results/test/"
  awsbucket = nothing
  snum = 1
end
flush(stdout)

# Create results/images dir if needed
imgdir = "$(resultsdir)/img"
mkpath(imgdir)

import Pkg; Pkg.activate(joinpath(@__DIR__, "../../../"))

using CytofDensityEstimation
const CDE = CytofDensityEstimation
using LaTeXStrings
using Distributions
using StatsPlots
using HypothesisTests
import Random
using BSON

include("scenarios.jl")

# Simulate data.
Random.seed!(2);
simdata = scenarios(snum)
yC, yT = simdata[:yC], simdata[:yT]

# Plot data.
density(yC[isfinite.(yC)],  lw=3, label=L"y_C", color=:blue)
density!(yT[isfinite.(yT)], lw=3, label=L"y_T", color=:red)
savefig(joinpath(imgdir, "data.pdf"))
closeall()

# Define data, prior, and initial state.
Random.seed!(1)
K = 6
data = CDE.Model.Data(yC, yT)
prior_mu = let
  yfinite = [data.yC_finite; data.yT_finite]
  Normal(mean(yfinite), std(yfinite)*2)
end
prior = CDE.Model.Prior(K, mu=prior_mu, nu=LogNormal(4, .01), p=Beta(.1, .9))
state = CDE.Model.State(data, prior)
tuners = CDE.Model.Tuners(K)

println("Priors:")
for fn in fieldnames(CDE.Model.Prior)
  println(fn, " => ", getfield(prior, fn))
end

# Parameters to fix
fix = Symbol[]

# Run chain
init = deepcopy(state)
@time chain, laststate, summarystats = CDE.Model.fit(init, data, prior, tuners,
                                                     nsamps=[1000], nburn=1000,
                                                     fix=fix)

# Save results
BSON.bson("$(resultsdir)/results.bson",
          Dict(:chain=>chain, :laststate=>laststate,
               :summarystats=>summarystats, :simdata=>simdata))

# Load via:
# using BSON, CytofDensityEstimation
out = BSON.load("$(resultsdir)/results.bson")

# Print KS statistic.
ks_fit = ApproximateTwoSampleKSTest(yC, yT)
println(ks_fit)
flush(stdout)

# Print summary statistics.
CDE.Model.printsummary(out[:chain], out[:summarystats])
flush(stdout)

# Plot results.
@time CDE.Model.plotpostsummary(out[:chain], out[:summarystats], yC, yT, imgdir,
                                simdata=out[:simdata], bw_postpred=.1)
flush(stdout)

if awsbucket != nothing
  CDE.Util.s3sync(from=resultsdir, to=awsbucket, tags=`--exclude '*.nfs'`)
end

println("Done!")
flush(stdout)

max_stars_repo_name: luiarthur/CytofDensityEstimation

max_issues_repo_name: luiarthur/CytofDensityEstimation

max_forks_repo_name: luiarthur/CytofDensityEstimation


text:
using ParetoSmooth
using Test

@testset "ParetoSmooth.jl" begin

    include("tests/BasicTests.jl")
    include("tests/TuringTests.jl")
    include("tests/ComparisonTests.jl")

end