text:
module DataFrameMacros

using Base: ident_cmp
using DataFrames: DataFrames, transform, transform!, select, select!, combine, subset, subset!, ByRow, passmissing, groupby, AsTable, DataFrame, GroupedDataFrame

export @transform, @transform!, @select, @select!, @combine, @subset, @subset!, @groupby, @sort, @sort!, @unique

funcsymbols = :transform, :transform!, :select, :select!, :combine, :subset, :subset!, :unique

subset_argument_docs(f) = """
## @subset argument

You can pass a `@subset` expression as the second argument to `@$f`,
between the input argument and the source-function-sink expressions.
Then, the call is equivalent to first taking a `subset` of the input
with `view = true`, then calling `$f` on the subset and returning the
mutated input. If the input is a `GroupedDataFrame`, the parent `DataFrame` is returned.

```julia
df = DataFrame(x = 1:5, y = 6:10)
@$f(df, @subset(:x > 3), :y = 20, :z = 3 * :x)
```
"""

for f in funcsymbols
    @eval begin
        """
            @$($f)(df, args...; kwargs...)

        The `@$($f)` macro builds a `DataFrames.$($f)` call. Each expression in `args` is converted to a `src => function => sink` construct that conforms to the transformation mini-language of DataFrames.

        Keyword arguments `kwargs` are passed down to `$($f)` but have to be separated from the positional arguments by a semicolon `;`.

        The transformation logic for all DataFrameMacros macros is explained in the `DataFrameMacros` module docstring, accessible via `?DataFrameMacros`.

        $($f in (transform!, select!) ? subset_argument_docs($f) : "")
        """
        macro $f(exprs...)
            macrohelper($(QuoteNode(f)), exprs...)
        end
    end
end

function defaultbyrow(s::Symbol)
    if s == :transform
        true
    elseif s == :select
        true
    elseif s == :subset
        true
    elseif s == :transform!
        true
    elseif s == :select!
        true
    elseif s == :subset!
        true
    elseif s == :combine
        false
    elseif s == :unique
        true
    else
        error("Unknown symbol $s")
    end
end

macro groupby(exprs...)
    f = :select
    df, subset_expr, func, kw_exprs = df_funcs_kwexprs(f, exprs...)

    select_kwexprs = [Expr(:kw, :copycols, false)]
    select_call = build_call(f, df, func, select_kwexprs)
    quote
        temp = $select_call
        df_copy = copy($(esc(df)))
        for name in names(temp)
            df_copy[!, name] = temp[!, name]
        end
        groupby(df_copy, names(temp); $(kw_exprs...))
    end
end

macro sort(exprs...)
    sorthelper(exprs...; mutate = false)
end

macro sort!(exprs...)
    sorthelper(exprs...; mutate = true)
end

function sorthelper(exprs...; mutate)
    f = :select
    df, subset_expr, func, kw_exprs = df_funcs_kwexprs(f, exprs...)

    select_kwexprs = [Expr(:kw, :copycols, false)]
    select_call = build_call(f, df, func, select_kwexprs)
    if mutate
        quote
            temp = $select_call
            sp = sortperm(temp; $(kw_exprs...))
            $(esc(df)) = $(esc(df))[sp, :]
        end
    else
        quote
            temp = $select_call

            sp = sortperm(temp; $(kw_exprs...))

            df_copy = copy($(esc(df)))
            df_copy[sp, :]
        end
    end
end

function macrohelper(f, exprs...)
    df, subset_expr, converted, kw_exprs = df_funcs_kwexprs(f, exprs...)

    if subset_expr !== nothing
        dfsym = gensym()
        dfsym_e = esc(dfsym)
        subset_converted = convert_subset_expr(subset_expr, dfsym)
        subset_converted_ungroup_false = copy(subset_converted)
        scg = subset_converted_ungroup_false

        if scg.args[2] isa Expr && scg.args[2].head == :parameters
            push!(scg.args[2].args, Expr(:kw, :ungroup, false))
        else
            insert!(scg.args, 2, Expr(:parameters, Expr(:kw, :ungroup, false)))
        end

        subs = gensym()
        call_expr = build_call(f, subs, converted, kw_exprs)
        e = quote
            let
                $dfsym_e = $(esc(df))
                if $dfsym_e isa GroupedDataFrame
                    $(esc(subs)) = $subset_converted_ungroup_false
                else
                    $(esc(subs)) = $subset_converted
                end
                $call_expr
                if $dfsym_e isa GroupedDataFrame
                    parent($dfsym_e)
                else
                    $dfsym_e
                end
            end
        end
        return e
    else
        return build_call(f, df, converted, kw_exprs)
    end
end

function convert_subset_expr(subset_expr, df)
    args = subset_expr.args[3:end]
    viewtrue_kw = Expr(:kw, :view, true)
    if args[1] isa Expr && args[1].head == :parameters
        push!(args[1].args, viewtrue_kw)
        insert!(args, 2, df)
    else
        insert!(args, 1, df)
        insert!(args, 1, Expr(:parameters, viewtrue_kw))
    end
    macrohelper(:subset, args...)
end

function build_call(f, df, converted, kw_exprs)
    :($f($(esc(df)), $(converted...); $(map(esc, kw_exprs)...)))
end

function extract_subset_expr!(exprs, f)
    subset_expr = nothing

    i_subsets = findall(is_subset_expr, exprs)

    if !isempty(i_subsets) && f ∉ (:transform!, :select!)
        error("The `@subset` argument syntax only works with `@transform!` and `@select!`, not `@$f`")
    end

    if length(i_subsets) > 1
        error("Only one @subset argument allowed, found $(length(i_subsets))")
    elseif length(i_subsets) == 1
        if first(i_subsets) != 1
            error("A @subset expression must be used after the dataframe argument and before all source-function-sink expressions.")
        end
        subset_expr = popfirst!(exprs)
    end

    subset_expr
end

function df_funcs_kwexprs(f, exprs...)

    exprs = [exprs...]

    if length(exprs) >= 1 && exprs[1] isa Expr && exprs[1].head == :parameters
        kw_exprs = exprs[1].args
        df = exprs[2]
        source_func_sink_exprs = exprs[3:end]
    else
        df = exprs[1]
        source_func_sink_exprs = exprs[2:end]
        kw_exprs = []
    end

    subset_expr = extract_subset_expr!(source_func_sink_exprs, f)

    if length(source_func_sink_exprs) == 1 && is_block_expression(source_func_sink_exprs[1])
        source_func_sink_exprs = extract_source_func_sink_exprs_from_block(
            source_func_sink_exprs[1])
    end

    converted = map(e -> convert_source_funk_sink_expr(f, e, df), source_func_sink_exprs)
    df, subset_expr, converted, kw_exprs
end

is_subset_expr(x) = false
is_subset_expr(e::Expr) = e.head == :macrocall &&
    e.args[1] == Symbol("@subset")

is_block_expression(x) = false
is_block_expression(e::Expr) = e.head == :block

function extract_source_func_sink_exprs_from_block(block::Expr)
    filter(x -> !(x isa LineNumberNode), block.args)
end

convert_source_funk_sink_expr(f, x, df) = x

function convert_source_funk_sink_expr(f, e::Expr, df)
    target, formula = split_formula(e)
    flags, formula = extract_macro_flags(formula)

    if 't' in flags
        if target !== nothing
            error("There should be no target expression when the @t flag is used. The implicit target is `AsTable`. Target received was $target")
        end
        target_expr = AsTable
        formula = convert_automatic_astable_formula(formula)
    else
        target_expr = make_target_expression(df, target)
    end

    formula_is_column = is_column_expr(formula)

    columns = gather_columns(formula)
    func, columns = make_function_expr(formula, columns)
    clean_columns = map(c -> clean_column(c, df), columns)
    stringified_columns = [esc(stringarg_expr(c, df)) for c in clean_columns]

    byrow = (defaultbyrow(f) && !('c' in flags)) ||
        (!defaultbyrow(f) && ('r' in flags))

    pass_missing = 'm' in flags

    func_esc = esc(func)

    if pass_missing
        func_esc = :(passmissing($func_esc))
    end

    func_esc = byrow ? :(ByRow($func_esc)) : :($func_esc)

    trans_expr = if target_expr === nothing
        if formula_is_column
            :($(stringified_columns...) .=> $(stringified_columns...))
        else
            :($(esc(vcat)).($(stringified_columns...)) .=> $func_esc)
        end
    else
        if formula_is_column
            :($(stringified_columns...) .=> $target_expr)
        else
            :($(esc(vcat)).($(stringified_columns...)) .=> $func_esc .=> $target_expr)
        end
    end
end

make_target_expression(df, ::Nothing) = nothing

function make_target_expression(df, expr)

    if expr isa Expr && expr.head == :string && any(is_target_shortcut_string, expr.args)
        return shortcut_string_expr_to_target_expr(expr)
    end

    # not really columns but resolved names
    columns = gather_columns(expr)
    clean_columns = map(c -> clean_column(c, df), columns)

    replaced_expr = postwalk(expr) do e
        # check first if this is an escaped symbol
        # and if yes return it unwrapped
        if is_escaped_symbol(e)
            return e.args[1]
        end

        # Check if this expression matches one of the column expressions
        # and wrap it with DataFrameMacros.stringargs(df, ex) if it matches.
        # This will later resolve to a vector of strings, which DataFrames
        # can handle.
        i = findfirst(c -> c == e, columns)
        if i === nothing
            e
        else
            c = clean_columns[i]
            stringarg_expr(c, df)
        end
    end

    esc(replaced_expr)
end

function make_target_expression(df, s::String)
    if !(is_target_shortcut_string(s))
        s
    else
        shortcut_string_expr_to_target_expr(s)
    end
end

is_target_shortcut_string(s::String) = match(r"{\d*}", s) !== nothing
is_target_shortcut_string(x) = false

function shortcut_string_expr_to_target_expr(e::Expr)
    sym = gensym()
    newargs = mapreduce(vcat, e.args) do arg
        if is_target_shortcut_string(arg)
            shortcut_string_expr_to_target_expr_args(arg, sym)
        else
            esc(arg)
        end
    end
    e = Expr(:string, newargs...)
    :($sym -> $e)
end

function shortcut_string_expr_to_target_expr(s::String)
    sym = gensym()
    args = shortcut_string_expr_to_target_expr_args(s, sym)
    e = Expr(:string, args...)
    :($sym -> $e)
end

function shortcut_string_expr_to_target_expr_args(s::String, sym)
    indices = findall(r"{\d*}", s)
    args = []
    index = 1
    i = 1
    while index < length(s)
        if index < indices[i].start
            push!(args, s[index:indices[i].start-1])
            index = indices[i].start
        elseif index == indices[i].start
            bracketcontent = s[indices[i]][2:end-1]
            if isempty(bracketcontent)
                push!(args, :(getindex_colnames($sym, 1)))
            else
                j = parse(Int, bracketcontent)
                push!(args, :(getindex_colnames($sym, $j)))
            end
            index = indices[i].stop + 1
            i = i < length(indices) ? i + 1 : i
        else
            push!(args, s[index:end])
            break
        end
    end
    args
end

function getindex_colnames(names, i)
    if !(1 <= i <= length(names))
        error("Invalid string index $i for $(length(names)) column name$(length(names) > 1 ? "s" : "") $names")
    end
    names[i]
end

function split_formula(e::Expr)
    if e.head != :(=)
        target = nothing
        formula = e
    else
        target = e.args[1]
        formula = e.args[2]
    end
    target, formula
end

function gather_columns(x; unique = true)
    columns = []
    gather_columns!(columns, x; unique = unique)
    columns
end

function gather_columns!(columns, x; unique)
    if is_column_expr(x)
        if !unique || x ∉ columns
            push!(columns, x)
        end
    elseif x isa Expr && !(is_escaped_symbol(x)) && !(is_dot_quotenode_expr(x))
        # we have to exclude quotenodes in dot syntax such as the b in `:a.b`
        args_to_scan = if x.head == :. && length(x.args) == 2 && x.args[2] isa QuoteNode
            x.args[1:1]
        else
            x.args
        end
        foreach(args_to_scan) do arg
            gather_columns!(columns, arg; unique = unique)
        end
    end
end

flagchars = "crmt"

extract_macro_flags(x) = "", x

function extract_macro_flags(e::Expr)
    if e.head == :macrocall && all(char -> occursin(char, flagchars), string(e.args[1])[2:end]) &&
            length(e.args) == 3
        string(e.args[1])[2:end], e.args[3]
    else
        "", e
    end
end

