text:
intrng(s::Spectrum) = s[s.default_proc].intrng

function intrng_indices(s::Spectrum)
    rng = intrng(s)
    [ppmtoindex(s,i[1]):ppmtoindex(s,i[2]) for i in rng]
end

function intrng_data(s::Spectrum)
    [@view s[r] for r in intrng_indices(s)]
end

function intrng_shifts(s::Spectrum)
    rng = intrng(s)
    [range(i[1]; stop=i[2], length=ppmtoindex(s,i[2])-ppmtoindex(s,i[1])+1) for i in rng]
end

function find_rng(p::ProcessedSpectrum, δ::Float64)
    for (i,(hi,lo)) in enumerate(p.intrng)
        if lo < δ < hi
            return i
        end
    end
end

find_rng(s::Spectrum, δ::Float64) = find_rng(s[s.default_proc], δ)

function remove_rng(sp::Union{Spectrum,ProcessedSpectrum}, δ::Float64)
    t = deepcopy(sp)
    remove_rng!(t, δ)
    t
end

remove_rng!(s::Spectrum, δ::Float64) = remove_rng!(s[s.default_proc], δ)
remove_rng!(p::ProcessedSpectrum, δ::Float64) = remove_rng!(p, find_rng(p, δ))
remove_rng!(p::ProcessedSpectrum, n::Int) = deleteat!(p.intrng, n)
remove_rng!(p::ProcessedSpectrum, x::Nothing) = nothing

integrate(v::Vector, r::UnitRange) = sum(v[r])
integrate(p::ProcessedSpectrum, r::UnitRange) = integrate(p[:], r)
integrate(s::Spectrum, r::UnitRange) = integrate(s[s.default_proc], r)

function integrate(s::Spectrum, ppm_range::Tuple{Float64,Float64})
    r1 = ppmtoindex(s, ppm_range[1])
    r2 = ppmtoindex(s, ppm_range[2])
    integrate(s, r1:r2)
end

function integrate(s::Spectrum)
    [integrate(s, r) for r in intrng(s)]
end

function integrate(s::Spectrum, ref_rng::Int)
    rngs = intrng(s)
    ref_int = integrate(s, rngs[ref_rng])
    integrate(s)./ref_int
end

integrate(s::Spectrum, δ::Float64) = integrate(s, find_rng(s, δ))


text:
using TensorDSL
using BenchmarkTools
using PyCall

dim = 60
V = VectorSpace(dim)
A = VariableTensor("A", V, V', RnSpace(6))
x = FreeIndex(V, "x")
knl = Kernel(((4*A)[x, x']⊗transpose(A))[1,2, x', x])

val = permutedims(rand(Float64, (dim, dim, 6)), [i for i in reverse(collect(1:length(A.shape)))])

b = @benchmarkable execute($knl, "A"=>$val)
run(b)

ext: jl

max_stars_repo_name: Sohva/oolong

max_issues_repo_name: Sohva/oolong

max_forks_repo_name: oolongtensor/oolong


text:
module RocketReduceOperatorTest

using Test
using Rocket

include("../test_helpers.jl")

@testset "operator: reduce()" begin

    println("Testing: operator reduce()")

    run_proxyshowcheck("Reduce", reduce(Any, +, 0))
    run_proxyshowcheck("ReduceNoSeed", reduce(+))

    run_testset([
        (
            source      = from(1:5) |> reduce(Int, +, 1),
            values      = @ts([ 16, c ]),
            source_type = Int
        ),
        (
            source      = from(1:5) |> reduce(+),
            values      = @ts([ 15, c ]),
            source_type = Int
        ),
        (
            source      = from(1:5) |> reduce(Vector{Int}, (d, c) -> [ c..., d ], Int[]),
            values      = @ts([ [1, 2, 3, 4, 5], c ]),
            source_type = Vector{Int}
        ),
        (
            source      = completed(Int) |> reduce(+),
            values      = @ts(c),
            source_type = Int
        ),
        (
            source      = completed(Int) |> reduce(Int, +, 2),
            values      = @ts([ 2, c ]),
            source_type = Int
        ),
        (
            source      = faulted("e") |> reduce(+),
            values      = @ts(e("e")),
        ),
        (
            source = never() |> reduce(+),
            values = @ts()
        )
    ])
end

end


text:
@testset "rounding mode" begin
  ArbNumerics.match_rounding_mode(RoundNearest) == 4
end


text:
# Copyright (c) 2020 Idiap Research Institute, http://www.idiap.ch/
#  Niccolò Antonello <nantonel@idiap.ch>

T = Float32
Nt = 20
Ns = 5
a = rand(T,Ns)   # initial state probability
a ./= norm(a,1)
A = rand(T,Ns,Ns)
for i in 1:Ns A[i,:] ./= norm(A[i,:],1) end
@assert HMMGradients.is_transition_matrix(A)
A = ones(T,Ns,Ns)
y = rand(T,Nt,Ns)

A, a, y = log.(A), log.(a), log.(y)
A, a, y = Float32.(A), Float32.(a), Float32.(y)

logalpha, logML = logforward(Nt,a,A,y)
logbeta = logbackward(Nt,A,y)
loggamma = logposterior(Nt,a,A,y)
gamma = posterior(Nt,exp.(a),exp.(A),exp.(y))
alpha,c = forward(Nt,exp.(a),exp.(A),exp.(y))
beta = backward(Nt,exp.(A),c,exp.(y))

grada = -T(Inf).*ones(T,Ns)
gradA = -T(Inf).*ones(T,Ns,Ns)
grady = -T(Inf).*ones(T,Nt,Ns)
betat1 = zeros(T,Ns)
betat0 = -T(Inf).*ones(T,Ns)
grada, gradA, grady = HMMGradients.get_loggrad!(grada,gradA,grady,betat0,betat1,Nt,a,A,y,logalpha,logML)
grada2, gradA2, grady2 = HMMGradients.get_loggrad(Nt,a,A,y,logalpha,logML)
A, a, y = Float64.(A), Float64.(a), Float64.(y) # needs double precision
grada_fd = gradient_fd(a -> nlogMLlog(Nt,a,A,y),a)
gradA_fd = gradient_fd(A -> nlogMLlog(Nt,a,A,y),A)
grady_fd = gradient_fd(y -> nlogMLlog(Nt,a,A,y),y)
grada_an = -1/Nt .* ( gamma[1,:] )
gradA_an = -1/(Nt*exp(logML*Nt)).*sum([exp(logalpha[t,i] + A[i,j] + logbeta[t+1,i] + y[t+1,j]) for i=1:Ns, j=1:Ns] for t = 1:Nt-1)
grady_an = -1/Nt .* ( gamma )
fy,p = ChainRulesCore.rrule(nlogMLlog, Nt, a, A, y)
_,_,grada3,gradA3,grady3 = p(1.0)

if iseven(Nt)
  @test norm(logbeta[1,:]-betat0) < 1e-5
else
  @test norm(logbeta[1,:]-betat1) < 1e-5
end
@test norm(grada   - grada_fd, Inf)/norm(grada_fd) < 1e-4
@test norm(gradA   - gradA_fd, Inf)/norm(gradA_fd) < 1e-4
@test norm(grady   - grady_fd, Inf)/norm(grady_fd) < 1e-4
@test norm(grada2  - grada_fd, Inf)/norm(grada_fd) < 1e-4
@test norm(gradA2  - gradA_fd, Inf)/norm(gradA_fd) < 1e-4
@test norm(grady2  - grady_fd, Inf)/norm(grady_fd) < 1e-4
@test norm(grada3  - grada_fd, Inf)/norm(grada_fd) < 1e-4
@test norm(gradA3  - gradA_fd, Inf)/norm(gradA_fd) < 1e-4
@test norm(grady3  - grady_fd, Inf)/norm(grady_fd) < 1e-4
@test norm(grada_an  - grada_fd, Inf)/norm(grada_fd) < 1e-4
@test norm(gradA_an  - gradA_fd, Inf)/norm(gradA_fd) < 1e-4
@test norm(grady_an  - grady_fd, Inf)/norm(grady_fd) < 1e-4

# test with zeropadded input
Nz = 20
y2 = [y;zeros(eltype(y),Nz,size(y,2))]
logalpha, logML = logforward(Nt,a,A,y2)
grada,gradA,grady = HMMGradients.get_loggrad(Nt,a,A,y2,logalpha,logML)
grady_fd = gradient_fd(y->nlogMLlog(Nt,a,A,y),Float64.(y2)) # needs double precision

@test size(grady) == size(y2)
@test norm(grada  - grada_fd, Inf)/norm(grada_fd) < 1e-4
@test norm(gradA  - gradA_fd, Inf)/norm(gradA_fd) < 1e-4
@test norm( grady  - grady_fd, Inf)/norm(grady_fd) < 1e-5
@test sum(grady[end-(Nz-1):end,:]) == 0.0

## test with batch Nt × Ns × Nb
T = Float32
Nb = 3
Ns = 10
Nts = [10,15,20]
A = log.(rand(T,Ns,Ns))
As = zeros(T,Ns,Ns,Nb)
for i = 1:Nb As[:,:,i] .= A end
as = log.(rand(T,Ns,Nb))
ys = log.(rand(T,maximum(Nts),Ns,Nb))
logalphas,logMLs = logforward(Nts,as,As,ys)
logalphas_logMLs = [logforward(Nts[i],as[:,i],As[:,:,i],ys[:,:,i]) for i=1:Nb]
@test all([norm(logalphas[i]-logalphas_logMLs[i][1])<1e-5 for i=1:Nb] )
@test all([norm(logMLs[i]-logalphas_logMLs[i][2])<1e-5 for i=1:Nb] )
@test all(length(logMLs) .== Nb)
@test all(size.(logalphas,2) .== Ns)
@test all(size.(logalphas,1) .== Nts)

grada, gradA, grady = HMMGradients.get_loggrad(Nts,as,As,ys,logalphas,logMLs)
grada_gradA_grady = [HMMGradients.get_loggrad(Nts[i],as[:,i],As[:,:,i],ys[:,:,i],logalphas[i],logMLs[i]) for i=1:Nb]
fy,p = ChainRulesCore.rrule(nlogMLlog, Nts, as, As, ys)
_,_,grada2,gradA2,grady2 = p(1.0)

@test -sum(logMLs) ≈ fy
@test all([norm(grada[:,i]-grada_gradA_grady[i][1]) for i = 1:Nb] .< 1e-5)
@test all([norm(gradA[:,:,i]-grada_gradA_grady[i][2]) for i = 1:Nb] .< 1e-5)
@test all([norm(grady[:,:,i]-grada_gradA_grady[i][3]) for i = 1:Nb] .< 1e-5)
@test all([norm(grada2[:,i]-grada_gradA_grady[i][1]) for i = 1:Nb] .< 1e-5)
@test all([norm(gradA2[:,:,i]-grada_gradA_grady[i][2]) for i = 1:Nb] .< 1e-5)
@test all([norm(grady2[:,:,i]-grada_gradA_grady[i][3]) for i = 1:Nb] .< 1e-5)

# constrained path
T = Float32
A = T[0.0 1.0 0.0; 0.0 0.5 0.5; 1.0 0.0 0.0]
t2tr = Dict{Int,Array{Int,1}}[Dict(1 => [2]), Dict(2 => [3, 2]), Dict(2 => [3, 2],3 => [1]), Dict(2 => [3, 2],3 => [1],1 => [2]), Dict(2 => [3, 2],3 => [1],1 => [2]), Dict(2 => [3, 2],3 => [1],1 => [2]), Dict(2 => [2],3 => [1],1 => [2]), Dict(2 => [2],1 => [2]), Dict(2 => [3])]
t2IJ= HMMGradients.t2tr2t2IJ(t2tr)

Nt = length(t2tr)+1
Ns = size(A,1)
y = rand(T,Nt,Ns)
A, y = log.(A), log.(y)

logalpha, logML = logforward(Nt,t2IJ,A,y)

logbeta = logbackward(Nt,A,t2IJ,y)

loggamma = logposterior(Nt,t2IJ,A,y)
@test all(sum( exp.(loggamma), dims=2) .≈ 1.0)

grada = zero(T)
gradA = zero(T)
grady = -T(Inf) .* ones(T,size(y,1),Ns)
betat1 = zeros(T,Ns)
betat0 = -T(Inf).*ones(T,Ns)
_,_,grady  = HMMGradients.get_loggrad!(grada,gradA,grady,betat0,betat1,Nt,t2IJ,A,y,logalpha,logML)
_,_,grady2 = HMMGradients.get_loggrad(Nt,t2IJ,A,y,logalpha,logML)

fy,p = ChainRulesCore.rrule(nlogMLlog, Nt, t2IJ, A, y)
_,_,grada3,gradA3,grady3 = p(1.0)

A,y = Float64.(A), Float64.(y) # needs double precision
grady_fd = gradient_fd(y->nlogMLlog(Nt,t2IJ,A,y),y)

@test norm(exp.(logbeta[1,:])-exp.(betat0)) < 1e-5
@test norm(grady   - grady_fd, Inf)/norm(grady_fd) < 1e-5
@test norm(grady2  - grady_fd, Inf)/norm(grady_fd) < 1e-5
@test norm(grady3  - grady_fd, Inf)/norm(grady_fd) < 1e-5
@test gradA3 == ChainRulesCore.NoTangent()
@test grada3 == ChainRulesCore.NoTangent()

# test with zeropadded input
Nz = 20
y2 = [y;zeros(eltype(y),Nz,size(y,2))]
logalpha, logML = logforward(Nt,t2IJ,A,y2)
_,_,grady = HMMGradients.get_loggrad(Nt,t2IJ,A,y2,logalpha,logML)
grady_fd = gradient_fd(y->nlogMLlog(Nt,t2IJ,A,y),Float64.(y2)) # needs double precision

@test size(grady) == size(y2)
@test norm( grady  - grady_fd, Inf)/norm(grady_fd) < 1e-5
@test sum(grady[end-(Nz-1):end,:]) == 0.0

## test with batch Nt × Ns × Nb
T = Float32
Nb = 3
A = log.(T[0.0 1.0 0.0; 0.0 0.5 0.5; 1.0 0.0 0.0])
t2IJs = [t2IJ for i=1:Nb]
Nts = length.(t2IJs).+1
y = log.(rand(T,maximum(Nts),Ns,Nb))
logalphas,logMLs = logforward(Nts,t2IJs,A,y)
logalphas_logMLs = [logforward(Nts[i],t2IJs[i],A,y[:,:,i]) for i=1:Nb]
@test all([norm(exp.(logalphas[i])-exp.(logalphas_logMLs[i][1]))<1e-5 for i=1:Nb] )
@test all([norm(logMLs[i]-logalphas_logMLs[i][2])<1e-5 for i=1:Nb] )
@test all(length(logMLs) .== Nb)
@test all(size.(logalphas,2) .== Ns)
@test all(size.(logalphas,1) .== Nts)
grada, gradA, grady = HMMGradients.get_loggrad(Nts,t2IJs,A,y,logalphas,logMLs)
grada_gradA_grady = [HMMGradients.get_loggrad(Nts[i],t2IJs[i],A,y[:,:,i],logalphas[i],logMLs[i]) for i=1:Nb]
fy,p = ChainRulesCore.rrule(nlogMLlog, Nts, t2IJs, A, y)
_,_,grada2,gradA2,grady2 = p(1.0)

@test all([norm(grady[:,:,i]-grada_gradA_grady[i][3]) for i = 1:Nb] .< 1e-5)
@test all([norm(grady2[:,:,i]-grada_gradA_grady[i][3]) for i = 1:Nb] .< 1e-5)
@test grada2 == ChainRulesCore.NoTangent()
@test gradA2 == ChainRulesCore.NoTangent()


text:
is_tracing() = false
enable_tracing() = @eval (is_tracing() = true; refresh(); is_tracing())
disable_tracing() = @eval (is_tracing() = false; refresh(); is_tracing())

macro trace(args...)
    quote
        if is_tracing()
            Recalls.@note($(map(esc, args)...))
        end
        nothing
    end
end

const var"@_assert" = var"@assert"


text:
# TODO:
# - refactor, simplify branching, unify duplications
# - fuzzy completions
# - (maybe) export latex completions into a separate package

using REPL

"""
    is_completion_match(s::AbstractString, prefix::AbstractString, cutoff=3)

Returns true if `s` starts with `prefix` or has a sufficiently high fuzzy score.
"""
function is_completion_match(s::AbstractString, prefix::AbstractString, cutoff=3)
    starter = if all(islowercase, prefix)
        startswith(lowercase(s), prefix)
    else
        startswith(s, prefix)
    end
    starter || REPL.fuzzyscore(prefix, s) >= cutoff
end

function textDocument_completion_request(params::CompletionParams, server::LanguageServerInstance, conn)
    CIs = CompletionItem[]
    doc = getdocument(server, URI2(params.textDocument.uri))
    offset = get_offset(doc, params.position)
    rng = Range(doc, offset:offset)
    ppt, pt, t, is_at_end  = get_partial_completion(doc, offset)
    x = get_expr(getcst(doc), offset)

    if pt isa CSTParser.Tokens.Token && pt.kind == CSTParser.Tokenize.Tokens.BACKSLASH
        # latex completion
        latex_completions(doc, offset, string("\\", CSTParser.Tokenize.untokenize(t)), CIs)
    elseif ppt isa CSTParser.Tokens.Token && ppt.kind == CSTParser.Tokenize.Tokens.BACKSLASH && pt isa CSTParser.Tokens.Token && pt.kind === CSTParser.Tokens.CIRCUMFLEX_ACCENT
        latex_completions(doc, offset, string("\\", CSTParser.Tokenize.untokenize(pt), CSTParser.Tokenize.untokenize(t)), CIs)
    elseif t isa CSTParser.Tokens.Token && t.kind == CSTParser.Tokenize.Tokens.COMMENT
        partial = is_latex_comp(t.val, offset - t.startbyte)
        !isempty(partial) && latex_completions(doc, offset, partial, CIs)
    elseif t isa CSTParser.Tokens.Token && (t.kind in (CSTParser.Tokenize.Tokens.STRING,
                                                       CSTParser.Tokenize.Tokens.TRIPLE_STRING,
                                                       CSTParser.Tokenize.Tokens.CMD,
                                                       CSTParser.Tokenize.Tokens.TRIPLE_CMD))
        string_completion(doc, offset, rng, t, CIs)
    elseif x isa EXPR && is_in_import_statement(x)
        import_completions(doc, offset, rng, ppt, pt, t, is_at_end, x, CIs, server)
    elseif t isa CSTParser.Tokens.Token && t.kind == CSTParser.Tokens.DOT && pt isa CSTParser.Tokens.Token && pt.kind == CSTParser.Tokens.IDENTIFIER
        # getfield completion, no partial
        px = get_expr(getcst(doc), offset - (1 + t.endbyte - t.startbyte))
        _get_dot_completion(px, "", rng, CIs, server)
    elseif t isa CSTParser.Tokens.Token && t.kind == CSTParser.Tokens.IDENTIFIER && pt isa CSTParser.Tokens.Token && pt.kind == CSTParser.Tokens.DOT && ppt isa CSTParser.Tokens.Token && ppt.kind == CSTParser.Tokens.IDENTIFIER
        # getfield completion, partial
        px = get_expr(getcst(doc), offset - (1 + t.endbyte - t.startbyte) - (1 + pt.endbyte - pt.startbyte)) # get offset 2 tokens back
        _get_dot_completion(px, t.val, rng, CIs, server)
    elseif t isa CSTParser.Tokens.Token && t.kind == CSTParser.Tokens.IDENTIFIER
        # token completion
        if is_at_end && x !== nothing
            if pt isa CSTParser.Tokens.Token && pt.kind == CSTParser.Tokens.AT_SIGN
                spartial = string("@", t.val)
            else
                spartial = t.val
            end
            kw_completion(doc, spartial, CIs, offset)
            rng = Range(doc, offset:offset)
            collect_completions(x, spartial, rng, CIs, server, false)
        end
    elseif t isa CSTParser.Tokens.Token && t.kind == CSTParser.Tokens.AT_SIGN
        # only `@` given
        x !== nothing && collect_completions(x, "@", rng, CIs, server, false)
    elseif t isa CSTParser.Tokens.Token && Tokens.iskeyword(t.kind) && is_at_end
        kw_completion(doc, CSTParser.Tokenize.untokenize(t), CIs, offset)
    elseif t isa CSTParser.Tokens.Token && t.kind == CSTParser.Tokens.IN && is_at_end
        collect_completions(x, "in", rng, CIs, server, false)
    elseif t isa CSTParser.Tokens.Token && t.kind == CSTParser.Tokens.ISA && is_at_end
        collect_completions(x, "isa", rng, CIs, server, false)
    end

    return CompletionList(true, unique(CIs))
end

function get_partial_completion(doc, offset)
    ppt, pt, t = get_toks(doc, offset)
    is_at_end = offset == t.endbyte + 1
    return ppt, pt, t, is_at_end
end

function latex_completions(doc, offset, partial, CIs)
    for (k, v) in REPL.REPLCompletions.latex_symbols
        if is_completion_match(string(k), partial)
            t1 = TextEdit(Range(doc, (offset - sizeof(partial)):offset), v)
            push!(CIs, CompletionItem(k, 11, missing, v, v, missing, missing, missing, missing, missing, missing, t1, missing, missing, missing, missing))
        end
    end
end

function kw_completion(doc, spartial, CIs, offset)
    length(spartial) == 0 && return
    for (kw, comp) in snippet_completions
        if startswith(kw, spartial)
             t1 = TextEdit(Range(doc, (offset - sizeof(spartial)):offset), comp)
            push!(CIs, CompletionItem(kw, 14, missing, missing, kw, missing, missing, missing, missing, missing, InsertTextFormats.Snippet, t1, missing, missing, missing, missing))
        end
    end
end

const snippet_completions = Dict{String,String}(
    "abstract" => "abstract type \$0 end",
    "baremodule" => "baremodule \$1\n\t\$0\nend",
    "begin" => "begin\n\t\$0\nend",
    "break" => "break",
    "catch" => "catch",
    "const" => "const ",
    "continue" => "continue",
    "do" => "do \$1\n\t\$0\nend",
    "else" => "else",
    "elseif" => "elseif ",
    "end" => "end",
    "export" => "export ",
    "finally" => "finally",
    "for" => "for \$1 in \$2\n\t\$0\nend",
    "function" => "function \$1(\$2)\n\t\$0\nend",
    "global" => "global ",
    "if" => "if \$1\n\t\$0\nend",
    "import" => "import",
    "let" => "let \$1\n\t\$0\nend",
    "local" => "local ",
    "macro" => "macro \$1(\$2)\n\t\$0\nend",
    "module" => "module \$1\n\t\$0\nend",
    "mutable" => "mutable struct \$0\nend",
    "outer" => "outer ",
    "primitive" => "primitive type \$1 \$0 end",
    "quote" => "quote\n\t\$0\nend",
    "return" => "return",
    "struct" => "struct \$0 end",
    "try" => "try\n\t\$0\ncatch\nend",
    "using" => "using ",
    "while" => "while \$1\n\t\$0\nend"
    )

function collect_completions(m::SymbolServer.ModuleStore, spartial, rng, CIs, server, inclexported=false, dotcomps=false)
    for val in m.vals
        n, v = String(val[1]), val[2]
        (startswith(n, ".") || startswith(n, "#")) && continue
        !is_completion_match(n, spartial) && continue
        if v isa SymbolServer.VarRef
            v = SymbolServer._lookup(v, getsymbolserver(server), true)
            v === nothing && return
        end
        if StaticLint.isexportedby(n, m) || inclexported
            rng1 = Range(Position(rng.start.line, rng.start.character - sizeof(spartial)), rng.stop)
            push!(CIs, CompletionItem(n, _completion_kind(v, server), MarkupContent(sanitize_docstring(v.doc)), TextEdit(rng1, n)))
        elseif dotcomps
            rng1 = Range(Position(rng.start.line, rng.start.character - sizeof(spartial)), rng.stop)
            push!(CIs, CompletionItem(n, _completion_kind(v, server), MarkupContent(sanitize_docstring(v.doc)), TextEdit(rng1, string(m.name, ".", n))))
        end
    end
end

function collect_completions(x::EXPR, spartial, rng, CIs, server, inclexported=false, dotcomps=false)
    if scopeof(x) !== nothing
        collect_completions(scopeof(x), spartial, rng, CIs, server, inclexported, dotcomps)
        if scopeof(x).modules isa Dict
            for m in scopeof(x).modules
                collect_completions(m[2], spartial, rng, CIs, server, inclexported, dotcomps)
            end
        end
    end
    if parentof(x) !== nothing && !CSTParser.defines_module(x)
        return collect_completions(parentof(x), spartial, rng, CIs, server, inclexported, dotcomps)
    else
        return
    end
end

function collect_completions(x::StaticLint.Scope, spartial, rng, CIs, server, inclexported=false, dotcomps=false)
    if x.names !== nothing
        for n in x.names
            if is_completion_match(n[1], spartial)
                documentation = ""
                if n[2] isa StaticLint.Binding
                    documentation = get_hover(n[2], documentation, server)
                    sanitize_docstring(documentation)
                end
                rng1 = Range(Position(rng.start.line, rng.start.character - sizeof(spartial)), rng.stop)
                push!(CIs, CompletionItem(n[1], _completion_kind(n[2], server), MarkupContent(documentation), TextEdit(rng1, n[1])))
            end
        end
    end
end

function is_rebinding_of_module(x)
    x isa EXPR && refof(x).type === StaticLint.CoreTypes.Module && # binding is a Module
    refof(x).val isa EXPR && CSTParser.isassignment(refof(x).val) && # binding expr is an assignment
    StaticLint.hasref(refof(x).val.args[2]) && refof(refof(x).val.args[2]).type === StaticLint.CoreTypes.Module &&
    refof(refof(x).val.args[2]).val isa EXPR && CSTParser.defines_module(refof(refof(x).val.args[2]).val)# double check the rhs points to a module
end

function _get_dot_completion(px, spartial, rng, CIs, server) end
function _get_dot_completion(px::EXPR, spartial, rng, CIs, server)
    if px !== nothing
        if refof(px) isa StaticLint.Binding
            if refof(px).val isa StaticLint.SymbolServer.ModuleStore
                collect_completions(refof(px).val, spartial, rng, CIs, server, true)
            elseif refof(px).val isa EXPR && CSTParser.defines_module(refof(px).val) && scopeof(refof(px).val) isa StaticLint.Scope
                collect_completions(scopeof(refof(px).val), spartial, rng, CIs, server, true)
            elseif is_rebinding_of_module(px)
                collect_completions(scopeof(refof(refof(px).val.args[2]).val), spartial, rng, CIs, server, true)
            elseif refof(px).type isa SymbolServer.DataTypeStore
                for a in refof(px).type.fieldnames
                    a = String(a)
                    if is_completion_match(a, spartial)
                        rng1 = Range(Position(rng.start.line, rng.start.character - sizeof(spartial)), rng.stop)
                        push!(CIs, CompletionItem(a, 2, MarkupContent(a), TextEdit(rng1, a)))
                    end
                end
            elseif refof(px).type isa StaticLint.Binding && refof(px).type.val isa SymbolServer.DataTypeStore
                for a in refof(px).type.val.fieldnames
                    a = String(a)
                    if is_completion_match(a, spartial)
                        rng1 = Range(Position(rng.start.line, rng.start.character - sizeof(spartial)), rng.stop)
                        push!(CIs, CompletionItem(a, 2, MarkupContent(a), TextEdit(rng1, a)))
                    end
                end
            elseif refof(px).type isa StaticLint.Binding && refof(px).type.val isa EXPR && CSTParser.defines_struct(refof(px).type.val) && scopeof(refof(px).type.val) isa StaticLint.Scope
                collect_completions(scopeof(refof(px).type.val), spartial, rng, CIs, server, true)
            end
        elseif refof(px) isa StaticLint.SymbolServer.ModuleStore
            collect_completions(refof(px), spartial, rng, CIs, server, true)
        end
    end
end

function _completion_kind(b, server)
    if b isa StaticLint.Binding
        if b.type == StaticLint.CoreTypes.String
            return 1
        elseif b.type == StaticLint.CoreTypes.Function
            return 2
        elseif b.type == StaticLint.CoreTypes.Module
            return 9
        elseif b.type == Int || b.type == StaticLint.CoreTypes.Float64
            return 12
        elseif b.type == StaticLint.CoreTypes.DataType
            return 22
        else
            return 13
        end
    elseif b isa SymbolServer.ModuleStore || b isa SymbolServer.VarRef
        return 9
    elseif b isa SymbolServer.MethodStore
        return 2
    elseif b isa SymbolServer.FunctionStore
        return 3
    elseif b isa SymbolServer.DataTypeStore
        return 22
    else
        return 6
    end
end

function get_import_root(x::EXPR)
    if CSTParser.isoperator(headof(x.args[1])) && valof(headof(x.args[1])) == ":"
        return last(x.args[1].args[1].args)
    end
    return nothing
end

function string_completion(doc, offset, rng, t, CIs)
    path_completion(doc, offset, rng, t, CIs)
    # Need to adjust things for quotation marks
    if t.kind in (CSTParser.Tokenize.Tokens.STRING, CSTParser.Tokenize.Tokens.CMD)
        t.startbyte < offset <= t.endbyte || return
        relative_offset = offset - t.startbyte - 1
        content = t.val[2:prevind(t.val, lastindex(t.val))]
    else
        t.startbyte < offset <= t.endbyte - 2 || return
        relative_offset = offset - t.startbyte - 3
        content = t.val[4:prevind(t.val, lastindex(t.val), 3)]
    end
    partial = is_latex_comp(content, relative_offset)
    !isempty(partial) && latex_completions(doc, offset, partial, CIs)
end

function is_latex_comp(s, i)
    i0 = i
    while firstindex(s) <= i
        s[i] == '\\' && return s[i:i0]
        !is_latex_comp_char(s[i]) && return ""
        i = prevind(s, i)
    end
    return ""
end

is_latex_comp_char(c::Char) = UInt32(c) <= typemax(UInt8) ? is_latex_comp_char(UInt8(c)) : false
function is_latex_comp_char(u)
    # Checks whether a Char (represented as a UInt8) is in the set of those those used to trigger
    # latex completions.
    # from: UInt8.(sort!(unique(prod([k[2:end] for (k,_) in REPL.REPLCompletions.latex_symbols]))))
    u === 0x28 ||
    u === 0x29 ||
    u === 0x2b ||
    u === 0x2d ||
    u === 0x2f ||
    0x30 <= u <= 0x39 ||
    u === 0x3d ||
    0x41 <= u <= 0x5a ||
    u === 0x5e ||
    u === 0x5f ||
    0x61 <= u <= 0x7a
end

function path_completion(doc, offset, rng, t, CIs)
    if t.kind == CSTParser.Tokenize.Tokens.STRING
        path = t.val[2:prevind(t.val, lastindex(t.val))]
        if startswith(path, "~")
            path = replace(path, '~' => homedir())
            dir, partial = _splitdir(path)
        else
            dir, partial = _splitdir(path)
            if !startswith(dir, "/")
                doc_path = getpath(doc)
                isempty(doc_path) && return
                dir = joinpath(_dirname(doc_path), dir)
            end
        end
        try
            fs = readdir(dir)
            for f in fs
                if startswith(f, partial)
                    try
                        if isdir(joinpath(dir, f))
                            f = string(f, "/")
                        end
                        push!(CIs, CompletionItem(f, 17, f, TextEdit(rng, f[nextind(f, lastindex(partial)):end])))
                    catch err
                        isa(err, Base.IOError) || isa(err, Base.SystemError) || rethrow()
                    end
                end
            end
        catch err
            isa(err, Base.IOError) || isa(err, Base.SystemError) || rethrow()
        end
    end
end

is_in_import_statement(x::EXPR) = is_in_fexpr(x, x -> headof(x) in (:using, :import))

function import_completions(doc, offset, rng, ppt, pt, t, is_at_end, x, CIs, server)
    import_statement = StaticLint.get_parent_fexpr(x, x -> headof(x) === :using || headof(x) === :import)

    import_root = get_import_root(import_statement)

    if (t.kind == CSTParser.Tokens.WHITESPACE && pt.kind ∈ (CSTParser.Tokens.USING, CSTParser.Tokens.IMPORT, CSTParser.Tokens.IMPORTALL, CSTParser.Tokens.COMMA, CSTParser.Tokens.COLON)) ||
        (t.kind in (CSTParser.Tokens.COMMA, CSTParser.Tokens.COLON))
        # no partial, no dot
        if import_root !== nothing && refof(import_root) isa SymbolServer.ModuleStore
            for (n, m) in refof(import_root).vals
                n = String(n)
                if is_completion_match(n, t.val) && !startswith(n, "#")
                    rng1 = Range(doc, offset - sizeof(t.val):offset)
                    push!(CIs, CompletionItem(n, _completion_kind(m, server), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), TextEdit(rng1, n)))
                end
            end
        else
            for (n, m) in StaticLint.getsymbolserver(server)
                n = String(n)
                (startswith(n, ".") || startswith(n, "#")) && continue
                push!(CIs, CompletionItem(n, 9, MarkupContent(sanitize_docstring(m.doc)), TextEdit(rng, n)))
            end
        end
    elseif t.kind == CSTParser.Tokens.DOT && pt.kind == CSTParser.Tokens.IDENTIFIER
        # no partial, dot
        if haskey(getsymbolserver(server), Symbol(pt.val))
            collect_completions(getsymbolserver(server)[Symbol(pt.val)], "", rng, CIs, server)
        end
    elseif t.kind == CSTParser.Tokens.IDENTIFIER && is_at_end
        # partial
        if pt.kind == CSTParser.Tokens.DOT && ppt.kind == CSTParser.Tokens.IDENTIFIER
            if haskey(StaticLint.getsymbolserver(server), Symbol(ppt.val))
                rootmod = StaticLint.getsymbolserver(server)[Symbol(ppt.val)]
                for (n, m) in rootmod.vals
                    n = String(n)
                    if is_completion_match(n, t.val) && !startswith(n, "#")
                        rng1 = Range(doc, offset - sizeof(t.val):offset)
                        push!(CIs, CompletionItem(n, _completion_kind(m, server), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), TextEdit(rng1, n)))
                    end
                end
            end
        else
            if import_root !== nothing && refof(import_root) isa SymbolServer.ModuleStore
                for (n, m) in refof(import_root).vals
                    n = String(n)
                    if is_completion_match(n, t.val) && !startswith(n, "#")
                        rng1 = Range(doc, offset - sizeof(t.val):offset)
                        push!(CIs, CompletionItem(n, _completion_kind(m, server), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), TextEdit(rng1, n)))
                    end
                end
            else
                for (n, m) in StaticLint.getsymbolserver(server)
                    n = String(n)
                    if is_completion_match(n, t.val)
                        rng1 = Range(doc, offset - sizeof(t.val):offset)
                        push!(CIs, CompletionItem(n, 9, MarkupContent(m isa SymbolServer.SymStore ? m.doc : n), TextEdit(rng1, n)))
                    end
                end
            end
        end
    end