is_dot_quotenode_expr(x) = false
# matches Module.[Submodule.Subsubmodule...].value
function is_dot_quotenode_expr(e::Expr)
    e.head == :(.) &&
        length(e.args) == 2 &&
        (e.args[1] isa Symbol || is_dot_quotenode_expr(e.args[1])) &&
        e.args[2] isa QuoteNode
end

is_column_expr(q::QuoteNode) = true
is_column_expr(x) = false
function is_column_expr(e::Expr)
    e.head == :call && e.args[1] in (:Not, :Between, :All) ||
        (e.head == :$ && !is_escaped_symbol(e))
end

function make_function_expr(formula, columns)
    is_simple, symbol = is_simple_function_call(formula, columns)
    if is_simple
        # :a + :a returns only +(:a) if we don't set unique false
        return symbol, gather_columns(formula; unique = false)
    end

    newsyms = map(x -> gensym(), columns)
    replaced_formula = postwalk(formula) do e
        # check first if this is an escaped symbol
        # and if yes return it unwrapped
        if is_escaped_symbol(e)
            return e.args[1]
        end

        # check if this expression matches one of the column expressions
        # and replace it with a newsym if it matches
        i = findfirst(c -> c == e, columns)
        if i === nothing
            e
        else
            newsyms[i]
        end
    end
    expr = quote
        ($(newsyms...),) -> $replaced_formula
    end
    expr, columns
end

stringarg_expr(x, df) = :(DataFrameMacros.stringargs($x, $df))
stringarg_expr(x::String, df) = x

clean_column(x::QuoteNode, df) = string(x.value)
clean_column(x, df) = :(DataFrameMacros.stringargs($x, $df))
clean_column(x::String, df) = x
function clean_column(e::Expr, df)
    stripped_e = if e.head == :$
        e.args[1]
    else
        e
    end
end

stringargs(x, df) = names(df, x)
stringargs(a::AbstractVector, df) = names(df, a)
# this is needed because from matrix up `names` fails
function stringargs(a::AbstractArray, df)
    s = size(a)
    reshape(names(df, vec(a)), s)
end

stringargs(sym::Symbol, df) = string(sym)
stringargs(s::String, df) = s

is_escaped_symbol(e::Expr) = e.head == :$ && length(e.args) == 1 && e.args[1] isa QuoteNode
is_escaped_symbol(x) = false

is_simple_function_call(x, columns) = false, nothing
function is_simple_function_call(expr::Expr, columns)
    is_call = expr.head == :call
    no_columns_in_funcpart = isempty(gather_columns(expr.args[1]))
    only_columns_in_argpart = length(expr.args) >= 2 &&
        all(x -> x in columns, expr.args[2:end])

    is_simple = is_call && no_columns_in_funcpart && only_columns_in_argpart

    is_simple, expr.args[1]
end

# from macrotools
walk(x, inner, outer) = outer(x)
walk(x::Expr, inner, outer) = outer(Expr(x.head, map(inner, x.args)...))
postwalk(f, x) = walk(x, x -> postwalk(f, x), f)

convert_automatic_astable_formula(x) = x
function convert_automatic_astable_formula(e::Expr)
    e_replaced, assigned_symbols, gensyms = replace_assigned_symbols(e)
    Expr(:block,
        e_replaced,
        Expr(:tuple,
            map(assigned_symbols, gensyms) do a, g
                Expr(:(=), a, g)
            end...
        )
    )
end

function replace_assigned_symbols(e)
    symbols = Symbol[]
    gensyms = Symbol[]

    function gensym_for_sym(sym)
        i = findfirst(==(sym), symbols)

        if i === nothing
            push!(symbols, sym)
            gs = gensym()
            push!(gensyms, gs)
        else
            gs = gensyms[i]
        end
        gs
    end

    new_ex = postwalk(e) do x
        # normal case :symbol = expression
        if x isa Expr && x.head == :(=) && x.args[1] isa QuoteNode
            sym = x.args[1].value
            gs = gensym_for_sym(sym)
            Expr(:(=), gs, x.args[2:end]...)
        # tuple destructuring case x, y = expr1, expr2 where x or y (or others) are :symbols
        elseif x isa Expr && x.head == :(=) && x.args[1] isa Expr && x.args[1].head == :tuple
            for (i, a) in enumerate(x.args[1].args)
                if a isa QuoteNode
                    sym = a.value
                    x.args[1].args[i] = gensym_for_sym(sym)
                end
            end
            x
        else
            x
        end
    end
    new_ex, symbols, gensyms
end

@doc """
DataFrameMacros offers macros which transform expressions for DataFrames functions that use the `source => function => sink` mini-language.
The supported functions are `@transform`/`@transform!`, `@select/@select!`, `@groupby`, `@combine`, `@subset`/`@subset!`, `@sort`/`@sort!` and `@unique`.

All macros have signatures of the form:
```julia
@macro(df, args...; kwargs...)
```

Each positional argument in `args` is converted to a `source .=> function .=> sink` expression for the transformation mini-language of DataFrames.
By default, all macros execute the given function **by-row**, only `@combine` executes **by-column**.
There is automatic broadcasting across all column specifiers, so it is possible to directly use multi-column specifiers such as `All()`, `Not(:x)`, `r"columnname"` and `startswith("prefix")`.

For example, the following pairs of expressions are equivalent:

```julia
transform(df, :x .=> ByRow(x -> x + 1) .=> :y)
@transform(df, :y = :x + 1)

select(df, names(df, All()) .=> ByRow(x -> x ^ 2))
@select(df, \$(All()) ^ 2)

combine(df, :x .=> (x -> sum(x) / 5) .=> :result)
@combine(df, :result = sum(:x) / 5)
```

## Column references

Each positional argument must be of the form `[sink =] some_expression`.
Columns can be referenced within `sink` or `some_expression` using a `Symbol`, a `String`, or an `Int`.
Any column identifier that is not a `Symbol` or one of the multicolumn selectors `All()`, `Between()` and `Not()` must be prefaced with the interpolation symbol `\$`.
The `\$` interpolation symbol also allows to use variables or expressions that evaluate to column identifiers.

The five expressions in the following code block are equivalent.

```julia
using DataFrames
using DataFrameMacros

df = DataFrame(x = 1:3)

@transform(df, :y = :x + 1)
@transform(df, :y = \$"x" + 1)
@transform(df, :y = \$1 + 1)
col = :x
@transform(df, :y = \$col + 1)
cols = [:x, :y, :z]
@transform(df, :y = \$(cols[1]) + 1)
```

### Multi-column references

You can also use multi-column specifiers.
For example `@select(df, sqrt(Between(2, 4)))` acts as if the function `sqrt` is applied along each column that belongs to the group selected by `Between(2, 4)`.
Because the source-function-sink complex is connected by broadcasted pairs like `source .=> function .=> sink`, you can use multi-column specifiers together with single-column specifiers in the same expression.
For example, `@select(df, All() + :x)` would compute `df.some_column + df.x` for each column in the DataFrame `df`.

### Sink names in multi-column scenarios

For most complex function expressions, DataFrames concatenates all names of the columns that you used to create a new sink column name, which looks like `col1_col2_function`.
It's common that you want to use a different naming scheme, but you can't write `@select(df, :x = All() + 1)` because then every new column would be named `x` and that is disallowed.
There are several options to deal with the problem of multiple new columns:

- You can use a Vector of strings such as `["x", "y", "z"] = sqrt(All())`, the length has to match the number of columns in the multi-column specifier. This is the most direct way to specify multiple names, but it doesn't leverage the names of the used columns dynamically.
- You can reference existing column names and modify them somehow, like `"sqrt_of_" .* All()) = sqrt(All())`. Note that column specifiers on the left-hand side of the `=` operator like `All()` are resolved to a Vector of column names, not to the content of the columns like on the right-hand side. This requires you to repeat selectors, though.
- You can use a function that takes in a vector of column names (even if there's just one) and outputs a new column name, like `(cols -> uppercase(cols[1])) = sqrt(All())`.
- You can use DataFrameMacro's string shortcut syntax. If there's a string literal with one or more {} brackets, it's treated as an anonymous function that takes in column names and splices them into the string. `{}` is equivalent to `{1}`, but you can access further names with `{2}` and so on, if there is more than one column used in the function. In the example above, you could rename all columns with `@select(df, "sqrt_of_{}" = sqrt(All()))`.

## Passing multiple expressions

Multiple expressions can be passed as multiple positional arguments, or alternatively as separate lines in a `begin end` block. You can use parentheses, or omit them. The following expressions are equivalent:

```julia
@transform(df, :y = :x + 1, :z = :x * 2)
@transform df :y = :x + 1 :z = :x * 2
@transform df begin
    :y = :x + 1
    :z = :x * 2
end
@transform(df, begin
    :y = :x + 1
    :z = :x * 2
end)
```

## Flag macros

You can modify the behavior of all macros using flag macros, which are not real macros but only signal changed behavior for a positional argument to the outer macro.

Each flag is specified with a single character, and you can combine these characters as well.
The supported flags are:

| character | meaning |
|:--|:--|
| r | Switch to **by-row** processing. |
| c | Switch to **by-column** processing. |
| m | Wrap the function expression in `passmissing`. |
| t | Collect all `:symbol = expression` expressions into a `NamedTuple` where `(; symbol = expression, ...)` and set the sink to `AsTable`. |

### Example `@c`

To compute a centered column with `@transform`, you need access to the whole column at once and signal this with the `@c` flag.

```julia
using Statistics
using DataFrames
using DataFrameMacros

julia> df = DataFrame(x = 1:3)
3×1 DataFrame
 Row │ x
     │ Int64
─────┼───────
   1 │     1
   2 │     2
   3 │     3

julia> @transform(df, :x_centered = @c :x .- mean(:x))
3×2 DataFrame
 Row │ x      x_centered
     │ Int64  Float64
─────┼───────────────────
   1 │     1        -1.0
   2 │     2         0.0
   3 │     3         1.0
```

### Example `@m`

Many functions need to be wrapped in `passmissing` to correctly return `missing` if any input is `missing`.
This can be achieved with the `@m` flag macro.

```julia
julia> df = DataFrame(name = ["alice", "bob", missing])
3×1 DataFrame
 Row │ name
     │ String?
─────┼─────────
   1 │ alice
   2 │ bob
   3 │ missing

julia> @transform(df, :name_upper = @m uppercasefirst(:name))
3×2 DataFrame
 Row │ name     name_upper
     │ String?  String?
─────┼─────────────────────
   1 │ alice    Alice
   2 │ bob      Bob
   3 │ missing  missing
```

### Example `@t`

In DataFrames, you can return a `NamedTuple` from a function and then automatically expand it into separate columns by using `AsTable` as the sink value. To simplify this process, you can use the `@t` flag macro, which collects all statements of the form `:symbol = expression` in the function body, collects them into a `NamedTuple`, and sets the sink argument to `AsTable`.

```julia
julia> df = DataFrame(name = ["Alice Smith", "Bob Miller"])
2×1 DataFrame
 Row │ name
     │ String
─────┼─────────────
   1 │ Alice Smith
   2 │ Bob Miller

julia> @transform(df, @t begin
           s = split(:name)
           :first_name = s[1]
           :last_name = s[2]
       end)
2×3 DataFrame
 Row │ name         first_name  last_name
     │ String       SubString…  SubString…
─────┼─────────────────────────────────────
   1 │ Alice Smith  Alice       Smith
   2 │ Bob Miller   Bob         Miller
```

The `@t` flag also works with tuple destructuring syntax, so the previous example can be shortened to:

```julia
@transform(df, @t :first_name, :last_name = split(:name))
```

""" DataFrameMacros

const _titanic = include("titanic.jl")
titanic() = deepcopy(_titanic)

end

max_issues_repo_name: jkrumbiegel/DFMacros

max_forks_repo_name: jkrumbiegel/DFMacros


text:
using BinaryBuilder

name = "Bzip2"
version = v"1.0.8"

# Collection of sources required to build bzip2
sources = [
    GitSource("git://sourceware.org/git/bzip2.git",
              "6a8690fc8d26c815e798c588f796eabe9d684cf0"),
]

# Bash recipe for building across all platforms
script = "VERSION=$(version)\n" * raw"""
cd $WORKSPACE/srcdir/bzip2*/

# Welp, auto-patching an include because otherwise win32/64 bzip2 won't cross-compile
sed -i 's/sys\\stat\.h/sys\/stat\.h/g' bzip2.c

# Override stubborn makevars
CFLAGS="-Wall -Winline -O2 -g -D_FILE_OFFSET_BITS=64 -fPIC"
OBJS="blocksort.o huffman.o crctable.o randtable.o compress.o decompress.o bzlib.o"
make CFLAGS="${CFLAGS}" -j${nproc} ${OBJS}
make CFLAGS="${CFLAGS}" PREFIX=${prefix} install

# Build dynamic library
if [[ "${target}" == *-darwin* ]]; then
    cc -shared -current_version "${VERSION}" -compatibility_version 1.0 -o "libbz2.${VERSION}.dylib" ${OBJS}
    ln -s libbz2.${VERSION}.dylib libbz2.1.0.dylib
    ln -s libbz2.${VERSION}.dylib libbz2.1.dylib
    ln -s libbz2.${VERSION}.dylib libbz2.dylib
elif [[ "${target}" == *-mingw* ]]; then
    cc -shared -o libbz2-1.dll ${OBJS}
    ln -s libbz2-1.dll libbz2.dll
else
    cc -shared -Wl,-soname -Wl,libbz2.so.1.0 -o "libbz2.so.${VERSION}" ${OBJS}
    ln -s "libbz2.so.${VERSION}" libbz2.so.1.0
    ln -s "libbz2.so.${VERSION}" libbz2.so.1
    ln -s "libbz2.so.${VERSION}" libbz2.so
fi
mkdir -p ${libdir}
mv libbz2*.${dlext}* ${libdir}/.

# Add pkg-config file
mkdir -p ${prefix}/lib/pkgconfig
cat << EOF > $prefix/lib/pkgconfig/bzip2.pc
prefix=${prefix}
exec_prefix=\${prefix}
libdir=\${exec_prefix}/$(basename ${libdir})
sharedlibdir=\${libdir}
includedir=\${prefix}/include

Name: bzip2
Description: bzip2 compression library
Version: ${VERSION}

Requires:
Libs: -L\${libdir} -L\${sharedlibdir} -lbz2
Cflags: -I\${includedir}
EOF
"""

# These are the platforms we will build for by default, unless further
# platforms are passed in on the command line.
platforms = supported_platforms(; experimental=true)

# The products that we will ensure are always built
products = [
    LibraryProduct("libbz2", :libbzip2),
]

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

build_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies; julia_compat="1.6")

ext: jl


text:
module CompileMRI

    using Pkg, PackageCompiler

    include("utility.jl")
    include("compile_romeo.jl")
    include("compile_clearswi.jl")

    export  compile_romeo,
            update_romeo,
            compile_clearswi,
            update_clearswi

end # module


text:
for (T, f) in ((:OtherSelection, :other_cols), (:ElseSelection, :else_cols), (:AllSelection, :all_cols))
    @eval begin
        struct $(T) <: AbstractContextSelection; end
        $(f)() = $(T)()
        (-)(x::$(T)) = throw(ArgumentError(string($(f), "() cannot be negated.")))
        bool(s::$(T)) = true
        Base.show(io::IO, s::$(T)) = print(io, $(f), "()")
    end
end

"""
```
other_cols()
all_cols()
else_cols()
```

`all_cols()` -- select all columns
`other_cols()` select the columns that were not selected in any other part of the selection query
`else_cols()` -- select the columns that the previous selection query didn't capture

See also: [`select`](@ref), [`selections`](@ref),
"""
all_cols, other_cols, else_cols


text:
n = rand(1:10)
A = matrixdepot("minij", n)
@test issym(A)
@test isposdef(A)
println("'minij' passed test...")


text:
using Documenter
using DocThemeIndigo
using Nabla

const indigo = DocThemeIndigo.install(Nabla)
makedocs(
    modules=[Nabla],
    format=Documenter.HTML(
        prettyurls=false,
        assets=[indigo],
    ),
    sitename="Nabla.jl",
    authors="Invenia Labs",
    pages=[
        "Home" => "index.md",
        "API" => "pages/api.md",
        "Custom Sensitivities" => "pages/custom.md",
        "Details" => "pages/autodiff.md",
    ],
)

deploydocs(
    repo = "github.com/invenia/Nabla.jl.git",
    push_preview=true,
)


text:
function occupied_neig(matr, i, j)
	occ = 0
	for x in (i > 1 ? i - 1 : i):(i < length(matr) ? i + 1 : i)
		for y in (j > 1 ? j - 1 : j):(j < length(matr[i]) ? j + 1 : j)
			if (x != i || y != j) && matr[x][y] == '#'
				occ += 1
			end
		end
	end
	occ
end

function occupied_look(matr, i, j)
	occ = 0
	dirs = [(1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)]
	for dir in dirs
		x = i + dir[1]
		y = j + dir[2]
		while (1 <= x && x <= length(matr)) && (1 <= y && y <= length(matr[x])) && matr[x][y] == '.'
			x += dir[1]
			y += dir[2]
		end
		if (1 <= x && x <= length(matr)) && (1 <= y && y <= length(matr[x])) && matr[x][y] == '#'
			occ += 1
		end
	end
	occ
end

function update(matr, occupied, tolerance)
	curr = copy(matr)
	next = [collect(str) for str in matr]
	for i in 1:length(curr)
		for j in 1:length(curr[i])
			if curr[i][j] == 'L' && occupied(curr, i, j) == 0
				next[i][j] = '#'
			elseif curr[i][j] == '#' && occupied(curr, i, j) >= tolerance
				next[i][j] = 'L'
			end
		end
	end
	[join(chars) for chars in next]
end

function solve_neig(input)
	curr = copy(input)
	prev = []
	while prev != curr
		prev = copy(curr)
		curr = update(prev, occupied_neig, 4)
	end
	count(c -> c == '#', join(curr))
end

function solve_look(input)
	curr = copy(input)
	prev = []
	while prev != curr
		prev = copy(curr)
		curr = update(prev, occupied_look, 5)
	end
	count(c -> c == '#', join(curr))
end

@assert solve_neig(readlines("input/test/test_day11.txt")) == 37 ["test case 1 failed"]
@assert solve_look(readlines("input/test/test_day11.txt")) == 26 ["test case 2 failed"]

input = readlines("input/day11.txt")
println("First star: " * string(@time solve_neig(input)))
println("Second star: " * string(@time solve_look(input)))


text:
# copied from https://github.com/FluxML/Zygote.jl/blob/master/src/lib/lib.jl

cache(cx::AContext) = cx.cache === nothing ? (cx.cache = IdDict()) : cx.cache

function grad_mut(cx::AContext, x)
  ch = cache(cx)
  if haskey(ch, x)
    ch[x]
  else
    ch[x] = grad_mut(x)
  end
end

accum(x, y) =
  x === nothing ? y :
  y === nothing ? x :
  x + y
accum(x::AbstractArray, y::AbstractArray) = accum.(x, y)


text:
using Test
using TOML
using TOML: Internals

function testval(s, v)
    f = "foo = $s"
    parsed = TOML.parse(f)["foo"]
    return isequal(v, parsed) && typeof(v) == typeof(parsed)
end

function failval(s, v)
    f = "foo = $s"
    err = TOML.tryparse(f);
    return err isa TOML.Internals.ParserError && err.type == v
end

@testset "Numbers" begin
    @test failval("00"                   , Internals.ErrParsingDateTime)
    @test failval("-00"                  , Internals.ErrParsingDateTime)
    @test failval("+00"                  , Internals.ErrParsingDateTime)
    @test failval("00.0"                 , Internals.ErrParsingDateTime)
    @test failval("-00.0"                , Internals.ErrParsingDateTime)
    @test failval("+00.0"                , Internals.ErrParsingDateTime)
    @test failval("9223372036854775808"  , Internals.ErrOverflowError)
    @test failval("-9223372036854775809" , Internals.ErrOverflowError)

    @test failval("0."        , Internals.ErrNoTrailingDigitAfterDot)
    @test failval("0.e"       , Internals.ErrNoTrailingDigitAfterDot)
    @test failval("0.E"       , Internals.ErrNoTrailingDigitAfterDot)
    @test failval("0.0E"      , Internals.ErrGenericValueError)
    @test failval("0.0e"      , Internals.ErrGenericValueError)
    @test failval("0.0e-"     , Internals.ErrGenericValueError)
    @test failval("0.0e+"     , Internals.ErrGenericValueError)
    @test_broken failval("0.0e+00" , Internals.ErrGenericValueError)

    @test testval("1.0"         , 1.0)
    @test testval("1.0e0"       , 1.0)
    @test testval("1.0e+0"      , 1.0)
    @test testval("1.0e-0"      , 1.0)
    @test testval("1.001e-0"    , 1.001)
    @test testval("2e10"        , 2e10)
    @test testval("2e+10"       , 2e10)
    @test testval("2e-10"       , 2e-10)
    @test testval("2_0.0"       , 20.0)
    @test testval("2_0.0_0e0_0" , 20.0)
    @test testval("2_0.1_0e1_0" , 20.1e10)

    @test testval("1_0"    , 10    |> Int64)
    @test testval("1_0_0"  , 100   |> Int64)
    @test testval("1_000"  , 1000  |> Int64)
    @test testval("+1_000" , 1000  |> Int64)
    @test testval("-1_000" , -1000 |> Int64)

    @test failval("0_"     , Internals.ErrLeadingZeroNotAllowedInteger)
    @test failval("0__0"   , Internals.ErrLeadingZeroNotAllowedInteger)
    @test failval("__0"    , Internals.ErrUnexpectedStartOfValue)
    @test failval("1_0_"   , Internals.ErrTrailingUnderscoreNumber)
    @test failval("1_0__0" , Internals.ErrUnderscoreNotSurroundedByDigits)
end

@testset "Booleans" begin
    @test testval("true", true)
    @test testval("false", false)

    @test failval("true2"  , Internals.ErrExpectedNewLineKeyValue)
    @test failval("false2" , Internals.ErrExpectedNewLineKeyValue)
    @test failval("talse"  , Internals.ErrGenericValueError)
    @test failval("frue"   , Internals.ErrGenericValueError)
    @test failval("t1"     , Internals.ErrGenericValueError)
    @test failval("f1"     , Internals.ErrGenericValueError)
end

@testset "Datetime" begin
    @test testval("2016-09-09T09:09:09"     , DateTime(2016 , 9 , 9 , 9 , 9 , 9))
    @test testval("2016-09-09T09:09:09Z"    , DateTime(2016 , 9 , 9 , 9 , 9 , 9))
    @test testval("2016-09-09T09:09:09.0Z"  , DateTime(2016 , 9 , 9 , 9 , 9 , 9))
    @test testval("2016-09-09T09:09:09.012" , DateTime(2016 , 9 , 9 , 9 , 9 , 9  , 12))

    @test failval("2016-09-09T09:09:09.0+10:00"   , Internals.ErrOffsetDateNotSupported)
    @test failval("2016-09-09T09:09:09.012-02:00" , Internals.ErrOffsetDateNotSupported)
    @test failval("2016-09-09T09:09:09.0+10:00"   , Internals.ErrOffsetDateNotSupported)
    @test failval("2016-09-09T09:09:09.012-02:00" , Internals.ErrOffsetDateNotSupported)

    @test failval("2016-09-09T09:09:09.Z" , Internals.ErrParsingDateTime)
    @test failval("2016-9-09T09:09:09Z"   , Internals.ErrParsingDateTime)
    @test failval("2016-13-09T09:09:09Z"  , Internals.ErrParsingDateTime)
    @test failval("2016-02-31T09:09:09Z"  , Internals.ErrParsingDateTime)
    @test failval("2016-09-09T09:09:09x"  , Internals.ErrParsingDateTime)
    @test failval("2016-09-09s09:09:09Z"  , Internals.ErrParsingDateTime)
    @test failval("2016-09-09T09:09:09x"  , Internals.ErrParsingDateTime)
end

@testset "Time" begin
    @test testval("09:09:09.99"    , Time(9 , 9 , 9 , 99))
    @test testval("09:09:09.99999" , Time(9 , 9 , 9 , 999))

    @test failval("09:09x09", Internals.ErrParsingDateTime)
end

# TODO: Add more dedicated value tests

@testset "String" begin
    @test failval("\"foooo", Internals.ErrUnexpectedEndString)

    #=
    Found these examples of string tests somewhere
    quot0=""" """    # valid
    quot1=""" """"   # valid
    quot2=""" """""  # valid
    quot3=""" """""" # invalid
    apos0=''' '''    # valid
    apos1=''' ''''   # valid
    apos2=''' '''''  # valid
    apos3=''' '''''' # invalid

    quot4=""""""     # valid (6 in a row, empty string)
    quot5="""" """   # valid
    quot6=""""" """  # valid
    quot7="""""" """ # invalid
    apos4=''''''     # valid (6 in a row, empty string)
    apos5='''' '''   # valid
    apos6=''''' '''  # valid
    apos7='''''' ''' # invalid

    quot8="""""\""""""     # valid (5 at start, 6 at end)
    quot9="""""\"""\"""""" # valid (3 in the middle 5 at start, 6 at end)

    =#