end


text:
# Tests the null of normality using the Jarque-Bera test statistic.
function jbtest(x::Vector)
    n = length(x)
    m1 = sum(x)/n
    m2 = sum((x - m1).^2)/n
    m3 = sum((x - m1).^3)/n
    m4 = sum((x - m1).^4)/n
    b1 = (m3/m2^(3/2))^2
    b2 = (m4/m2^2)
    statistic = n * b1/6 + n*(b2 - 3)^2/24
    d = Chisq(2.)
    pvalue = 1.-cdf(d,statistic)
    statistic, pvalue
end


text:
# Note that this script can accept some limited command-line arguments, run
# `julia build_tarballs.jl --help` to see a usage message.
using BinaryBuilder

name = "LLVMCompilerRT"
version = v"13.0.1"

sources = [
    ArchiveSource(
        "https://github.com/llvm/llvm-project/releases/download/llvmorg-$(version)/compiler-rt-$(version).src.tar.xz",
        "7b33955031f9a9c5d63077dedb0f99d77e4e7c996266952c1cec55626dca5dfc"
    ),
    DirectorySource("./bundled"),
]

# Bash recipe for building across all platforms
script = raw"""
cd $WORKSPACE/srcdir/compiler-rt*/

# We'll codesign during audit
atomic_patch -p1 ../patches/do-not-codesign.patch

FLAGS=()
if [[ "${target}" == *-apple-* ]]; then
    # Fake `PlistBuddy` to make detection of aarch64 support work
    mkdir -p /usr/libexec
    cat > /usr/libexec/PlistBuddy << EOF
#!/bin/bash

if [[ "${target}" == aarch64-* ]]; then
    echo " arm64"
else
    echo " $(uname -m)"
fi
EOF
    chmod +x /usr/libexec/PlistBuddy

    # We use could use `${MACOSX_DEPLOYMENT_TARGET}` to specify the SDK version, but it's
    # set to 10.10 on x86_64, but compiler-rt requires at least 10.12 and we actually use
    # 10.12.  On aarch64 it's 11.0, but the CMake script doesn't seem to like values greater
    # than 10, so let's just use 10.12 everywhere.
    FLAGS+=(
            -DDARWIN_macosx_OVERRIDE_SDK_VERSION:STRING=10.12
            -DDARWIN_macosx_CACHED_SYSROOT=/opt/${target}/${target}/sys-root
           )
fi

mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=${prefix} \
    -DCMAKE_TOOLCHAIN_FILE="${CMAKE_TARGET_TOOLCHAIN}" \
    -DCMAKE_BUILD_TYPE=Release \
    "${FLAGS[@]}" \
    ..
make -j${nproc}
make install
"""