end

@testset "Array" begin
    @test testval("[1,2,3]", Int64[1,2,3])
    @test testval("[1.0, 2.0, 3.0]", Float64[1.0, 2.0, 3.0])
    @test testval("[1.0, 2.0, 3]", Union{Int64, Float64}[1.0, 2.0, Int64(3)])
    @test testval("[1.0, 2, \"foo\"]", Any[1.0, Int64(2), "foo"])
end


text:
# Higher-level helpers for libsilo_h functions
const DBCreate = DBCreateReal

_VERSION_STRING() = unsafe_string(DBVersion())

function _VERSION()
    _VERSION_Maj = Ref{Cint}(0)
    _VERSION_Min = Ref{Cint}(0)
    _VERSION_Pat = Ref{Cint}(0)
    _VERSION_Pre = Ref{Cint}(0)
    DBVersionDigits(_VERSION_Maj, _VERSION_Min, _VERSION_Pat, _VERSION_Pre)
    VersionNumber(getindex(_VERSION_Maj), getindex(_VERSION_Min), getindex(_VERSION_Pat))
end

"""
Checks whether passed `file_path` points to valid silo file.
"""
is_silofile(file_path::String) = (DBInqFileReal(file_path)>0)


text:
using BaremetalPi

"""
    function trigger(pin::Int)

The pin numbers to use are the GPIO pin numbers.  Thus 23 is
GPIO23 which is the Pi pin number 16.  The pin number reference
is available from the web or the Pi command pinout.

Alternative Packages to try:
    https://github.com/notinaboat/PiGPIOMEM.jl/blob/master/src/PiGPIOMEM.jl
    PiGPIO.jl
    etc.
"""
function trigger(pin::Int; duration::Real = 0.050)

    # The following command is required by BaremetalPi.jl but
    # may be done by the MCC software
    init_gpio()

    gpio_set_mode(pin, :out)
    try
        gpio_set(pin)
        sleep(duration)
        gpio_clear(pin)
    finally
        gpio_set_mode(pin, :in)
    end
end

#=
# Using the PiGPIO package.  Its downside is that it requires
# root privileges.
using PiGPIO

"""
    function trigger(pin::Int)

Before first use, the daemon must be launched from the shell
with sudo privileges.  This is only done once per Linux session

The pin numbers to use are the GPIO pin numbers.  Thus 23 is
GPIO23 which is the Pi pin number 16.  The pin number reference
is available from the web or the Pi command pinout.

    try
        t = `sudo pigpiod`
        run(t)
    end
"""
function trigger(pin::Int)
    p = Pi()
    set_mode(p, pin, PiGPIO.OUTPUT)
    try
        PiGPIO.write(p, pin, PiGPIO.ON)
        sleep(5)
        PiGPIO.write(p, pin, PiGPIO.OFF)
    finally
        set_mode(p, pin, PiGPIO.INPUT)
    end
end
=#

max_stars_repo_name: Spectrum-Tec/MccDaqHats

max_issues_repo_name: Spectrum-Tec/MccDaqHats

max_forks_repo_name: Spectrum-Tec/MccDaqHats


text:
"""
    TiffFile(io) -> TiffFile

Wrap `io` with helper parameters to keep track of file attributes.

$(FIELDS)
"""
mutable struct TiffFile
    """A unique ID describing this file that is embedded in the XML"""
    uuid::String

    """The relative path to this file"""
    filepath::String

    """The file stream"""
    io::Stream

    """Location of the first IFD in the file stream"""
    first_offset::Int

    """Whether this file has a different endianness than the host computer"""
    need_bswap::Bool

    function TiffFile(io::Stream)
        file = new()
        file.io = io
        seekstart(io)
        # TODO: Parsing the filename from the IO name is likely to be fragile
        file.filepath = extract_filename(io)
        file.need_bswap = check_bswap(io)
        file.first_offset = Int(do_bswap(file, read(file.io, UInt32)))
        file
    end
end

function TiffFile(uuid::String, filepath::String)
    try
        file = TiffFile(open(filepath))
        file.uuid = uuid
        file.filepath = filepath
        return file
    catch e # the file probably got renamed
        (!isa(e, SystemError)) && rethrow(e)
        throw(FileIO.LoaderError("OMETIFF", "It looks like this file was renamed, "*
        "but has internal links with the original name. Please rename to $filepath "*
        "to load. See https://github.com/tlnagy/OMETIFF.jl/issues/14 for details."))
    end
end

TiffFile(io::IOStream) = TiffFile(getstream(io))

"""
    usingUUID(tf) -> Bool

Whether there was a UUID embedded in this file. According to the
[schema](https://www.openmicroscopy.org/Schemas/Documentation/Generated/OME-2016-06/ome.html)
UUIDs have the following pattern:
`(urn:uuid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})`
"""
usingUUID(tf::TiffFile) = isdefined(tf, :uuid) && startswith(tf.uuid, "urn:uuid:")

"""
    IFD(file, strip_offsets) -> IFD

Build an Image File Directory (IFD), i.e. a TIFF slice. This structure retains a
pointer to its parent file and a list of the offsets within the file
corresponding to the data strips.

$(FIELDS)
"""
struct IFD
    """Pointer to the file containing this IFD"""
    file::TiffFile

    """Location(s) in `file` of the data corresponding to this IFD"""
    strip_offsets::Vector{Int}
end

"""
    get_ifds(orig_file, ifd_index, ifd_files) -> Dict, Dict

Run through all the IFDs extracted from the OMEXML and open all the referenced
files to construct a mapping of ZCTP (not guaranteed order) index to IFD object.
This is necessary because there can be multiple files referenced in a single
OMEXML and we need to iterate over the files to identify the actual offsets for
the data since this information isn't found in the OMEXML.

**Output**
- `Dict{Tuple{String, String}, TiffFile}`: a mapping of filepath, UUID to the
   actual TiffFile object
- `OrderedDict{NTuple{4, Int}, IFD}`: a mapping of ZCTP (or other order) index
   to the IFD objects in order that the IFDs are referenced in the OMEXML
"""
function get_ifds(orig_file::TiffFile,
                  ifd_index::OrderedDict{Int, NTuple{4, Int}},
                  ifd_files::OrderedDict{Int, Tuple{String, String}})

    # open all files referenced by the tiffdatas
    files = Dict{Tuple{String, String}, TiffFile}()
    for item in unique(values(ifd_files))
        uuid, filepath = item

        target_uuid = isdefined(orig_file, :uuid) ? orig_file.uuid : nothing

        # if this file is the same as the base file, then don't open it again
        if uuid != nothing && uuid == target_uuid
            files[item] = orig_file
        # if the UUID is missing or different, attempt to match on filepath
        elseif filepath == orig_file.filepath
            files[item] = orig_file
        # else open a new pointer to this file
        else
            files[item] = TiffFile(uuid, filepath)
        end
    end

    ifds = OrderedDict{NTuple{4, Int}, IFD}()

    for (fileid, file) in files
        # iterate over the file and find all stored offsets for IFDs
        ifd_offsets = collect(file)

        ifd_idx_in_file = 0
        for ifd in sort(collect(keys(ifd_index)))
            ifd_file = ifd_files[ifd]

            if ifd_file == fileid
                ifds[ifd_index[ifd]] = IFD(file, ifd_offsets[ifd_idx_in_file+=1])
            end
        end
    end
    files, ifds
end

"""
    load_master_xml(file::TiffFile) -> EzXML.doc

Loads the master OME-XML file from `file` or from a linked file.
"""
function load_master_xml(file::TiffFile)
    omexml = loadxml(file)
    # update the UUID of the file with the value from the OMEXML
    uuid_node = findfirst("/ns:OME/@UUID", omexml, ["ns"=>namespace(omexml)])
    if uuid_node != nothing
        file.uuid = nodecontent(uuid_node)
    else # if there isn't a UUID, there might still be an internal filename
        internal_filename = findfirst("(//ns:Image | //ns:Pixels)[@Name]/@Name", omexml, ["ns"=>namespace(omexml)])
        if internal_filename != nothing
            file.uuid = nodecontent(internal_filename)
        end
    end
    try
        # Check if the full OME-XML metadata is stored in another file
        metadata_file = findfirst("/ns:OME/ns:BinaryOnly", omexml, ["ns"=>namespace(omexml)])
        (metadata_file == nothing) && return omexml
        uuid, filepath = metadata_file["UUID"], joinpath(dirname(file.filepath), metadata_file["MetadataFile"])

        # we have a companion metadata file
        if endswith(filepath, ".companion.ome")
            xdoc = readxml(filepath)
            omexml = root(xdoc)
        else
            metadata_file = TiffFile(uuid, filepath)
            omexml = loadxml(metadata_file)
            close(metadata_file.io) # clean up
            return omexml
        end
    catch err
        isa(err, BoundsError) && return omexml
        rethrow(err)
    end
end

"""
    dump_omexml(filepath) -> String

Returns the OME-XML embedded inside the OME-TIFF as a prettified string.
"""
function dump_omexml(filepath::String)
    if !endswith(filepath, ".ome.tif")
        error("Passed file is not an OME-TIFF")
    end
    io = IOBuffer()
    open(filepath) do f
        s = getstream(f)
        orig_file = OMETIFF.TiffFile(s)
        omexml = OMETIFF.load_master_xml(orig_file)
        prettyprint(io, omexml)
    end
    String(take!(io))
end

Base.eltype(::Type{TiffFile}) = Vector{Int}
Base.IteratorSize(::Type{TiffFile}) = Base.SizeUnknown()

"""
    iterate(file) -> Vector

Initializes the iterator over all IFDs in a TIFF file. This is necessary to find
the offsets for all data strips in the file, which is not stored in the OME-XML and
can only be determined by visiting each IFD.

**Output**
- `Vector{Int}`: Offsets within file for all strips corresponding to the current
   IFD
"""
Base.iterate(file::TiffFile) = iterate(file, (read_ifd(file, file.first_offset)))

"""
    iterate(file, state) -> Vector, Int

Advances the iterator to the next IFD.

**Output**
- `Vector{Int}`: Offsets within file for all strips corresponding to the current
   IFD
- `Int`: Offset of the next IFD
"""
function Base.iterate(file::TiffFile, state::Tuple{Union{Vector{Int}, Nothing}, Int})
    strip_locs, ifd = state
    # if current element doesn't exist, exit
    (strip_locs == nothing) && return nothing
    (ifd <= 0) && return (strip_locs, (nothing, 0))

    next_strip_locs, next_ifd = read_ifd(file, ifd)

    return (strip_locs, (next_strip_locs, next_ifd))
end

loadxml(file::TiffFile) = read_ifd(file, file.first_offset; getxml=true)

"""
    read_ifd(file, offset; getxml)

Read the tags of the IFD located at `offset` in TiffFile `file`.
"""
function read_ifd(file::TiffFile, offset::Int; getxml=false)
    seek(file.io, offset)

    number_of_entries = do_bswap(file, read(file.io, UInt16))

    strip_offset_list = Int[]
    strip_offset = 0
    strip_count = 0
    width = 0
    height = 0

    tag_info = Array{UInt16}(undef, 2)
    data_info = Array{UInt32}(undef, 2)

    for i in 1:number_of_entries
        read!(file.io, tag_info)
        read!(file.io, data_info)
        tag_id, tag_type = do_bswap(file, tag_info)
        data_count, data_offset = do_bswap(file, data_info)

        curr_pos = position(file.io)
        if tag_id == 256
            width = data_offset
        elseif tag_id == 257 # height of image
            height = data_offset
        elseif tag_id == 273 # offset in stream to first strip
            strip_offset = data_offset
            strip_count = data_count
        # number of rows per strip should be equal to the height of image for now
        elseif tag_id == 278
            rows_per_strip = data_offset
            strip_num = floor(Int, (height + rows_per_strip - 1) / rows_per_strip)

            # if the data is spread across multiple strips
            if strip_num > 1
                seek(file.io, strip_offset)
                strip_info = Vector{UInt32}(undef, strip_num)
                read!(file.io, strip_info)
                strip_offsets = do_bswap(file, strip_info)
                strip_offset_list = Int.(strip_offsets)
            else
                strip_offset_list = [Int(strip_offset)]
            end
        elseif getxml && tag_id == 270 # Image Description tag
            seek(file.io, data_offset)
            # strip null values from string
            _data = Array{UInt8}(undef, data_count)
            read!(file.io, _data)
            raw_str = replace(String(_data), "\0"=>"")
            # check if is xml since ImageJ display settings are also stored in
            # ImageDescription tags
            # TODO: This should be replaced with some proper validation
            if raw_str[1:5] == "<?xml"
                xdoc = parsexml(raw_str)
                return root(xdoc)
            end
        end
        seek(file.io, curr_pos)
    end

    next_ifd = Int(do_bswap(file, read(file.io, UInt32)))
    strip_offset_list, next_ifd