# These are the platforms we will build for by default, unless further
# platforms are passed in on the command line
platforms = supported_platforms()
# Exclude failing platforms.  This package is a stop-gap solution for being able to link
# some packages on aarch64-apple-darwin, so there is little need to spend time on getting
# this to build for _all_ platforms.  The long-term plan is to have these libraries as part
# of LLVMBootstrap: https://github.com/JuliaPackaging/Yggdrasil/pull/1681
filter!(p -> arch(p) != "powerpc64le" && !(BinaryBuilder.proc_family(p) == "intel" && libc(p) == "musl"), platforms)

# The products that we will ensure are always built
products = LibraryProduct[
]

# Dependencies that must be installed before this package can be built
dependencies = Dependency[
]

# Build the tarballs, and possibly a `build.jl` as well.
build_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies; julia_compat="1.6", preferred_gcc_version=v"8", preferred_llvm_version=version)

max_stars_repo_name: MichelJuillard/Yggdrasil

max_issues_repo_name: MichelJuillard/Yggdrasil

max_forks_repo_name: MichelJuillard/Yggdrasil


text:
#--------------------------------------------------------------------
# Spacetime Discretization methods in Julia
# Soham 01-2018
#--------------------------------------------------------------------

function testrestrictmodes1D(fn::Function, Nx::Int, M::Int)::Float64
    fpatch  = Float64[fn(x) for x in chebgrid(Nx)]
    vndmx   = vandermonde(Nx)
    frpatch = vndmx*restrictmodes!(inv(vndmx)*fpatch, M)
    w       = chebweights(Nx)
    L2error = sqrt(w'*(fpatch - frpatch).^2)
    return L2error
end

function testrestrictmodes2D(fn::Function, Nx::Int, Ny::Int, M::Int, N::Int)::Float64
    fpatch  = Float64[fn(x,y) for x in chebgrid(Nx), y in chebgrid(Ny)]
    vndmx   = vandermonde(Nx)
    vndmy   = vandermonde(Ny)
    frpatch = vndmx*restrictmodes!(inv(vndmx)*fpatch*inv(vndmy)', M, N)*vndmy'
    return L2norm(fpatch, frpatch, chebweights(Nx), chebweights(Ny))
end

function testprolongatemodes1D(fn::Function, Nx::Int, M::Int)::Float64
    fpatch  = Float64[fn(x) for x in chebgrid(Nx)]
    vndmx   = vandermonde(Nx)
    vndmMx  = pseudovandermonde(M, chebgrid(Nx))
    fppatch = vndmMx*prolongatemodes(inv(vndmx)*fpatch, M)
    w       = chebweights(Nx)
    return  sqrt(w'*(fpatch - fppatch).^2)
end

function testprolongatemodes2D(fn::Function, Nx::Int, Ny::Int, M::Int, N::Int)::Float64
    fpatch  = Float64[fn(x,y) for x in chebgrid(Nx), y in chebgrid(Ny)]
    vndmx   = vandermonde(Nx)
    vndmy   = vandermonde(Ny)
    vndmMx  = pseudovandermonde(M, chebgrid(Nx))
    vndmNy  = pseudovandermonde(N, chebgrid(Ny))
    fppatch = vndmMx*prolongatemodes(inv(vndmx)*fpatch*inv(vndmy)', M, N)*vndmNy'
    return L2norm(fpatch, fppatch, chebweights(Nx), chebweights(Ny))
end

function testprolongate1Dx(fn::Function, Nx::Int, M::Int)::Float64
    patch1D = Float64[fn(x) for x in chebgrid(Nx)]
    patchMD = prolongateOP(Nx, M)*patch1D
    wx      = chebweights(Nx)
    L2error = sum(sqrt(wx'*(patchMD[1+(m-1)*(Nx+1):m*(Nx+1)] - Float64[fn(x) for x in chebgrid(Nx, M, m)]).^2) for m in 1:M)
    return L2error
end

function testprolongate1Dy(fn::Function, Ny::Int, M::Int)::Float64
    patch1D = Float64[fn(y) for y in chebgrid(Ny)]
    patchMD = patch1D'*prolongateOP(Ny, M)'
    wy      = chebweights(Ny)
    L2error = sum(sqrt(wy'*(patchMD'[1+(m-1)*(Ny+1):m*(Ny+1)] - Float64[fn(y) for y in chebgrid(Ny, M, m)]).^2) for m in 1:M)
    return L2error
end

function testrestrict1Dx(fn::Function, Nx::Int, M::Int)::Float64
    patch1D = Float64[fn(x) for x in chebgrid(Nx)]
    patchMD = prolongateOP(Nx, M)*patch1D
    patchSD = restrictOP(Nx, M)*patchMD
    wx      = chebweights(Nx)
    L2error = sqrt(wx'*(patch1D - patchSD).^2)
    return L2error
end

function testrestrict1Dy(fn::Function, Ny::Int, M::Int)::Float64
    patch1D = Float64[fn(y) for y in chebgrid(Ny)]
    patchMD = patch1D'*prolongateOP(Ny, M)'
    patchSD = patchMD*restrictOP(Ny,M)'
    wy      = chebweights(Ny)
    L2error = sqrt(wy'*(patch1D - patchSD').^2)
    return L2error
end

function testprolongatePatch(fn::Function, Nx::Int, Ny::Int, M::Int)::Float64
    sPatch = Float64[fn(x,y) for x in chebgrid(Nx), y in chebgrid(Ny)]
    mPatch = prolongatePatch(Patch([1,1], sPatch), M)
    L2err  = 0.0
    for m in 1:M, n in 1:M
        rPatch = Float64[fn(x,y) for x in chebgrid(Nx, M, m), y in chebgrid(Ny, M, n)]
        L2err += L2norm(mPatch[[m,n]].value, rPatch, chebweights(Nx)/M, chebweights(Ny)/M)
    end
    return L2err
end

function testProlongateThenRestrictPatch(fn::Function, Nx::Int, Ny::Int, M::Int)::Float64
    sPatch   = Patch([1,1], Float64[fn(x,y) for x in chebgrid(Nx), y in chebgrid(Ny)])
    mPatch   = prolongatePatch(sPatch, M)
    m2sPatch = restrictPatch(mPatch)
    return L2norm(sPatch.value, m2sPatch.value, chebweights(Nx), chebweights(Ny))
end

@test testprolongatePatch((x,y)->x^9+y^8, 12, 12, 4) < 1e-14
@test testrestrictmodes1D(x->x^9, 12, 4) > testrestrictmodes1D(x->x^9, 12, 5)
@test testrestrictmodes2D((x,y)->x^9+y^8, 12, 12, 4, 5) > testrestrictmodes2D((x,y)->x^9+y^8, 12, 12, 7, 8)
@test testprolongatemodes1D(x->x^9, 10, 12) == testprolongatemodes1D(x->x^9, 10, 13)
@test testprolongatemodes2D((x,y)->x^2+y^2, 9, 9, 10, 10) == testprolongatemodes2D((x,y)->x^2+y^2, 9, 9, 12, 12)
@test prolongateOP(4,2)*restrictOP(4,2)*prolongateOP(4,2) ≈ prolongateOP(4,2)
@test restrictOP(4,2)*prolongateOP(4,2)*restrictOP(4,2) ≈ restrictOP(4,2)
@test (prolongateOP(4,2)*restrictOP(4,2))' ≈ prolongateOP(4,2)*restrictOP(4,2)
@test (restrictOP(4,2)*prolongateOP(4,2))' ≈ restrictOP(4,2)*prolongateOP(4,2)
@test testprolongate1Dx(x->x.^3, 10, 3) < 1e-14
@test testprolongate1Dy(x->x.^5, 12, 2) < 1e-14
@test testrestrict1Dx(x->x.^5, 10, 4) < 1e-14
@test testrestrict1Dy(x->x.^2, 4, 2) < 1e-14
@test testProlongateThenRestrictPatch((x,y)->x^2 + y^3, 12, 8, 2) < 1e-14


text:
using ThreadedSparseArrays
using SparseArrays
using LinearAlgebra
using Random
using StableRNGs
using Test

@static if VERSION < v"1.6.0"
    parentmodule(x) = x.linfo.def.module
end

function match_exception(f, ::Type{T}=DimensionMismatch, func=r"^mul!$", m=ThreadedSparseArrays) where T
    try
        f()
    catch ex
        st = stacktrace(catch_backtrace())[1]
        return ex isa T && match(func,string(st.func))!==nothing && parentmodule(st) === m
    end
    false
end

function rand_dense(rng,::Type{ComplexF64}, N, n)
    M = max(N,n)
    Matrix(sparse(randperm(rng,M),1:M,one(ComplexF64)))[1:N,1:n]
end
rand_dense(rng,::Type{ComplexF64}, N) = (x=zeros(ComplexF64,N); x[rand(rng,1:N)] = one(ComplexF64); x)
rand_dense(rng,::Type{Complex{Int}}, sz...) = rand(rng,0:5,sz...) .+ im*rand(rng,0:5,sz...)

rand_sparse(rng,::Type{ComplexF64}, N, n, p)   = sprand(rng,ComplexF64,N,n,p)
rand_sparse(rng,::Type{Complex{Int}}, N, n, p) = sprand(N,n,p,x->Complex{Int}.(rand(rng,0:5,x),rand(rng,0:5,x)))

rand_scalar(rng,::Type{T}) where T<:Complex = T(rand(rng,2 .^ (1:5)) + im*rand(rng,2 .^ (1:5)))

@testset "ThreadedSparseArrays.jl" begin
    # Test that all combinations of dense*sparse multiplication are threaded
    @testset "Threading" begin
        A = ThreadedSparseMatrixCSC(spzeros(2,3))
        B = zeros(4,5)
        @test match_exception(()->A*B)
        @test match_exception(()->A'B)
        @test match_exception(()->A*B')
        @test match_exception(()->A'B')
        @test match_exception(()->B*A)
        @test match_exception(()->B'A)
        @test match_exception(()->B*A')
        @test match_exception(()->B'A')
    end

    # These test below are here to get the right fallback for sparse times sparse.
    # The implementations are not (currently) threaded.
    @testset "Fallbacks" begin
        A = ThreadedSparseMatrixCSC(spzeros(2,3))
        B = spzeros(4,5)
        @test match_exception(()->A*B,  DimensionMismatch, r"", SparseArrays)
        @test match_exception(()->A'B,  DimensionMismatch, r"", SparseArrays)
        @test match_exception(()->A*B', DimensionMismatch, r"", SparseArrays)
        @test match_exception(()->A'B', DimensionMismatch, r"", SparseArrays)
        @test match_exception(()->B*A,  DimensionMismatch, r"", SparseArrays)
        @test match_exception(()->B'A,  DimensionMismatch, r"", SparseArrays)
        @test match_exception(()->B*A', DimensionMismatch, r"", SparseArrays)
        @test match_exception(()->B'A', DimensionMismatch, r"", SparseArrays)
    end

    @testset "ReturnType" for op1 in [identity,adjoint,transpose], op2 in [identity,adjoint,transpose]
        rng = StableRNG(1234)
        A = rand_sparse(rng,Complex{Int64},10,10,0.4)
        B = rand_sparse(rng,Complex{Int64},10,10,0.4)
        ref = op1(A)*op2(B)
        out = op1(ThreadedSparseMatrixCSC(A))*op2(ThreadedSparseMatrixCSC(B))
        @test out isa ThreadedSparseMatrixCSC
        @test out == ref
        out = op1(ThreadedSparseMatrixCSC(A))*op2(B)
        @test out isa SparseMatrixCSC
        @test out == ref
        out = op1(A)*op2(ThreadedSparseMatrixCSC(B))
        @test out isa SparseMatrixCSC
        @test out == ref
    end

    N = 1000
    n = 200
    @testset "$T" for T in (ComplexF64, Complex{Int64})
        rng = StableRNG(1234)
        C = rand_sparse(rng,T,N,n,0.05)

        @testset "(α,β)=$αβ" for αβ in ((), (rand_scalar(rng,T), zero(T)), (zero(T),rand_scalar(rng,T)), (rand_scalar(rng,T),rand_scalar(rng,T)))
            @testset "R_$(op)" for op in [identity,adjoint,transpose]
                Ct = op(ThreadedSparseMatrixCSC(C))
                M = size(Ct,1)

                X = rand_dense(rng,T,M,M)

                out = zeros(T, size(Ct))
                LinearAlgebra.mul!(out, X, Ct, αβ...)
                ref = zeros(T, size(op(C)))
                LinearAlgebra.mul!(ref, X, op(C), αβ...)
                @test out == ref
            end

            @testset "L_$(op)" for op in [identity,adjoint,transpose]
                Ct = op(ThreadedSparseMatrixCSC(C))
                m = size(Ct,2)

                X = rand_dense(rng,T,m,m)

                out = zeros(T, size(Ct))
                LinearAlgebra.mul!(out, Ct, X, αβ...)
                ref = zeros(T, size(op(C)))
                LinearAlgebra.mul!(ref, op(C), X, αβ...)
                @test out == ref
            end

            x = rand_dense(rng,T,N)
            @testset "L_$(op)_vec" for op in [adjoint,transpose]
                Ct = op(ThreadedSparseMatrixCSC(C))

                out = zeros(T, n)
                LinearAlgebra.mul!(out, Ct, x, αβ...)
                ref = zeros(T, n)
                LinearAlgebra.mul!(ref, op(C), x, αβ...)
                @test out == ref
            end
        end
    end
end


text:
# Uses "fallback" start and end times to deal with outages
function check_get_data!(S::SeisData, protocol::String, channels::Union{String, Array{String,1}};
       incr::DatePeriod        = Day(1)        , # Increment for failed reqs
max_retries::Integer           = 7             , # Maximum retries on fail
   autoname::Bool              = false         , # Auto-generate file names?
     demean::Bool              = false         , # Demean after download?
    detrend::Bool              = false         , # Detrend after download?
        fmt::String            = KW.fmt        , # File format
        msr::Bool              = false         , # Get multi-stage response?
         nd::Real              = KW.nd         , # Number of days per request
       opts::String            = KW.opts       , # Options string
        rad::Array{Float64, 1} = KW.rad        , # Query radius
        reg::Array{Float64, 1} = KW.reg        , # Query region
      prune::Bool              = KW.prune      , # Prune empty channels?
         rr::Bool              = false         , # Remove instrument response?
          s::TimeSpec          = 0             , # Start
         si::Bool              = KW.si         , # Fill station info?
        src::String            = KW.src        , # Data source
      taper::Bool              = false         , # Taper after download?
          t::TimeSpec          = (-600)        , # End or Length (s)
         to::Int64             = KW.to         , # Timeout (s)
      ungap::Bool              = false         , # Remove time gaps?
    unscale::Bool              = false         , # Unscale (divide by gain)?
          v::Integer           = KW.v          , # Verbosity
          w::Bool              = KW.w          , # Write to disc?
         xf::String            = "FDSNsta.xml" , # XML save file
          y::Bool              = KW.y          , # Sync
          )

  has_data(S::SeisData) = (isempty(S) ? 0 : maximum([length(x) for x in S.x])) > 0
  warn_str(s::DateTime, α::DateTime, i::Integer, incr::DatePeriod; a::Bool=false) =
      string("S had no data ",
              a ? string("after ", i, " retries") : string("until retry #", i),
              " (", typeof(incr)(α-s), " before original request begin time)")
  s,t = parsetimewin(s,t)
  max_retries = max(max_retries, 3)
  s = DateTime(s)
  t = DateTime(t)
  α = deepcopy(s)
  i = 0
  while i ≤ max_retries
    i += 1
    try
      get_data!(S, protocol, channels,
               autoname=autoname,
                 demean=demean,
                detrend=detrend,
                    fmt=fmt,
                    msr=msr,
                     nd=nd,
                   opts=opts,
                  prune=prune,
                    rad=rad,
                    reg=reg,
                     rr=rr,
                      s=s,
                     si=si,
                    src=src,
                      t=t,
                  taper=taper,
                     to=to,
                  ungap=ungap,
                unscale=unscale,
                      v=v,
                      w=w,
                     xf=xf,
                      y=y)
    catch err
      @warn(string("Calling get_data threw error ", err))
    end
    if has_data(S)
      if i > 1
        str = warn_str(s, α, i-1, incr)
        printstyled("WARNING: ", str, ".\nCheck server; contact admins if problem persists.\n", color=:light_yellow)
        @warn(str)
      end
      return nothing
    end
    s -= incr
    t -= incr
    println("Retrying; start time decremented by ", incr)
  end
  str = warn_str(s, α, max_retries, incr, a=true)
  printstyled("WARNING: ", str, ".\nCheck ", protocol, " scripts and dependencies for new bugs!\n", color=:magenta, bold=true)
  @warn(str)
  return nothing
end

function check_get_data(protocol::String, channels::Union{String, Array{String,1}};
       incr::DatePeriod        = Day(1)        , # Increment for failed reqs
max_retries::Integer           = 7             , # Maximum retries on fail
   autoname::Bool              = false         , # Auto-generate file names?
     demean::Bool              = false         , # Demean after download?
    detrend::Bool              = false         , # Detrend after download?
        fmt::String            = KW.fmt        , # File format
        msr::Bool              = false         , # Get multi-stage response?
         nd::Real              = KW.nd         , # Number of days per request
       opts::String            = KW.opts       , # Options string
        rad::Array{Float64, 1} = KW.rad        , # Query radius
        reg::Array{Float64, 1} = KW.reg        , # Query region
      prune::Bool              = KW.prune      , # Prune empty channels?
         rr::Bool              = false         , # Remove instrument response?
          s::TimeSpec          = 0             , # Start
         si::Bool              = KW.si         , # Fill station info?
        src::String            = KW.src        , # Data source
      taper::Bool              = false         , # Taper after download?
          t::TimeSpec          = (-600)        , # End or Length (s)
         to::Int64             = KW.to         , # Timeout (s)
      ungap::Bool              = false         , # Remove time gaps?
    unscale::Bool              = false         , # Unscale (divide by gain)?
          v::Integer           = KW.v          , # Verbosity
          w::Bool              = KW.w          , # Write to disc?
         xf::String            = "FDSNsta.xml" , # XML save file
          y::Bool              = KW.y          , # Sync
          )

  S = SeisData()
  check_get_data!(S, protocol, channels,
               autoname=autoname,
                 demean=demean,
                detrend=detrend,
                    fmt=fmt,
                    msr=msr,
                     nd=nd,
                   opts=opts,
                  prune=prune,
                    rad=rad,
                    reg=reg,
                     rr=rr,
                      s=s,
                     si=si,
                    src=src,
                      t=t,
                  taper=taper,
                     to=to,
                  ungap=ungap,
                unscale=unscale,
                      v=v,
                      w=w,
                     xf=xf,
                      y=y)
  return S
end


text:
using Unitful, UnitfulMoles, Test

@mol Baz
@mol Foo 55.5
x = 99.9
@mol Bar x

@testset "Printing" begin
    @test string(3molBaz) == "3 molBaz"
    @test string(3.0mmolBaz) == "3.0 mmolBaz"
end

@testset "units convert to grams" begin
    @test uconvert(u"g", 2molFoo) == 111.0u"g"
end

@testset "Can add or subtract mol units" begin
    @test 3molFoo + 2molFoo == 5molFoo
    @test 3molFoo - 2molFoo == 1molFoo
    @test 2molFoo + 3molBar ==  0.4107u"kg"
    @test 2molFoo - 3molBar == -0.18870000000000003u"kg"
end

@testset "Can multiply or divide mol units" begin
    @test molFoo * 2molFoo == 2molFoo^2
    @test 1molFoo / 2molFoo == 1/2
    molFoo/molBar * 2molBar == 2molFoo
end

@testset "Compound weight is the sum of its components" begin
    @compound FooBar2
    @test uconvert(u"g", 1molFooBar₂) == 255.3u"g"
    @compound Foo₄Bar
    @test uconvert(u"g", 1molFoo₄Bar) == 321.9u"g"
    @compound Foo90Bar
end

@testset "Sum base" begin
    elements = ["C" => 1, "A" => 1, "B" => 2, "C" => 2]
    base = "C"
    @test UnitfulMoles.sum_base(base, elements) == 3
end

@testset "Parse compound" begin
    @test UnitfulMoles.parse_compound("CO2") == ["C" => 1, "O" => 2]
    @test UnitfulMoles.parse_compound("CO₂") == ["C" => 1, "O" => 2]
    @test UnitfulMoles.parse_compound("NaCl") == ["Na" => 1, "Cl" => 1]
    @test UnitfulMoles.parse_compound("C8H10N4O2") == ["C" => 8, "H" => 10, "N" => 4, "O" => 2]
    @test UnitfulMoles.parse_compound("C₈H₁₀N₄O₂") == ["C" => 8, "H" => 10, "N" => 4, "O" => 2]
end

@testset "xmols are the correct fraction of their compound" begin
    @xmol Bar FooBar₂
    1molFooBar₂ == 2BarmolFooBar₂
    @xmol Bar Foo4Bar
    4molFoo₄Bar == 1BarmolFoo₄Bar
end


text:
export triangle_update!

function prepare_dest!(D::LowerTriangular{T}) where T
    n = size(D,1)
    P = parent(D)
    @inbounds for j=2:n
        for i=1:j-1
            P[i,j] = zero(T)
        end
    end
    return P
end

function prepare_dest!(D::UpperTriangular{T}) where T
    n = size(D,1)
    P = parent(D)
    @inbounds for j=1:n-1
        for i=j+1:n
            P[i,j] = zero(T)
        end
    end
    return P
end

function prepare_dest!(D::UnitLowerTriangular{T}) where T
    n = size(D,1)
    P = parent(D)
    @inbounds P[1,1] = one(T)
    @inbounds for j=2:n
        for i=1:j-1
            P[i,j] = zero(T)
        end
        P[j,j] = one(T)
    end
    return P
end

function prepare_dest!(D::UnitUpperTriangular{T}) where T
    n = size(D,1)
    P = parent(D)
    @inbounds for j=1:n-1
        P[j,j] = one(T)
        for i=j+1:n
            P[i,j] = zero(T)
        end
    end
    @inbounds P[n,n] = one(T)
    return P
end

prepare_dest!(D) = D

function wrap_dest(D::UnitLowerTriangular, F::BlasTriangular, α::Number)
    uplo_eff(F) == 'L' || return parent(D)  # result is full square
    (F.diag == 'U' && α == 1 ) || return LowerTriangular(parent(D))  # result diagonal not unit
    return D # result still has unit diagonal
end
function wrap_dest(D::UnitUpperTriangular, F::BlasTriangular, α::Number)
    uplo_eff(F) == 'U' || return parent(D)  # result is full square
    (F.diag == 'U' && α == 1 )  || return UpperTriangular(parent(D))  # result diagonal not unit
    return D # result still has unit diagonal
end
function wrap_dest(D::AbstractTriangular, F::BlasTriangular, α::Number)
    uplo_eff(F) == uplo_eff(D) || return parent(D) # result is full square
    return D # result is still triangular
end

import LinearAlgebra.BLAS: trmm!, trsm!
for (fun,blasfun,side) in ( (:lmul!, :trmm!, 'L'),
                            (:rmul!, :trmm!, 'R'),
                            (:ldiv!, :trsm!, 'L'),
                            (:rdiv!, :trsm!, 'R')
                         )
    @eval begin
        function triangle_update!(Dest::AbstractTriangular{T}, F::BlasTriangular{T},
                                  ::typeof($fun), α::Number=1.0) where T
            Dest.blasnode == F.blasnode && error("arguments have common ancestor")
            P = prepare_dest!(Dest)
            α = convert(T,α)
            $blasfun($side, F.uplo, F.trans, F.diag, α, F.blasnode, P)
            return wrap_dest(Dest,F,α)
        end
    end
end

for (tra,Tra) in ( (:transpose, :Transpose), (:adjoint, :Adjoint))
    for (fwd,rev) in ( (:lmul!,:rmul!),
                       (:rmul!,:lmul!),
                       (:ldiv!,:rdiv!),
                       (:rdiv!,:ldiv!)
                     )
        @eval begin
            function triangle_update!(D::$Tra{T,<:BlasMatrix{T}},
                                    F::BlasTriangular{T}, ::typeof($fwd),
                                    α::Number=1.0) where T <: Real
                $tra( triangle_update!($tra(D), $tra(F), $rev, α) )
            end
        end
    end
end