end

"""
    load_comments(file) -> String

Extracts the MicroManager embedded description, if present. Else returns an
empty string.
"""
function load_comments(file)
    seek(file.io, 24)
    comment_header = Int(do_bswap(file, read(file.io, UInt32)))
    if comment_header != 99384722
        return ""
    end
    comment_offset = Int(do_bswap(file, read(file.io, UInt32)))
    seek(file.io, comment_offset)
    comment_header = read(file.io, UInt32)
    comment_length = Int(do_bswap(file, read(file.io, UInt32)))
    metadata_bytes = Array{UInt8}(undef, comment_length)
    read!(file.io, metadata_bytes)
    metadata = JSON.parse(String(metadata_bytes))
    if !haskey(metadata, "Summary")
        return ""
    end
    metadata["Summary"]
end

"""
    _read_ifd_data!(target, ifd, buffer)

Reads the IFD `ifd` into `target` using a temporary buffer `buffer`. If the IFD
is stripped, `buffer` must be 1-dimensional array, otherwise, it should be the
same size as a `target`.
"""
function _read_ifd_data!(ifd::IFD, target::AbstractArray{T, 2}, buffer::AbstractArray{T, 1}) where {T}
    n_strips = length(ifd.strip_offsets)

    for j in 1:n_strips
        seek(ifd.file.io, ifd.strip_offsets[j])
        read!(ifd.file.io, buffer)
        do_bswap(ifd.file, buffer)
        view(target, j, :) .= buffer
    end
end

function _read_ifd_data!(ifd::IFD, target::AbstractArray{T, 2}, buffer::AbstractArray{T, 2}) where {T}
    seek(ifd.file.io, first(ifd.strip_offsets))
    read!(ifd.file.io, buffer)
    do_bswap(ifd.file, buffer)
end

"""
    do_bswap(file, values) -> Array

If the endianness of file is different than that of the current machine, swap
the byte order.
"""
function do_bswap(file::TiffFile, values::AbstractArray)
    if file.need_bswap
        values .= bswap.(values)
    end
    values
end

do_bswap(file::TiffFile, value) = file.need_bswap ? bswap(value) : value


text:
module OpenBLASHighCoreCount

using LinearAlgebra.BLAS
using OpenBLASHighCoreCount_jll

__init__() = BLAS.lbt_forward(OpenBLASHighCoreCount_jll.libopenblas_path)

end # module


text:
"Number of variables in the circuit"
num_variables(c::Union{ΔNode,Δ}) = length(variable_scope(c))

"Get the probability that a random world satisties the circuit"
function sat_prob(circuit::Union{ΔNode,Δ})::Rational{BigInt}
    sat_prob(circuit, v -> BigInt(1) // BigInt(2))
end

function sat_prob(circuit::Δ, varprob::Function)::Rational{BigInt}
    sat_prob(circuit[end], varprob)
end

function sat_prob(root::ΔNode, varprob::Function)::Rational{BigInt}
    f_con(n) = is_true(n) ? one(Rational{BigInt}) : zero(Rational{BigInt})
    f_lit(n) = positive(n) ? varprob(variable(n)) : one(Rational{BigInt}) - varprob(variable(n))
    f_a(n, callback) = mapreduce(callback, *, children(n))
    f_o(n, callback) = mapreduce(callback, +, children(n))
    foldup(root, f_con, f_lit, f_a, f_o, Rational{BigInt})
end

"Get the model count of the circuit"
function model_count(circuit::Δ, num_vars_in_scope::Int = num_variables(circuit))::BigInt
    # note that num_vars_in_scope can be more than num_variables(circuit)
    BigInt(sat_prob(circuit) * BigInt(2)^num_vars_in_scope)
end

const Signature = Vector{Rational{BigInt}}

"""
Get a signature for each node using probabilistic equivalence checking
"""
function prob_equiv_signature(circuit::Δ, k::Int)::Dict{Union{Var,ΔNode},Signature}
    prob_equiv_signature(circuit[end],k)
end

function prob_equiv_signature(circuit::ΔNode, k::Int)::Dict{Union{Var,ΔNode},Signature}
    # uses probability instead of integers to circumvent smoothing, no mod though
    signs::Dict{Union{Var,ΔNode},Signature} = Dict{Union{Var,ΔNode},Signature}()
    prime::Int = 7919 #TODO set as smallest prime larger than num_variables
    randprob() = BigInt(1) .// rand(1:prime,k)
    do_signs(v::Var) = get!(randprob, signs, v)
    f_con(n) = (signs[n] = (is_true(n) ? ones(Rational{BigInt}, k) : zeros(Rational{BigInt}, k)))
    f_lit(n) = (signs[n] = (positive(n) ? do_signs(variable(n)) : BigInt(1) .- do_signs(variable(n))))
    f_a(n, call) = (signs[n] = (mapreduce(c -> call(c), (x,y) -> (x .* y), children(n))))
    f_o(n, call) = (signs[n] = (mapreduce(c -> call(c), (x,y) -> (x .+ y), children(n))))
    foldup(circuit, f_con, f_lit, f_a, f_o, Signature)
    signs
end

"Get the variable scope of the circuit node"
function variable_scope(circuit::Δ)::BitSet
    variable_scope(circuit[end])
end

"Get the variable scope of the root of the circuit"
function variable_scope(root::ΔNode)::BitSet
    f_con(n) = BitSet()
    f_lit(n) = BitSet(variable(n))
    f_inner(n, call) = mapreduce(call, union, children(n))
    foldup(root, f_con, f_lit, f_inner, f_inner, BitSet)
end

"Get the variable scope of each node in the circuit"
function variable_scopes(circuit::Δ)::Dict{ΔNode,BitSet}
    variable_scopes(circuit[end])
end

function variable_scopes(root::ΔNode)::Dict{ΔNode,BitSet}
    # variable_scopes(node2dag(root))
    scope = Dict{ΔNode,BitSet}()
    f_con(n) = scope[n] = BitSet()
    f_lit(n) = scope[n] = BitSet(variable(n))
    f_inner(n, call) = scope[n] = mapreduce(call, union, children(n))
    foldup(root, f_con, f_lit, f_inner, f_inner, BitSet)
    scope
end

"Is the circuit smooth?"
function is_smooth(circuit::Δ)::Bool
    is_smooth(circuit[end])
end

function is_smooth(root::ΔNode)::Bool
    result::Bool = true
    f_con(n) = BitSet()
    f_lit(n) = BitSet(variable(n))
    f_a(n, cs) = reduce(union, cs)
    f_o(n, cs) = begin
        scope = reduce(union, cs)
        result = result && all(c -> c == scope, cs)
        scope
    end
    foldup_aggregate(root, f_con, f_lit, f_a, f_o, BitSet)
    result
end

"Is the circuit decomposable?"
function is_decomposable(circuit::Δ)::Bool
    is_decomposable(circuit[end])
end

function is_decomposable(root::ΔNode)::Bool
    result::Bool = true
    f_con(n) = BitSet()
    f_lit(n) = BitSet(variable(n))
    f_a(n, cs) = begin
        result = result && disjoint(cs...)
        reduce(union, cs)
    end
    f_o(n, cs) = reduce(union, cs)
    foldup_aggregate(root, f_con, f_lit, f_a, f_o, BitSet)
    result
end

"Construct a mapping from literals to their canonical node representation"
function literal_nodes(circuit::Union{Δ,ΔNode})::Dict{Lit,ΔNode}
    lit_dict = Dict{Lit,ΔNode}()
    foreach(circuit) do n
        if isliteralgate(n)
            if haskey(lit_dict, literal(n))
                error("Circuit has multiple representations of literal $(literal(n))")
            end
            lit_dict[literal(n)] = n
        end
    end
    lit_dict
end

"Construct a mapping from constants to their canonical node representation"
function constant_nodes(circuit::Δ)::Tuple{Union{Nothing, ΔNode},Union{Nothing, ΔNode}}
    true_node = nothing
    false_node = nothing
    visit(n::ΔNode) = visit(GateType(n),n)
    visit(::GateType, n::ΔNode) = ()
    visit(::ConstantGate, n::ΔNode) = begin
        if is_true(n)
            if issomething(true_node)
                error("Circuit has multiple representations of true")
            end
            true_node = n
        else
            @assert is_false(n)
            if issomething(false_node)
                error("Circuit has multiple representations of false")
            end
            false_node = n
        end
    end
    for node in circuit
        visit(node)
    end
    (false_node, true_node)
end

"Construct a mapping from constants to their canonical node representation"
function constant_nodes2(circuit::Union{Δ,ΔNode})::Tuple{Union{Nothing, ΔNode},Union{Nothing, ΔNode}}
    true_node = nothing
    false_node = nothing
    foreach(circuit) do n
        if isconstantgate(n)
            if is_true(n)
                isnothing(true_node) || error("Circuit has multiple representations of true")
                true_node = n
            else
                @assert is_false(n)
                isnothing(false_node) || error("Circuit has multiple representations of false")
                false_node = n
            end
        end
    end
    (false_node, true_node)
end

"Check whether literal nodes are unique"
function has_unique_literal_nodes(circuit::Δ)::Bool
    literals = Set{Lit}()
    result = true
    visit(n::ΔNode) = visit(GateType(n),n)
    visit(::GateType, n::ΔNode) = ()
    visit(::LiteralGate, n::ΔNode) = begin
        if literal(n) ∈ literals
            result = false
        end
        push!(literals, literal(n))
    end
    for node in circuit
        visit(node)
    end
    return result
end

"Check whether literal nodes are unique"
function has_unique_literal_nodes2(circuit::Δ)::Bool
    has_unique_literal_nodes2(circuit[end])
end

function has_unique_literal_nodes2(root::ΔNode)::Bool
    literals = Set{Lit}()
    @inline f_con(n) = true
    @inline f_lit(n) = begin
        lit = literal(n)
        if lit in literals
            false
        else
            push!(literals, lit)
            true
        end
    end
    @inline f_a(n, cs) = all(cs)
    @inline f_o(n, cs) = all(cs)
    foldup_aggregate(root, f_con, f_lit, f_a, f_o, Bool)
end

"Check whether constant nodes are unique"
function has_unique_constant_nodes(circuit::Δ)::Bool
    seen_false = false
    seen_true = false
    result = true
    visit(n::ΔNode) = visit(GateType(n),n)
    visit(::GateType, n::ΔNode) = ()
    visit(::ConstantGate, n::ΔNode) = begin
        if is_true(n)
            if seen_true
                result = false
            end
            seen_true = true
        else
            @assert is_false(n)
            if seen_false
                result = false
            end
            seen_false = true
        end
    end
    for node in circuit
        visit(node)
    end
    return result
end

function (circuit::Δ)(data::XData)
    circuit[end](data)
end

function (root::ΔNode)(data::XData)
    evaluate(root, data)
end

function evaluate(root::ΔNode, data::XData{Bool})::BitVector
    @inline f_lit(n) = if positive(n)
        [feature_matrix(data)[:,variable(n)]]
    else
        [broadcast(!,feature_matrix(data)[:,variable(n)])]
    end
    @inline f_con(n) =
        is_true(n) ? always(Bool, num_examples(data)) : never(Bool, num_examples(data))
    @inline fa(n, call) = begin
        if num_children(n) < 2
            return call(@inbounds children(n)[1])
        else
            c1 = call(@inbounds children(n)[1])
            c2 = call(@inbounds children(n)[2])
            if num_children(n) == 2 && length(c1) == 1 && length(c2) == 1
                return [c1[1], c2[1]] # no need to allocate a new BitVector, just return pair
            end
            x = always(Bool, num_examples(data))
            accumulate_elements(x, c1, &)
            accumulate_elements(x, c2, &)
            for c in children(n)[3:end]
                accumulate_elements(x, call(c), &)
            end
            return [x]
        end
    end
    @inline fo(n, call) = begin
        x = never(Bool, num_examples(data))
        for c in children(n)
            accumulate_elements(x, call(c), |)
        end
        return [x]
    end
    conjoin_elements(foldup(root, f_con, f_lit, fa, fo, Vector{BitVector}))
end

@inline function conjoin_elements(elems::Vector{BitVector})::BitVector
    reduce((x,y) -> x .& y, elems)
end

@inline function disjoin_elements(elems::Vector{BitVector})::BitVector
    reduce((x, y) -> x .| y, elems)
end

@inline function accumulate_elements(x::BitVector, elems::Vector{BitVector}, op)::BitVector
    if length(elems) == 1
        @inbounds @. x = op(x, elems[1])
    else
        @assert length(elems) == 2
        @inbounds @. x = op(x, elems[1] & elems[2])
    end
end

function pass_down2(circuit::Δ, data::XData{Bool})
    num = num_examples(data)
    @inline f_root(n, f_s) = begin
        [always(Bool, num)]
    end
    @inline f_inner(n, f_s, p_s) = begin
        x = never(Bool, num)
        if length(f_s) == 2
            for y in p_s
                @inbounds @. x = x | ( y[1] & f_s[1] & f_s[2])
            end
        else
            @assert length(f_s) == 1
            for y in p_s
                @inbounds @. x = x | ( y[1] & f_s[1])
            end
        end
        [x]
    end

    f_leaf = f_inner

    folddown_aggregate(circuit, f_root, f_leaf, f_inner, Vector{BitVector})
end

function pass_up_down2(circuit::Δ, data::XData{Bool})
    circuit[end](data)
    pass_down2(circuit, data)
end


text:
using StochasticBlockModel

A = [0 1 1 0 0 0 0 0;
     1 0 0 1 0 0 0 0;
     1 0 0 1 0 0 0 0;
     0 1 1 0 0 0 0 0;
     0 0 0 0 0 1 1 0;
     0 0 0 0 1 0 0 1;
     0 0 0 0 1 0 0 1;
     0 0 0 0 0 1 1 0]

x = [1 0; 0 1; 0 1; 1 0; 1 0; 0 1; 0 1; 1 0]
dataset = StochasticBlockModel.Dataset(A, 2)

w = StochasticBlockModel.optimalProbMatrix(dataset, x)
obj = StochasticBlockModel.calculateObjective(dataset, w, x)
println("Obj = $obj")

sbm = StochasticBlockModel.SBM(w, "poisson")
opt_results = StochasticBlockModel.optimalAssignments(dataset, sbm, time_limit=400, verbose=true)


text:
#this is an example by https://github.com/musmo

using GLVisualize, GeometryTypes, Reactive, GLAbstraction, Colors, GLWindow, ModernGL

description = """
Example showing a basic interactive setup for working inside the REPL/Atom/Jupyter.
It also shows how to not clear what has been drawn.
The camera is fixed, since otherwise it would smear the whole image.
"""

"""
Simulation function
"""
function solve_particles(pos_vel_s)
    dt = 1.5f0 # control simulation speed

    positions, velocity, s = pos_vel_s
    for i in eachindex(positions)
        velx = 0.0f0
        vely = 0.0f0
        posi = positions[i]
        for j in eachindex(positions)
            posj = positions[j]
            dx = posj[1] - posi[1]
            dy = posj[2] - posi[2]
            distsq = dx*dx + dy*dy + 1f0
            velx = velx - s[j]*dy/distsq
            vely = vely + s[j]*dx/distsq
        end
        positions[i] = Point2f0(posi[1] + dt*velx, posi[2] + dt*vely)
    end
    positions, velocity, s
end

"""
Clears the image of the window to `color`
"""
function clear_frame!(window, color = RGB(0.2,0.2,0.2))
    glClearColor(red(color), green(color), blue(color), 1)
    GLWindow.clear_all!(window)
end

"""
Resets the state of a window
"""
function reset!(window, color=RGB(0.2,0.2,0.2))
    clear_frame!(window, color)
    empty!(window) # removes all viewables that where added with `_view`
end

"""
This code should be executed only one time per julia session!!
If you accidantly close the window, you can call this again.
"""
function init(res = (800,800))
    # giving the window a transparent background color makes it transparent to
    # the previous frame. It's arguable, if that's really how things should be,
    # but that's how it currently works ;)
    window = glscreen(
        "vortex", resolution = res,
        color = RGB(0.2,0.2,0.2), clear = false
    )
    timesignal = Signal(0.0)
    speed = Signal(1/30)

    # this is equivalent to @async renderloop(window).
    # but now you can do other stuff before an image is rendered
    # the @async is used to make this non blocking for working in the REPL/Atom
    @async begin
        while isopen(window)
            # timesignal update value (here 0.01) doesn't matter for particle simulation, but controls the color update speed
            push!(timesignal, value(timesignal)+0.01)
            render_frame(window)
            swapbuffers(window)
            poll_glfw()
            yield()
            sleep(value(speed))
        end
        destroy!(window)
    end

    # this registers a callback whenever a keybutton is clicked.
    # We use Reactive signals, so every registered callback
    # returns a new signal with the returnvalue of that callback. Since we don't
    # use that signal, Reactive will try to garbage collect it, which is why we need
    # to call preserve on it.
    preserve(map(window.inputs[:keyboard_buttons]) do ksam
        key, scancode, action, mods = ksam
        if key == GLFW.KEY_S
            println("saving screenshot")
            screenshot(window, path="screenshot.jpg")
        end
        # make sure that this function doesn't return different types
        # for the if branch.
        # Reactive would try to convert them otherwise.
        nothing
    end)

    window, timesignal, speed
end

function main(window, timesignal)
    # get the resolution of the window
    res = widths(window)
    # use Float32 whenever possible to avoid conversions (GLVisualize can
    # convert to appropriate type most of the time, though)
    num = 1000 # change the number of particles!

    # particle initial conditions. Experiment with different ideas here.
    x = Float32[(res[1]/2.0) + (res[2]/3.5) * sin(i*2*pi/num) for i=0:num-1]
    y = Float32[(res[2]/2.0) + (res[2]/3.5) * cos(i*2*pi/num) for i=0:num-1]
    s = (-1 + 2*rand(Float32,num))

    # GeometryTypes.Point2f0 -> Point{2, Float32}
    start_position = Point2f0[Point2f0(xi,yi) for (xi,yi) in zip(x,y)]

    # Everything in GLVisualize gets animated with Reactive signals. So drawing a frame
    # and animating some graphic is decoupled.
    # for more infos, checkout Reactives documentation:
    # https://github.com/JuliaLang/Reactive.jl/blob/master/doc/index.md
    # In this case we use foldp to simulate the particles for every time step.
    # signature: foldp(function, startvalue, signals...), function will be called
    # with f(startvalue, signals...) every time timesignal updates.

    position_velocity = foldp(
        (v0, t) -> solve_particles(v0),
        (start_position, zeros(Float32, num), s),
        timesignal
    )
    # extract the position
    position = map(first, position_velocity)

    # create a color signal that changes over time
    # the color will update whenever timesignal updates
    color = map(timesignal) do t
        RGBA(1,1,(sin(t)+1.)/2., 0.6)
    end

    circle = Sphere(Point2f0(0), 1f0)

    # boundingbox is still a very expensive operation, so if you don't need it
    # you can simply set it to nothing.
    # The signature for all kind of particles is:
    # visualize((primitive, positions), keyword_arguments...)
    viewable = visualize(
        (circle, position),
        boundingbox = nothing,
        color = color
    )
    # reset is basically the antagonist of _view
    reset!(window) # when you reset the window here, you can call main multiple times

    # _view adds an (animated) viewable to the list of things that you want to see
    # in `window`
    _view(viewable, window, camera=:fixed_pixel)
end

#=
workflow in Julia REPL or you can also just evaluate parts in Atom:
include("simulation.jl")
window, t, speed = init()
main(window, t)
#redefine main/solve_particles
include("simulation.jl") # if you have the chan ges in the file
main() # call again! If you only changed solve_particles, you don't even have to call main again
push!(speed, 1/20) # this is fully interactive so you can, e.g. change the speed
=#

if !isdefined(:runtests)
    window, timesignal, speed = init()
else
    # if we get the window from the example recorder, change color!
    window.color = RGBA{Float32}(0,0,0,0)
    window.clear = true
end

main(window, timesignal)
# clean up window


text:
module DomainIntegrals

using QuadGK, LinearAlgebra
using GaussQuadrature, FastGaussQuadrature, HCubature

using StaticArrays
using IntervalSets, DomainSets

import Base: convert, checkbounds, length

import DomainSets:
    EmptySpace,
    FullSpace,
    domaintype,
    codomaintype,
    prectype,
    numtype,
    domain

include("common.jl")
include("gauss.jl")
include("measure.jl")
include("singularity.jl")
include("strategy.jl")
include("rules.jl")
include("integral.jl")

end # module


text:
@testset "mass" begin

@testset "mass" begin
    iron = SDFileAtom(:Fe)
    @test atomnumber(iron) == 26
    @test standardweight(iron) == (55.845, 0.002)
    @test standardweight(Float64, iron) == 55.84
    @test monoisotopicmass(iron) == (55.93493633, 4.9e-7)
    @test monoisotopicmass(Float64, iron) == 55.934936
    @test exactmass(iron) == (55.93493633, 4.9e-7)
    @test exactmass(Float64, iron) == 55.934936

    og = SDFileAtom(:Og)
    @test atomnumber(og) == 118
    @test standardweight(og) === (NaN, NaN)
    @test exactmass(og) === (NaN, NaN)
    og294 = SDFileAtom(:Og, 0, 1, 294, [0.0, 0.0, 0.0])
    @test monoisotopicmass(og294) === (NaN, NaN)
    @test exactmass(og294) === (294.21392, 0.00071)

    etoh = smilestomol("CCO")
    wt = standardweight(etoh)
    @test isapprox(wt[1], 46.069, atol=1e-3)
    @test isapprox(wt[2], 0.004, atol=1e-3)
    @test standardweight(Float64, etoh) == 46.07
    iso = monoisotopicmass(etoh)
    @test isapprox(iso[1], 46.0418648130, atol=1e-10)
    @test isapprox(iso[2], 7.1e-10, atol=1e-10)
    ex = exactmass(etoh)
    @test isapprox(iso[1], 46.0418648130, atol=1e-10)
    @test isapprox(iso[2], 7.1e-10, atol=1e-10)

    etohd6 = smilestomol("[2H]C([2H])([2H])C([2H])([2H])O[2H]")
    @test standardweight(Float64, etohd6) == 52.11
    @test monoisotopicmass(Float64, etohd6) == 46.041865
    @test exactmass(Float64, etohd6) == 52.079525
end

@testset "isotopiccomposition" begin
    @test length(isotopiccomposition(:C, 100; threshold=0.01)) == 5
    @test length(isotopiccomposition(:H, 1000; threshold=0.01)) == 2
    @test length(isotopiccomposition(smilestomol("CCl"))) == 4
    # display(simulatemassspec(smilestomol("CCl")))
end

end  # mass


text:
function TensorKit.TO.contract!(
    α,
    tA::AbstractPhysicalTensor,
    CA::Symbol,
    tB::AbstractPhysicalTensor,
    CB::Symbol,
    β,
    tC::AbstractPhysicalTensor,
    oindA::TensorKit.IndexTuple,
    cindA::TensorKit.IndexTuple,
    oindB::TensorKit.IndexTuple,
    cindB::TensorKit.IndexTuple,
    p1::TensorKit.IndexTuple,
    p2::TensorKit.IndexTuple,
    syms::Union{Nothing,NTuple{3,Symbol}}=nothing,
)
    return TensorKit.TO.contract!(
        α, tA.tensor, CA, tB.tensor, CB, β, tC, oindA, cindA, oindB, cindB, p1, p2, syms
    )
end

TensorKit.TO.memsize(t::AbstractPhysicalTensor) = TensorKit.TO.memsize(t.tensor)

function TensorKit.storagetype(
    ::Type{<:AbstractPhysicalTensor{<:IndexSpace,N₁,N₂,Trivial,A}}
) where {N₁,N₂,A<:DenseMatrix}
    return A
end
function TensorKit.storagetype(
    ::Type{<:AbstractPhysicalTensor{<:IndexSpace,N₁,N₂,I,<:TensorKit.SectorDict{I,A}}}
) where {N₁,N₂,I<:Sector,A<:DenseMatrix}
    return A
end

@forward(
    AbstractPhysicalTensor.tensor,
    (
        Base.getindex,
        TensorKit.domain,
        TensorKit.codomain,
        TensorKit.fusiontrees,
        TensorKit.blocks,
        TensorKit.block,
        TensorKit.blocksectors,
    )
)

max_stars_repo_name: jack-dunham/PEPOKit

max_issues_repo_name: jack-dunham/PEPOKit

max_forks_repo_name: jack-dunham/PEPOKit


text:
"""An image, taken though a particular filter band"""
type Image
    # The image height.
    H::Int

    # The image width.
    W::Int

    # An HxW matrix of pixel intensities.
    pixels::Matrix{Float32}

    # The band id (takes on values from 1 to 5).
    b::Int

    # World coordinates
    wcs::WCSTransform

    # The components of the point spread function.
    psf::Vector{PsfComponent}

    # SDSS-specific identifiers. A field is a particular region of the sky.
    # A Camcol is the output of one camera column as part of a Run.
    run_num::Int
    camcol_num::Int
    field_num::Int

    # The background noise in nanomaggies. (varies by position)
    epsilon_mat::Array{Float32, 2}

    # The expected number of photons contributed to this image
    # by a source 1 nanomaggie in brightness. (varies by row)
    iota_vec::Array{Float32, 1}

    # storing a RawPSF here isn't ideal, because it's an SDSS type
    # not a Celeste type
    raw_psf_comp::RawPSF
end


text:
# example for fixed lag operation

using Test
using RoME
using LinearAlgebra
# , Distributions

# TODO -- use this for cleanup
function driveSomeMore!(fg::FactorGraph, idx)
    for i in idx:(idx+5)
      psym = Symbol("x$i")
      nsym = Symbol("x$(i+1)")
      addVariable!(fg, nsym, Pose2)
      pp = Pose2Pose2(MvNormal([10.0;0;pi/3], diagm([0.1;0.1;0.1].^2)))
      addFactor!(fg, [psym;nsym], pp )
    end
    return Symbol("x$(idx+5)")
end

@testset "test basic fixed lag operations..." begin

# start with an empty factor graph object
fg = initfg()
# IIF.getSolverParams(fg).drawtree = true
# IIF.getSolverParams(fg).showtree = true

# Set up a quasi fixed-lag horizon of 8 nodes and enable the fixed-lag solving.
# If the graph grows over 8 nodes, the older nodes will be frozen to limit the computational window.
IIF.getSolverParams(fg).qfl = 6
IIF.getSolverParams(fg).isfixedlag = true # limit up solve
IIF.getSolverParams(fg).limitfixeddown = true # also limit down solve

## 1. Drive around in a hexagon
# Add the first pose :x0
println("STEP 1: Driving around a bit")
addVariable!(fg, :x0, Pose2)
# Add at a fixed location PriorPose2 to pin :x0 to a starting location
addFactor!(fg, [:x0], PriorPose2(MvNormal(zeros(3), 0.01*Matrix{Float64}(LinearAlgebra.I,3,3))) )
for i in 0:5
  psym = Symbol("x$i")
  nsym = Symbol("x$(i+1)")
  addVariable!(fg, nsym, Pose2)
  pp = Pose2Pose2(MvNormal([10.0;0;pi/3], Matrix(Diagonal([0.1;0.1;0.1].^2))))
  addFactor!(fg, [psym;nsym], pp )
end

# Add node linking initial pose with a bearing range measurement landmark
addVariable!(fg, :l1, Point2, labels=[:LANDMARK;])
p2br = Pose2Point2BearingRange(Normal(0,0.1),Normal(20.0,1.0))
addFactor!(fg, [:x0; :l1], p2br)

## 2. Solve graph when shorter than fixed length - should solve full session.
println("STEP 2: Solve graph when shorter than fixed length")

# getSolverParams(fg).drawtree = true
# getSolverParams(fg).showtree = true
tree, smt, hist = solveTree!(fg)

# Add another node when it comes around again, linking the node with the initial landmark
p2br = Pose2Point2BearingRange(Normal(0,0.1),Normal(20.0,1.0))
addFactor!(fg, [:x6; :l1], p2br)

# 3. Drive a couple more, longer than fixed lag window
println("STEP 3: Drive a couple more, longer than fixed lag window")
for i in 6:11
  psym = Symbol("x$i")
  nsym = Symbol("x$(i+1)")
  addVariable!(fg, nsym, Pose2)
  pp = Pose2Pose2(MvNormal([10.0;0;pi/3], Matrix(Diagonal([0.1;0.1;0.1].^2))))
  addFactor!(fg, [psym;nsym], pp )
end

## At this point our window is 8 nodes, but our graph consists of 13 nodes.
## Next, freezing nodes beyond our fixed-lag horizon.

# Back up the graph
fgOriginal = deepcopy(fg)
getSolverParams(fgOriginal).isfixedlag = false

# Back up data from these two poses so we can compare them once we solve again.
X5 = deepcopy(getVal(fg, :x5))
X7 = deepcopy(getVal(fg, :x7))

@test isInitialized(fg, :x5)

fifoFreeze!(fg)
@test getData(getVariable(fg, :x5)).ismargin

# getSolverParams(fg).drawtree=true
# getSolverParams(fg).dbg=true

# Now solve again, which will freeze vertices < 5
println("STEP 4: Solve graph when shorter than fixed length, and show time to solve")
tree, smt, hist = solveTree!(fg, tree) #, recordcliqs=ls(fg))

# drawTree(stuff[1], show=true)
# st = fetch(stuff[2][11])
# hist = getCliqSolveHistory(stuff[1], :x2)

# fg.solverparams.isfixedlag
# tofreeze = fg.fifo[1:(end-fg.qfl)]
# @test length(tofreeze) > 0
# IIF.setfreeze!.(fg, tofreeze)
#
# fifoFreeze!(fg)
# tree = IIF.wipeBuildNewTree!(fg)
# inferOverTreeR!(fg, tree)

# Confirm that the initial nodes (x0 - x5) are frozen.
@test getData(getVariable(fg, :x5)).ismargin
@test !getData(getVariable(fg, :x7)).ismargin

# X5 should be exactly same
# X6 should be different
X5cmp = deepcopy(getVal(fg, :x5))
X7cmp = deepcopy(getVal(fg, :x7))
@test X5 == X5cmp #Frozen
@test X7 != X7cmp #Recalculated

# Solve original graph with the to get time comparison
# tree, smt, hist = solveTree!(fg, tree)
# @time IIF.batchSolve!(fgOriginal, treeinit=true)

# IIF.drawTree(tree, show=true)

end

#


text:
using ArrayInterface, Test
using Base: setindex
import ArrayInterface: has_sparsestruct, findstructralnz, fast_scalar_indexing, lu_instance
@test ArrayInterface.ismutable(rand(3))

using StaticArrays
@test ArrayInterface.ismutable(@SVector [1,2,3]) == false
@test ArrayInterface.ismutable(@MVector [1,2,3]) == true
@test ArrayInterface.ismutable(1:10) == false
@test ArrayInterface.ismutable((0.1,1.0)) == false
@test isone(ArrayInterface.known_first(typeof(StaticArrays.SOneTo(7))))
@test ArrayInterface.known_last(typeof(StaticArrays.SOneTo(7))) == 7

using LinearAlgebra, SparseArrays

D=Diagonal([1,2,3,4])
@test has_sparsestruct(D)
rowind,colind=findstructralnz(D)
@test [D[rowind[i],colind[i]] for i in 1:length(rowind)]==[1,2,3,4]
@test length(rowind)==4
@test length(rowind)==length(colind)

Bu = Bidiagonal([1,2,3,4], [7,8,9], :U)
@test has_sparsestruct(Bu)
rowind,colind=findstructralnz(Bu)
@test [Bu[rowind[i],colind[i]] for i in 1:length(rowind)]==[1,7,2,8,3,9,4]
Bl = Bidiagonal([1,2,3,4], [7,8,9], :L)
@test has_sparsestruct(Bl)
rowind,colind=findstructralnz(Bl)
@test [Bl[rowind[i],colind[i]] for i in 1:length(rowind)]==[1,7,2,8,3,9,4]

Tri=Tridiagonal([1,2,3],[1,2,3,4],[4,5,6])
@test has_sparsestruct(Tri)
rowind,colind=findstructralnz(Tri)
@test [Tri[rowind[i],colind[i]] for i in 1:length(rowind)]==[1,2,3,4,4,5,6,1,2,3]
STri=SymTridiagonal([1,2,3,4],[5,6,7])
@test has_sparsestruct(STri)
rowind,colind=findstructralnz(STri)
@test [STri[rowind[i],colind[i]] for i in 1:length(rowind)]==[1,2,3,4,5,6,7,5,6,7]

Sp=sparse([1,2,3],[1,2,3],[1,2,3])
@test has_sparsestruct(Sp)
rowind,colind=findstructralnz(Sp)
@test [Tri[rowind[i],colind[i]] for i in 1:length(rowind)]==[1,2,3]

@test ArrayInterface.ismutable(spzeros(1, 1))
@test ArrayInterface.ismutable(spzeros(1))

@test !fast_scalar_indexing(qr(rand(10, 10)).Q)
@test !fast_scalar_indexing(qr(rand(10, 10), Val(true)).Q)
@test !fast_scalar_indexing(lq(rand(10, 10)).Q)

using BandedMatrices

B=BandedMatrix(Ones(5,5), (-1,2))
B[band(1)].=[1,2,3,4]
B[band(2)].=[5,6,7]
@test has_sparsestruct(B)
rowind,colind=findstructralnz(B)
@test [B[rowind[i],colind[i]] for i in 1:length(rowind)]==[5,6,7,1,2,3,4]
B=BandedMatrix(Ones(4,6), (-1,2))
B[band(1)].=[1,2,3,4]
B[band(2)].=[5,6,7,8]
rowind,colind=findstructralnz(B)
@test [B[rowind[i],colind[i]] for i in 1:length(rowind)]==[5,6,7,8,1,2,3,4]

using BlockBandedMatrices
BB=BlockBandedMatrix(Ones(10,10),[1,2,3,4],[4,3,2,1],(1,0))
BB[Block(1,1)].=[1 2 3 4]
BB[Block(2,1)].=[5 6 7 8;9 10 11 12]
rowind,colind=findstructralnz(BB)
@test [BB[rowind[i],colind[i]] for i in 1:length(rowind)]==
    [1,5,9,2,6,10,3,7,11,4,8,12,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1]

dense=collect(Ones(8,8))
for i in 1:8
    dense[:,i].=[1,2,3,4,5,6,7,8]
end
BBB=BandedBlockBandedMatrix(dense, [4, 4] ,[4, 4], (1, 1), (1, 1))
rowind,colind=findstructralnz(BBB)
@test [BBB[rowind[i],colind[i]] for i in 1:length(rowind)]==
    [1,2,3,1,2,3,4,2,3,4,5,6,7,5,6,7,8,6,7,8,
     1,2,3,1,2,3,4,2,3,4,5,6,7,5,6,7,8,6,7,8]

@testset "setindex" begin
    @testset "$(typeof(x))" for x in [
        zeros(3),
        falses(3),
        spzeros(3),
    ]
        y = setindex(x, true, 1)
        @test iszero(x)  # x is not mutated
        @test y[1] == true
        @test iszero(x[CartesianIndices(size(x)) .== [CartesianIndex(1)]])

        y2 = setindex(x, one.(x), :)
        @test iszero(x)
        @test all(isone, y2)
    end

    @testset "$(typeof(x))" for x in [
        zeros(3, 3),
        falses(3, 3),
        spzeros(3, 3),
    ]
        y = setindex(x, true, 1, 1)
        @test iszero(x)  # x is not mutated
        @test y[1, 1] == true
        @test iszero(x[CartesianIndices(size(x)) .== [CartesianIndex(1, 1)]])

        y2 = setindex(x, one.(x), :, :)
        @test iszero(x)
        @test all(isone, y2)
    end

    @testset "$(typeof(x))" for x in [
        zeros(3, 3, 3),
        falses(3, 3, 3),
    ]
        y = setindex(x, true, 1, 1, 1)
        @test iszero(x)  # x is not mutated
        @test y[1, 1, 1] == true
        @test iszero(x[CartesianIndices(size(x)) .== [CartesianIndex(1, 1, 1)]])

        y2 = setindex(x, one.(x), :, :, :)
        @test iszero(x)
        @test all(isone, y2)
    end
end

using SuiteSparse
@testset "lu_instance" begin
  for A in [
    randn(5, 5),
    @SMatrix(randn(5, 5)),
    @MMatrix(randn(5, 5)),
    sprand(50, 50, 0.5)
  ]
    @test lu_instance(A) isa typeof(lu(A))
  end
  @test lu_instance(1) === 1
end

using Random
using ArrayInterface: issingular
@testset "issingular" begin
    for T in [Float64, ComplexF64]
        R = randn(MersenneTwister(2), T, 5, 5)
        S = Symmetric(R)
        L = UpperTriangular(R)
        U = LowerTriangular(R)
        @test all(!issingular, [R, S, L, U, U'])
        R[:, 2] .= 0
        @test all(issingular, [R, L, U, U'])
        @test !issingular(S)
        R[2, :] .= 0
        @test issingular(S)
        @test all(!issingular, [UnitLowerTriangular(R), UnitUpperTriangular(R), UnitUpperTriangular(R)'])
    end
end

using ArrayInterface: zeromatrix
@test zeromatrix(rand(4,4,4)) == zeros(4*4*4,4*4*4)

using ArrayInterface: parent_type
@testset "Parent Type" begin
    x = ones(4, 4)
    @test parent_type(view(x, 1:2, 1:2)) <: typeof(x)
    @test parent_type(reshape(x, 2, :)) <: typeof(x)
    @test parent_type(transpose(x)) <: typeof(x)
    @test parent_type(Symmetric(x)) <: typeof(x)
    @test parent_type(UpperTriangular(x)) <: typeof(x)
end

@testset "Range Interface" begin
    @test isnothing(ArrayInterface.known_first(typeof(1:4)))
    @test isone(ArrayInterface.known_first(typeof(Base.OneTo(4))))

    @test isnothing(ArrayInterface.known_last(typeof(1:4)))

    @test isnothing(ArrayInterface.known_step(typeof(1:0.2:4)))
    @test isone(ArrayInterface.known_step(typeof(1:4)))
end


text:
mutable struct GFUpdateState
    prev_trace::GFTrace
    trace::GFTrace
    constraints::Any
    score::Float64
    visitor::AddressVisitor
    params::Dict{Symbol,Any}
    discard::DynamicAssignment
    args_change::Any
    retchange::Union{Some{Any},Nothing}
    callee_output_changes::HomogenousTrie{Any,Any}
end

function GFUpdateState(args_change, prev_trace, constraints, params)
    visitor = AddressVisitor()
    discard = DynamicAssignment()
    GFUpdateState(prev_trace, GFTrace(), constraints, 0., visitor,
                  params, discard, args_change, nothing, HomogenousTrie{Any,Any}())
end

get_args_change(state::GFUpdateState) = state.args_change

function set_ret_change!(state::GFUpdateState, value)
    if state.retchange === nothing
        state.retchange = value
    else
        lightweight_retchange_already_set_err()
    end
end

function get_addr_change(state::GFUpdateState, addr)
    get_leaf_node(state.callee_output_changes, addr)
end

function addr(state::GFUpdateState, dist::Distribution{T}, args, addr) where {T}
    visit!(state.visitor, addr)
    constrained = has_leaf_node(state.constraints, addr)
    has_previous = has_primitive_call(state.prev_trace, addr)
    if has_internal_node(state.constraints, addr)
        lightweight_got_internal_node_err(addr)
    end
    local retval::T
    if has_previous
        prev_call::CallRecord = get_primitive_call(state.prev_trace, addr)
    end
    if constrained
        retval = get_leaf_node(state.constraints, addr)
    elseif has_previous
        retval = prev_call.retval
    else
        error("Constraint not given for new address: $addr")
    end
    if constrained && has_previous
        # there was a change and this was the previous value
        retchange = (true, prev_call.retval)
    elseif has_previous
        retchange = NoChange()
    else
        # retchange is null, because the address is new
        retchange = nothing
    end
    score = logpdf(dist, retval, args...)
    call = CallRecord(score, retval, args)
    state.trace = assoc_primitive_call(state.trace, addr, call)
    state.score += score
    if constrained && has_previous
        set_leaf_node!(state.discard, addr, prev_call.retval)
    end
    set_leaf_node!(state.callee_output_changes, addr, retchange)
    retval
end

function addr(state::GFUpdateState, gen::Generator{T}, args, addr, args_change) where {T}
    visit!(state.visitor, addr)
    if has_internal_node(state.constraints, addr)
        constraints = get_internal_node(state.constraints, addr)
    elseif has_leaf_node(state.constraints, addr)
        lightweight_got_leaf_node_err(addr)
    else
        constraints = EmptyAssignment()
    end
    if has_subtrace(state.prev_trace, addr)
        prev_trace = get_subtrace(state.prev_trace, addr)
        (trace, _, discard, retchange) = update(gen, args, args_change,
            prev_trace, constraints)
        set_internal_node!(state.discard, addr, discard)
        set_leaf_node!(state.callee_output_changes, addr, retchange)
    else
        trace = assess(gen, args, constraints)
        set_leaf_node!(state.callee_output_changes, addr, nothing)
    end
    call::CallRecord = get_call_record(trace)
    retval::T = call.retval
    state.trace = assoc_subtrace(state.trace, addr, trace)
    state.score += call.score
    retval
end

splice(state::GFUpdateState, gen::GenFunction, args::Tuple) = exec(gf, state, args)

function update(gen::GenFunction, new_args, args_change, trace::GFTrace, constraints)
    state = Gen.GFUpdateState(args_change, trace, constraints, gen.params)
    retval = Gen.exec(gen, state, new_args)
    new_call = Gen.CallRecord{Any}(state.score, retval, new_args)
    state.trace.call = new_call
    # discard addresses that were deleted
    unvisited = Gen.get_unvisited(state.visitor, get_assignment(state.prev_trace))
    merge!(state.discard, unvisited) # TODO use merge()?
    if !isempty(Gen.get_unvisited(state.visitor, constraints))
        error("Update did not consume all constraints")
    end

    # compute the weight
    prev_score = get_call_record(trace).score
    weight = state.score - prev_score
    (state.trace, weight, state.discard, state.retchange)
end

max_stars_repo_name: BoltzmannBrain/Gen

max_issues_repo_name: BoltzmannBrain/Gen

max_forks_repo_name: BoltzmannBrain/Gen


text:
using Base: @deprecate

@deprecate Distinct() Unique()


text:
# ===== VSB Verifications: Pressure Distribution =====

println("=== BEGINNING Verifications1.jl ===")

# == Imports ==
println("   IMPORTING PACKAGES...")
using VSB
using SimpleVPM
using LinearAlgebra
using Plots
pyplot()
using LaTeXStrings
using Printf

cd("/Users/Damyn/Documents/BYU/FLOW Lab/VSB/verifications")

# === Boundary Geometry and Plotting Grids ===
println("   GENERATING GEOMETRY...")
NPTS = 150                              # Number of Points on Boundary
del_theta = (2*pi)/(NPTS - 1)           # Step in theta around body
R = 1.0                                 # Cylinder Radius
X_coor = zeros(NPTS)                    # X-coordinates
Y_coor = zeros(NPTS)                    # Y-coordinates
X_tHat = zeros(NPTS)                    # X Tangent Vector
Y_tHat = zeros(NPTS)                    # X Tangent Vector
X_nHat = zeros(NPTS)                    # Y Normal Vector
Y_nHat = zeros(NPTS)                    # Y Normal Vector
theta = zeros(NPTS)                     # Angle from positive X-axis
for i=1:NPTS
    X_coor[i] = R*cos((i-1)*del_theta)
    Y_coor[i] = R*sin((i-1)*del_theta)

    X_tHat[i] = sin((i-1)*del_theta)
    Y_tHat[i] = -cos((i-1)*del_theta)

    X_nHat[i] = cos((i-1)*del_theta)
    Y_nHat[i] = sin((i-1)*del_theta)

    theta[i] = (i-1)*del_theta
end
body_pts = [[X_coor[i], Y_coor[i], 0.0] for i in 1:NPTS]
t_hats = [[X_tHat[i], Y_tHat[i], 0.0] for i in 1:NPTS]
n_hats = [[X_nHat[i], Y_nHat[i], 0.0] for i in 1:NPTS]
body = VSB.Boundary(body_pts,t_hats,n_hats)

# === CP: Analytical ===
println("   CALCULATING CP_ANALYTICAL...")
CP_analytical = [1 - 4*sin(theta[i])^2 for i in 1:NPTS]

# === CP: VSB ===
println("   CALCULATING CP_VSB...")
println("   CALCULATING PROBLEM PARAMETERS...")

U_inf = [1.0, 0.0, 0.0]
U_inf_func(X) = U_inf

params = VSB.Parameters(body, U_inf_func, verbose = true)
alphas = params.alphas

U_gamma(X) = VSB.CalcVSVelocityCirlce(body, alphas, X)

CP_VSB = zeros(NPTS)
for i in 1:NPTS
    if i % 10 == 0
        println("     - ON POINT ",i," OF ",NPTS)
    end
    scale_factor = 0.000
    X_eval = body.bodyPTS[i] + scale_factor .* body.nHats[i]
    CP_VSB[i] = 1 - (norm( U_gamma(X_eval)) / norm( U_inf_func(X_eval) ))^2
end

# === CP: VPM ===
println("   CALCULATING CP_VPM...")
CP_VPM = zeros(NPTS)

nu = 1e1
Uinf(t) = U_inf
sigma = 1.0

print("     - CREATING PARTICLE FIELD...")
pfield = SimpleVPM.ParticleField(nu,                 # Kinematic viscosity
                                 zeta_gauserf,       # Basis function
                                 g_gauserf;          # Regularizing function of zeta
                                 transposed=false,    # Transposed scheme
                                 relax=!true,        # Pedrizzeti relaxation scheme
                                 rlxf=0.3,           # Relaxation factor
                                 integration="euler",# Time integration scheme
                                 Uinf=Uinf)          # Free stream velocity
print("   DONE!")
println()

print("     - ADDING PARTICLES...")
for i=1:NPTS
    loc = body.bodyPTS[i]
    Gamma = [0.0, 0.0, VSB.CalcVS(body,alphas,loc)]
    vol = 1.0
    particle = Particle(loc, Gamma, sigma, vol)
    addparticle(pfield, particle)
end
print("   DONE!")
println()

for i=1:NPTS
    if i % 10 == 0
        println("     - ON POINT ",i," OF ",NPTS)
    end
    CP_VPM[i] = -(1 - ( norm( U(pfield, body.bodyPTS[i]) ) / norm( U_inf ))^2)
end

println()
println("   RESULTS...")
println("CP_ANALYTICAL    CP_VSB    CP_VPM")
for i in 1:NPTS
    if i % 10 == 0
        println(@sprintf("%13.3f",CP_analytical[i]),"  ",
                @sprintf("%8.3f",CP_VSB[i]),"  ",
                @sprintf("%8.3f",CP_VPM[i]))
    end
end

# === Plotting ===
println("   PLOTTING...")
plt1 = plot(theta, CP_analytical, line = (:red), label = "Analytical")
plot!(theta, CP_VSB, line = (:blue, :dash), label = "VSB")
plot!(title = "VSB Verification - Pressure Distribution",
      xlabel = L"\theta",
      xlims = (0.0, 2*pi),
      xticks = ([0.0,pi/2,pi,3*pi/2,2*pi],["0",L"\frac{\pi}{2}",L"\pi",L"\frac{3\pi}{2}",L"2\pi"]),
      ylabel = L"C_P",
      ylims = (-4,2),
      yflip = true)

plt2 = plot(theta, alphas, line=(:red), title = "Alpha Coefficients")
savefig(plt1, "VSB_Verification_CP.pdf")
savefig(plt2, "VSB_Verification_Alphas.pdf")

println("=== END OF Verifications1.jl ===")


text:
if nprocs() < 2
    id_me = myid()
    id_other = addprocs(1)[1]

    using MessageUtils
    using Base.Test
end

for id in [id_me, id_other]
    t = kvspace(id)
    put!(t, 1, 10)
    put!(t, "Hello", "World")
    put!(t, 5.0, 50.0)

    @test isready(t, 1) == true
    @test fetch(t, 1) == 10
    @test fetch(t, 5.0) == 50.0
    @test fetch(t, "Hello") == "World"
    @test take!(t, 1) == 10
    @test isready(t, 1) == false
    @test isready(t, 5.0) == true

    t = tspace(id)
    put!(t, (1, 10))
    put!(t, ("Hello", "World", "!"))
    put!(t, (5.0,))

    @test isready(t, 1) == true
    @test fetch(t, 1) == (1, 10)
    @test fetch(t, 5.0) == (5.0,)
    @test fetch(t, "Hello") == ("Hello", "World", "!")
    @test fetch(t, r".*") == ("Hello", "World", "!")
    @test take!(t, 1) == (1, 10)
    @test isready(t, 1) == false
    @test isready(t, 5.0) == true
end


text:
using JuliaKitchenSink
using Test

@testset "JuliaKitchenSink.jl" begin
    # Write your own tests here.
end