text:
struct TestStruct
    x::Bool
    y::Int64
    z::Float16
end

struct AnotherTestStruct
    x::Int
end

struct TestSingleton
end

@testset "core" begin

@testset "context" begin

let
    ctx = Context()
    @assert ctx != GlobalContext()
    dispose(ctx)
end

Context() do ctx end

end

@testset "type" begin

Context() do ctx
    typ = LLVM.Int1Type(ctx)
    @test typeof(typ.ref) == LLVM.API.LLVMTypeRef                 # untyped

    @test typeof(LLVM.IntegerType(typ.ref)) == LLVM.IntegerType   # type reconstructed
    if Base.JLOptions().debug_level >= 2
        @test_throws ErrorException LLVM.FunctionType(typ.ref)    # wrong type
    end
    @test_throws UndefRefError LLVM.FunctionType(LLVM.API.LLVMTypeRef(C_NULL))

    @test typeof(typ.ref) == LLVM.API.LLVMTypeRef
    @test typeof(LLVMType(typ.ref)) == LLVM.IntegerType           # type reconstructed
    @test_throws UndefRefError LLVMType(LLVM.API.LLVMTypeRef(C_NULL))

    @test width(LLVM.IntType(8; ctx)) == 8

    @test issized(LLVM.Int1Type(ctx))
    @test !issized(LLVM.VoidType(ctx))
end

# integer
Context() do ctx
    typ = LLVM.Int1Type(ctx)
    @test context(typ) == ctx

    show(devnull, typ)

    @test !isempty(typ)
end

# floating-point

# function
Context() do ctx
    x = LLVM.Int1Type(ctx)
    y = [LLVM.Int8Type(ctx), LLVM.Int16Type(ctx)]
    ft = LLVM.FunctionType(x, y)
    @test context(ft) == ctx

    @test !isvararg(ft)
    @test return_type(ft) == x
    @test parameters(ft) == y
    @test_throws BoundsError parameters(ft)[3]
end

# sequential
Context() do ctx
    eltyp = LLVM.Int32Type(ctx)

    ptrtyp = LLVM.PointerType(eltyp)
    @test eltype(ptrtyp) == eltyp
    @test context(ptrtyp) == context(eltyp)

    @test addrspace(ptrtyp) == 0

    ptrtyp = LLVM.PointerType(eltyp, 1)
    @test addrspace(ptrtyp) == 1
end
Context() do ctx
    eltyp = LLVM.Int32Type(ctx)

    arrtyp = LLVM.ArrayType(eltyp, 2)
    @test eltype(arrtyp) == eltyp
    @test context(arrtyp) == context(eltyp)
    @test !isempty(arrtyp)

    @test length(arrtyp) == 2
end
Context() do ctx
    eltyp = LLVM.Int32Type(ctx)

    arrtyp = LLVM.ArrayType(eltyp, 0)
    @test isempty(arrtyp)
end
Context() do ctx
    eltyp = LLVM.Int32Type(ctx)

    vectyp = LLVM.VectorType(eltyp, 2)
    @test eltype(vectyp) == eltyp
    @test context(vectyp) == context(eltyp)

    @test size(vectyp) == 2
end

# structure
Context() do ctx
    elem = [LLVM.Int32Type(ctx), LLVM.FloatType(ctx)]

    let st = LLVM.StructType(elem; ctx)
        @test context(st) == ctx
        @test !ispacked(st)
        @test !isopaque(st)

        let elem_it = elements(st)
            @test eltype(elem_it) == LLVMType

            @test length(elem_it) == length(elem)

            @test first(elem_it) == elem[1]
            @test last(elem_it) == elem[end]
            @test_throws BoundsError elem_it[3]

            i = 1
            for el in elem_it
                @test el == elem[i]
                i += 1
            end

            @test collect(elem_it) == elem
        end
    end

    let st = LLVM.StructType("foo"; ctx)
        @test name(st) == "foo"
        @test isopaque(st)
        elements!(st, elem)
        @test collect(elements(st)) == elem
        @test !isopaque(st)
    end
end

# other
Context() do ctx
    typ = LLVM.VoidType(ctx)
    @test context(typ) == ctx
end
Context() do ctx
    typ = LLVM.LabelType(ctx)
    @test context(typ) == ctx
end
Context() do ctx
    typ = LLVM.MetadataType(ctx)
    @test context(typ) == ctx
end
Context() do ctx
    typ = LLVM.TokenType(ctx)
    @test context(typ) == ctx
end

# type iteration
Context() do ctx
    st = LLVM.StructType("SomeType"; ctx)

    let ts = types(ctx)
        @test keytype(ts) == String
        @test valtype(ts) == LLVMType

        @test haskey(ts, "SomeType")
        @test ts["SomeType"] == st

        @test !haskey(ts, "SomeOtherType")
        @test_throws KeyError ts["SomeOtherType"]
    end
end

end

@testset "value" begin

Context() do ctx
Builder(ctx) do builder
LLVM.Module("SomeModule"; ctx) do mod
    ft = LLVM.FunctionType(LLVM.VoidType(ctx), [LLVM.Int32Type(ctx)])
    fn = LLVM.Function(mod, "SomeFunction", ft)

    entry = BasicBlock(fn, "entry"; ctx)
    position!(builder, entry)
    @test name(entry) == "entry"

    typ = LLVM.Int32Type(ctx)
    val = alloca!(builder, typ, "foo")
    @test context(val) == ctx
    @test typeof(val.ref) == LLVM.API.LLVMValueRef                # untyped

    @test typeof(LLVM.Instruction(val.ref)) == LLVM.AllocaInst    # type reconstructed
    if Base.JLOptions().debug_level >= 2
        @test_throws ErrorException LLVM.Function(val.ref)        # wrong
    end
    @test_throws UndefRefError LLVM.Function(LLVM.API.LLVMValueRef(C_NULL))

    @test typeof(Value(val.ref)) == LLVM.AllocaInst               # type reconstructed
    @test_throws UndefRefError Value(LLVM.API.LLVMValueRef(C_NULL))

    show(devnull, val)

    @test llvmtype(val) == LLVM.PointerType(typ)
    @test name(val) == "foo"
    @test !isconstant(val)
    @test !isundef(val)

    name!(val, "bar")
    @test name(val) == "bar"
end
end
end

# usage

Context() do ctx
Builder(ctx) do builder
LLVM.Module("SomeModule"; ctx) do mod
    ft = LLVM.FunctionType(LLVM.VoidType(ctx), [LLVM.Int32Type(ctx)])
    fn = LLVM.Function(mod, "SomeFunction", ft)

    entry = BasicBlock(fn, "entry"; ctx)
    position!(builder, entry)

    valueinst1 = add!(builder, parameters(fn)[1],
                      ConstantInt(Int32(1); ctx))
    @test !isterminator(valueinst1)

    userinst = add!(builder, valueinst1,
                    ConstantInt(Int32(1); ctx))

    # use iteration
    let usepairs = uses(valueinst1)
        @test eltype(usepairs) == Use

        usepair = first(usepairs)
        @test value(usepair) == valueinst1
        @test user(usepair) == userinst

        for _usepair in usepairs
            @test usepair == _usepair
        end

        @test value.(collect(usepairs)) == [valueinst1]
        @test user.(collect(usepairs)) == [userinst]
    end

    valueinst2 = add!(builder, parameters(fn)[1],
                    ConstantInt(Int32(2); ctx))

    replace_uses!(valueinst1, valueinst2)
    @test user.(collect(uses(valueinst2))) == [userinst]
end
end
end

# users

Context() do ctx
    # operand iteration
    mod = parse(LLVM.Module,  """
        define void @fun(i32) {
        top:
          %1 = add i32 %0, 1
          ret void
        }"""; ctx)
    fun = functions(mod)["fun"]

    for (i, instr) in enumerate(instructions(first(blocks(fun))))
        ops = operands(instr)
        @test eltype(ops) == Value
        if i == 1
            @test length(ops) == 2
            @test ops[1] == first(parameters(fun))
            @test ops[2] == ConstantInt(LLVM.Int32Type(ctx), 1)
            @test_throws BoundsError ops[3]
        elseif i == 2
            @test length(ops) == 0
            @test collect(ops) == []
        end
    end

    dispose(mod)
end

# constants

Context() do ctx
    @testset "constants" begin

    typ = LLVM.Int32Type(ctx)
    ptrtyp = LLVM.PointerType(typ)

    let val = null(typ)
        @test isnull(val)
    end

    let val = all_ones(typ)
        @test !isnull(val)
    end

    let val = PointerNull(ptrtyp)
        @test isnull(val)
    end

    let val = UndefValue(typ)
        @test isundef(val)
        @test val isa LLVM.Constant
    end

    if LLVM.version() >= v"12"
        let val = PoisonValue(typ)
            @test ispoison(val)
            @test val isa LLVM.Constant
        end
    end

    end
end

# scalar
Context() do ctx
    @testset "integer constants" begin

    # manual construction of small values
    let
        typ = LLVM.Int32Type(ctx)
        constval = ConstantInt(typ, -1)
        @test convert(Int, constval) == -1
        @test convert(UInt32, constval) == typemax(UInt32)
    end

    # manual construction of large values
    let
        typ = LLVM.Int64Type(ctx)
        constval = ConstantInt(typ, BigInt(2)^100-1)
        @test convert(Int, constval) == -1
    end

    # automatic construction
    let
        constval = ConstantInt(UInt32(1); ctx)
        @test convert(UInt, constval) == 1
    end
    let
        constval = ConstantInt(false; ctx)
        @test llvmtype(constval) == LLVM.Int1Type(ctx)
        @test !convert(Bool, constval)

        constval = ConstantInt(true; ctx)
        @test convert(Bool, constval)
    end

    # issue #81
    for T in [Int32, UInt32, Int64, UInt64]
        constval = ConstantInt(typemax(T); ctx)
        @test convert(T, constval) == typemax(T)
    end

    end

    @testset "floating point constants" begin

    let
        typ = LLVM.HalfType(ctx)
        c = ConstantFP(typ, Float16(1.1f0))
        @test convert(Float16, c) == Float16(1.1f0)
    end
    let
        typ = LLVM.FloatType(ctx)
        c = ConstantFP(typ, 1.1f0)
        @test convert(Float32, c) == 1.1f0
    end
    let
        typ = LLVM.DoubleType(ctx)
        c = ConstantFP(typ, 1.1)
        @test convert(Float64, c) == 1.1
    end
    let
        typ = LLVM.X86FP80Type(ctx)
        # TODO: how to construct full-width constants?
        c = ConstantFP(typ, 1.1)
        @test convert(Float64, c) == 1.1
    end
    for T in [LLVM.FP128Type, LLVM.PPCFP128Type]
        typ = T(ctx)
        # TODO: how to construct full-width constants?
        c = ConstantFP(typ, 1.1)
        @test convert(Float64, c) == 1.1
    end
    for T in [Float16, Float32, Float64]
        c = ConstantFP(typemax(T); ctx)
        @test convert(T, c) == typemax(T)
    end

    end

    @testset "array constants" begin

    # from Julia values
    let
        vec = Int32[1,2,3,4]
        ca = ConstantArray(vec; ctx)
        @test size(vec) == size(ca)
        @test length(vec) == length(ca)
        @test ca[1] == ConstantInt(vec[1]; ctx)
        @test collect(ca) == ConstantInt.(vec; ctx)
    end
    let
        vec = Float32[1.1f0,2.2f0,3.3f0,4.4f0]
        ca = ConstantArray(vec; ctx)
        @test size(vec) == size(ca)
        @test length(vec) == length(ca)
        @test ca[1] == ConstantFP(vec[1]; ctx)
        @test collect(ca) == ConstantFP.(vec; ctx)
    end
    let
        # tests for ConstantAggregateZero, constructed indirectly.
        # should behave similarly to ConstantArray since it can get returned there.
        ca = ConstantArray(Int[]; ctx)
        @test size(ca) == (0,)
        @test length(ca) == 0
        @test isempty(collect(ca))
    end

    # multidimensional
    let
        vec = rand(Int, 2,3,4)
        ca = ConstantArray(vec; ctx)
        @test size(vec) == size(ca)
        @test length(vec) == length(ca)
        @test collect(ca) == ConstantInt.(vec; ctx)
    end

    end

    @testset "struct constants" begin

    # from Julia values
    let
        test_struct = TestStruct(true, -99, 1.5)
        constant_struct = ConstantStruct(test_struct; ctx, anonymous=true)
        constant_struct_type = llvmtype(constant_struct)

        @test constant_struct_type isa LLVM.StructType
        @test context(constant_struct) == ctx
        @test !ispacked(constant_struct_type)
        @test !isopaque(constant_struct_type)

        @test collect(elements(constant_struct_type)) ==
            [LLVM.Int1Type(ctx), LLVM.Int64Type(ctx), LLVM.HalfType(ctx)]

        expected_operands = [
            ConstantInt(LLVM.Int1Type(ctx), Int(true)),
            ConstantInt(LLVM.Int64Type(ctx), -99),
            ConstantFP(LLVM.HalfType(ctx), 1.5)
        ]
        @test collect(operands(constant_struct)) == expected_operands
    end
    let
        test_struct = TestStruct(false, 52, -2.5)
        constant_struct = ConstantStruct(test_struct; ctx)
        constant_struct_type = llvmtype(constant_struct)

        @test constant_struct_type isa LLVM.StructType

        expected_operands = [
            ConstantInt(LLVM.Int1Type(ctx), Int(false)),
            ConstantInt(LLVM.Int64Type(ctx), 52),
            ConstantFP(LLVM.HalfType(ctx), -2.5)
        ]
        @test collect(operands(constant_struct)) == expected_operands

        # re-creating the same type shouldn't fail
        ConstantStruct(TestStruct(true, 42, 0); ctx)
        # unless it's a conflicting type
        @test_throws ArgumentError ConstantStruct(AnotherTestStruct(1), "TestStruct"; ctx)

    end
    let
        test_struct = TestSingleton()
        constant_struct = ConstantStruct(test_struct; ctx)
        constant_struct_type = llvmtype(constant_struct)

        @test isempty(operands(constant_struct))
    end
    let
        @test_throws ArgumentError ConstantStruct(1; ctx)
    end

    end
end

# constant expressions
Context() do ctx
    @testset "constant expressions" begin

    # inline assembly
    let
        ft = LLVM.FunctionType(LLVM.VoidType(ctx))
        asm = InlineAsm(ft, "nop", "", false)
        @check_ir asm "void ()* asm \"nop\", \"\""
    end

    # integer
    let
        val = LLVM.ConstantInt(Int32(42); ctx)

        for f = [const_neg, const_nswneg, const_nuwneg]
            ce = f(val)::LLVM.Constant
            @check_ir ce "i32 -42"
        end

        ce = const_not(val)::LLVM.Constant
        @check_ir ce "i32 -43"

        other_val = LLVM.ConstantInt(Int32(2); ctx)

        for f in [const_add, const_nswadd, const_nuwadd]
            ce = f(val, other_val)::LLVM.Constant
            @check_ir ce "i32 44"
        end

        for f in [const_sub, const_nswsub, const_nuwsub]
            ce = f(val, other_val)::LLVM.Constant
            @check_ir ce "i32 40"
        end

        for f in [const_mul, const_nswmul, const_nuwmul]
            ce = f(val, other_val)::LLVM.Constant
            @check_ir ce "i32 84"
        end

        for f in [const_udiv, const_sdiv]
            ce = f(val, other_val)::LLVM.Constant
            @check_ir ce "i32 21"

            ce = f(val, other_val; exact=true) # TODO: test that differs::LLVM.Constant
            @check_ir ce "i32 21"
        end

        for f in [const_urem, const_srem]
            ce = f(val, other_val)::LLVM.Constant
            @check_ir ce "i32 0"
        end

        ce = const_and(val, other_val)::LLVM.Constant
        @check_ir ce "i32 2"

        ce = const_or(val, other_val)::LLVM.Constant
        @check_ir ce "i32 42"

        ce = const_xor(val, other_val)::LLVM.Constant
        @check_ir ce "i32 40"

        ce = const_icmp(LLVM.API.LLVMIntUGT, val, other_val)::LLVM.Constant
        @check_ir ce "i1 true"

        ce = const_shl(val, other_val)::LLVM.Constant
        @check_ir ce "i32 168"

        for f in [const_lshr, const_ashr]
            ce = f(val, other_val)::LLVM.Constant
            @check_ir ce "i32 10"
        end

        for f in [const_trunc, const_truncorbitcast]
            ce = const_trunc(val, LLVM.Int16Type(ctx))::LLVM.Constant
            @check_ir ce "i16 42"
        end

        for f in [const_sext, const_zext, const_sextorbitcast, const_zextorbitcast]
            ce = f(val, LLVM.Int64Type(ctx))::LLVM.Constant
            @check_ir ce "i64 42"
        end

        for f in [const_uitofp, const_sitofp]
            ce = f(val, LLVM.FloatType(ctx))::LLVM.Constant
            @check_ir ce "float 4.200000e+01"
        end

        ce = const_bitcast(val, LLVM.FloatType(ctx))::LLVM.Constant
        @check_ir ce "float 0x36F5000000000000"

        ce = const_intcast(val, LLVM.Int64Type(ctx), true)::LLVM.Constant
        @check_ir ce "i64 42"

        ce = const_intcast(val, LLVM.Int16Type(ctx), true)::LLVM.Constant
        @check_ir ce "i16 42"
    end

    # floating-point
    let
        val = LLVM.ConstantFP(Float32(42.); ctx)

        ce = const_fneg(val)::LLVM.Constant
        @check_ir ce "float -4.200000e+01"

        other_val = LLVM.ConstantFP(Float32(2.); ctx)

        ce = const_fadd(val, other_val)::LLVM.Constant
        @check_ir ce "float 4.400000e+01"

        ce = const_fsub(val, other_val)::LLVM.Constant
        @check_ir ce "float 4.000000e+01"

        ce = const_fmul(val, other_val)::LLVM.Constant
        @check_ir ce "float 8.400000e+01"

        ce = const_fdiv(val, other_val)::LLVM.Constant
        @check_ir ce "float 2.100000e+01"

        ce = const_frem(val, other_val)::LLVM.Constant
        @check_ir ce "float 0.000000e+00"

        ce = const_fcmp(LLVM.API.LLVMRealUGT, val, other_val)::LLVM.Constant
        @check_ir ce "i1 true"

        for f in [const_fptrunc, const_fpcast]
            ce = f(val, LLVM.HalfType(ctx))::LLVM.Constant
            @check_ir ce "half 0xH5140"
        end

        for f in [const_fpext, const_fpcast]
            ce = f(val, LLVM.DoubleType(ctx))::LLVM.Constant
            @check_ir ce "double 4.200000e+01"
        end

        for f in [const_fptoui, const_fptosi]
            ce = const_fptoui(val, LLVM.Int32Type(ctx))::LLVM.Constant
            @check_ir ce "i32 42"
        end
    end

    # pointer
    let
        ptr = LLVM.PointerNull(LLVM.PointerType(LLVM.Int32Type(ctx)))

        ce = const_ptrtoint(ptr, LLVM.Int32Type(ctx))::LLVM.Constant
        @check_ir ce "i32 0"

        ce = const_inttoptr(ce, llvmtype(ptr))::LLVM.Constant
        @check_ir ce "i32* null"

        @test isempty(uses(ptr))
        for f in [const_addrspacecast, const_pointercast]
            ce = f(ptr, LLVM.PointerType(LLVM.Int32Type(ctx), 1))::LLVM.Constant
            @check_ir ce "i32 addrspace(1)* addrspacecast (i32* null to i32 addrspace(1)*)"

            # deletion of a constant
            @test !isempty(uses(ptr))
            LLVM.unsafe_destroy!(ce)
            @test isempty(uses(ptr))
        end
    end

    # gep, inbounds_gep, select, extractelement, insertelement, shufflevector, exactvalue, insertvalue

    end
end

# global values
Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    st = LLVM.StructType("SomeType"; ctx)
    ft = LLVM.FunctionType(st, [st])
    fn = LLVM.Function(mod, "SomeFunction", ft)

    @test isdeclaration(fn)
    @test linkage(fn) == LLVM.API.LLVMExternalLinkage
    linkage!(fn, LLVM.API.LLVMAvailableExternallyLinkage)
    @test linkage(fn) == LLVM.API.LLVMAvailableExternallyLinkage

    @test section(fn) == ""
    section!(fn, "SomeSection")
    @test section(fn) == "SomeSection"

    @test visibility(fn) == LLVM.API.LLVMDefaultVisibility
    visibility!(fn, LLVM.API.LLVMHiddenVisibility)
    @test visibility(fn) == LLVM.API.LLVMHiddenVisibility

    @test dllstorage(fn) == LLVM.API.LLVMDefaultStorageClass
    dllstorage!(fn, LLVM.API.LLVMDLLImportStorageClass)
    @test dllstorage(fn) == LLVM.API.LLVMDLLImportStorageClass

    @test !unnamed_addr(fn)
    unnamed_addr!(fn, true)
    @test unnamed_addr(fn)

    @test alignment(fn) == 0
    alignment!(fn, 4)
    @test alignment(fn) == 4
end
end

# global variables
Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    @test isempty(globals(mod))
    gv = GlobalVariable(mod, LLVM.Int32Type(ctx), "SomeGlobal")
    @test !isempty(globals(mod))

    show(devnull, gv)

    @test initializer(gv) === nothing
    init = ConstantInt(Int32(0); ctx)
    initializer!(gv, init)
    @test initializer(gv) == init
    initializer!(gv, nothing)
    @test initializer(gv) === nothing

    @test !isthreadlocal(gv)
    threadlocal!(gv, true)
    @test isthreadlocal(gv)

    @test !isconstant(gv)
    constant!(gv, true)
    @test isconstant(gv)

    @test !isextinit(gv)
    extinit!(gv, true)
    @test isextinit(gv)

    @test threadlocalmode(gv) == LLVM.API.LLVMGeneralDynamicTLSModel
    threadlocalmode!(gv, LLVM.API.LLVMNotThreadLocal)
    @test threadlocalmode(gv) == LLVM.API.LLVMNotThreadLocal

    @test !haskey(globals(mod), "llvm.used")
    set_used!(mod, gv)
    @test haskey(globals(mod), "llvm.used")
    unsafe_delete!(mod, globals(mod)["llvm.used"])

    @test !haskey(globals(mod), "llvm.compiler.used")
    set_compiler_used!(mod, gv)
    @test haskey(globals(mod), "llvm.compiler.used")
    unsafe_delete!(mod, globals(mod)["llvm.compiler.used"])

    let gvars = globals(mod)
        @test gv in gvars
        unsafe_delete!(mod, gv)
        @test isempty(gvars)
    end
end
end

Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    st = LLVM.StructType("SomeType"; ctx)
    gv = GlobalVariable(mod, st, "SomeGlobal")

    init = null(st)
    initializer!(gv, init)
    @test initializer(gv) == init
end
end

Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    gv = GlobalVariable(mod, LLVM.Int32Type(ctx), "SomeGlobal", 1)

    @test addrspace(llvmtype(gv)) == 1
end
end

end

@testset "metadata" begin

Context() do ctx
    str = MDString("foo"; ctx)
    @test string(str) == "foo"

    # wrap as Value
    val = Value(str; ctx)
    @test val isa LLVM.MetadataAsValue

    # back to Metadata
    md = Metadata(val)
    @test md == str

    # more specific conversion
    @test convert(MDString, val) == str
end

Context() do ctx
    int = ConstantInt(42; ctx)
    @test convert(Int, int) == 42

    # wrap as Metadata
    md = Metadata(int)
    @test md isa LLVM.ValueAsMetadata

    # back to Value
    val = Value(md; ctx)
    @test val == int

    # more specific conversion
    @test convert(ConstantInt, val) == int
end

Context() do ctx
    mod = LLVM.Module("SomeModule"; ctx)
    ft = LLVM.FunctionType(LLVM.VoidType(ctx))
    f1 = LLVM.Function(mod, "f1", ft)

    push!(metadata(mod)["function"], MDNode([f1]; ctx))
    @test Value(operands(operands(metadata(mod)["function"])[1])[1]; ctx) == f1

    f2 = LLVM.Function(mod, "f2", ft)
    replace_metadata_uses!(f1, f2)
    @test Value(operands(operands(metadata(mod)["function"])[1])[1]; ctx) == f2
end

# different type; requires a hack
Context() do ctx
    mod = LLVM.Module("SomeModule"; ctx)
    ft1 = LLVM.FunctionType(LLVM.VoidType(ctx))
    f1 = LLVM.Function(mod, "f1", ft1)

    push!(metadata(mod)["function"], MDNode([f1]; ctx))
    @test Value(operands(operands(metadata(mod)["function"])[1])[1]; ctx) == f1

    ft2 = LLVM.FunctionType(LLVM.Int32Type(ctx))
    f2 = LLVM.Function(mod, "f2", ft2)
    replace_metadata_uses!(f1, f2)
    @test Value(operands(operands(metadata(mod)["function"])[1])[1]; ctx) == f2
end

Context() do ctx
    str = MDString("foo"; ctx)
    node = MDNode([str]; ctx)
    ops = operands(node)
    @test length(ops) == 1
    @test ops[1] == str
end

# null metadata, represented as null pointers in the API, by `nothing` in Julia
Context() do ctx
    ir = """
            !0 = !{i32 42, null, !"string"}
            !foo = !{!0}
        """
    mod = parse(LLVM.Module, ir; ctx)

    foo_md = operands(metadata(mod)["foo"])[1]
    @test operands(foo_md)[1] !== nothing
    @test operands(foo_md)[2] === nothing
    @test operands(foo_md)[3] !== nothing

    bar_md = MDNode([ConstantInt(Int32(42); ctx), nothing, MDString("string"; ctx)]; ctx)
    @test foo_md == bar_md
end

@testset "debuginfo" begin

Context() do ctx
mod = parse(LLVM.Module, raw"""
       define double @test(i64 signext %0, double %1) !dbg !5 {
       top:
         %2 = sitofp i64 %0 to double, !dbg !7
         %3 = fadd double %2, %1, !dbg !18
         ret double %3, !dbg !17
       }

       !llvm.module.flags = !{!0, !1}
       !llvm.dbg.cu = !{!2}

       !0 = !{i32 2, !"Dwarf Version", i32 4}
       !1 = !{i32 1, !"Debug Info Version", i32 3}
       !2 = distinct !DICompileUnit(language: DW_LANG_Julia, file: !3, producer: "julia", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, nameTableKind: GNU)
       !3 = !DIFile(filename: "promotion.jl", directory: ".")
       !4 = !{}
       !5 = distinct !DISubprogram(name: "+", linkageName: "julia_+_2055", scope: null, file: !3, line: 321, type: !6, scopeLine: 321, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
       !6 = !DISubroutineType(types: !4)
       !7 = !DILocation(line: 94, scope: !8, inlinedAt: !10)
       !8 = distinct !DISubprogram(name: "Float64;", linkageName: "Float64", scope: !9, file: !9, type: !6, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
       !9 = !DIFile(filename: "float.jl", directory: ".")
       !10 = !DILocation(line: 7, scope: !11, inlinedAt: !13)
       !11 = distinct !DISubprogram(name: "convert;", linkageName: "convert", scope: !12, file: !12, type: !6, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
       !12 = !DIFile(filename: "number.jl", directory: ".")
       !13 = !DILocation(line: 269, scope: !14, inlinedAt: !15)
       !14 = distinct !DISubprogram(name: "_promote;", linkageName: "_promote", scope: !3, file: !3, type: !6, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
       !15 = !DILocation(line: 292, scope: !16, inlinedAt: !17)
       !16 = distinct !DISubprogram(name: "promote;", linkageName: "promote", scope: !3, file: !3, type: !6, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
       !17 = !DILocation(line: 321, scope: !5)
       !18 = !DILocation(line: 326, scope: !19, inlinedAt: !17)
       !19 = distinct !DISubprogram(name: "+;", linkageName: "+", scope: !9, file: !9, type: !6, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)"""; ctx)

    fun = functions(mod)["test"]
    bb = first(collect(blocks(fun)))
    inst = first(collect(instructions(bb)))

    @test haskey(metadata(inst), LLVM.MD_dbg)
    loc = metadata(inst)[LLVM.MD_dbg]

    @test loc isa DILocation
    @test LLVM.line(loc) == 94
    @test LLVM.column(loc) == 0

    scope = LLVM.scope(loc)
    @test scope isa DISubProgram
    @test LLVM.line(scope) == 0
    @test LLVM.name(scope) == "Float64;"

    file = LLVM.file(scope)
    @test file isa DIFile
    @test LLVM.filename(file) == "float.jl"
    @test LLVM.directory(file) == "."
    @test LLVM.source(file) == ""

    loc = LLVM.inlined_at(loc)
    @test loc isa DILocation
    @test LLVM.line(loc) == 7

    loc = LLVM.inlined_at(loc)
    @test loc isa DILocation

    loc = LLVM.inlined_at(loc)
    @test loc isa DILocation

    loc = LLVM.inlined_at(loc)
    @test loc isa DILocation

    loc = LLVM.inlined_at(loc)
    @test loc === nothing
end

end

end

@testset "module" begin

Context() do ctx
    mod = LLVM.Module("SomeModule"; ctx)
    @test context(mod) == ctx

    @test name(mod) == "SomeModule"
    name!(mod, "SomeOtherName")
    @test name(mod) == "SomeOtherName"
end

Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    clone = copy(mod)
    @test mod != clone
    @test context(clone) == ctx
    dispose(clone)

    show(devnull, mod)

    inline_asm!(mod, "nop")
    @test occursin("module asm", sprint(io->show(io,mod)))

    dummyTriple = "SomeTriple"
    triple!(mod, dummyTriple)
    @test triple(mod) == dummyTriple

    dummyLayout = "e-p:64:64:64"
    datalayout!(mod, dummyLayout)
    @test string(datalayout(mod)) == dummyLayout

    md = Metadata(ConstantInt(42; ctx))

    mod_flags = flags(mod)
    mod_flags["foobar", LLVM.API.LLVMModuleFlagBehaviorError] = md

    @test occursin("!llvm.module.flags = !{!0}", sprint(io->show(io,mod)))
    @test occursin(r"!0 = !\{i\d+ 1, !\"foobar\", i\d+ 42\}", sprint(io->show(io,mod)))

    @test mod_flags["foobar"] == md
    @test_throws KeyError mod_flags["foobaz"]
end
end

# metadata iteration
Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    node = MDNode([MDString("SomeMDString"; ctx)]; ctx)

    let mds = metadata(mod)
        @test keytype(mds) == String
        @test valtype(mds) == NamedMDNode

        @test !haskey(mds, "SomeMDNode")
        @test !(node in operands(mds["SomeMDNode"]))
        @test haskey(mds, "SomeMDNode") # getindex is mutating

        push!(mds["SomeMDNode"], node)
        @test node in operands(mds["SomeMDNode"])
    end
end
end

# global variable iteration
Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    dummygv = GlobalVariable(mod, LLVM.Int32Type(ctx), "SomeGlobal")

    let gvs = globals(mod)
        @test eltype(gvs) == typeof(dummygv)

        @test first(gvs) == dummygv
        @test last(gvs) == dummygv

        for gv in gvs
            @test gv == dummygv
        end

        @test collect(gvs) == [dummygv]

        @test haskey(gvs, "SomeGlobal")
        @test gvs["SomeGlobal"] == dummygv

        @test !haskey(gvs, "SomeOtherGlobal")
        @test_throws KeyError gvs["SomeOtherGlobal"]
    end
end
end

# function iteration
Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    st = LLVM.StructType("SomeType"; ctx)
    ft = LLVM.FunctionType(st, [st])
    @test isempty(functions(mod))

    dummyfn = LLVM.Function(mod, "SomeFunction", ft)
    let fns = functions(mod)
        @test eltype(fns) == LLVM.Function

        @test !isempty(fns)

        @test first(fns) == dummyfn
        @test last(fns) == dummyfn

        for fn in fns
            @test fn == dummyfn
        end

        @test collect(fns) == [dummyfn]

        @test haskey(fns, "SomeFunction")
        @test fns["SomeFunction"] == dummyfn

        @test !haskey(fns, "SomeOtherFunction")
        @test_throws KeyError fns["SomeOtherFunction"]
    end
end
end

end

@testset "function" begin

Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    ft = LLVM.FunctionType(LLVM.VoidType(ctx), [LLVM.Int32Type(ctx)])
    fn = LLVM.Function(mod, "SomeFunction", ft)

    show(devnull, fn)

    @test personality(fn) === nothing
    pers_ft = LLVM.FunctionType(LLVM.Int32Type(ctx); vararg=true)
    pers_fn = LLVM.Function(mod, "PersonalityFunction", ft)
    personality!(fn, pers_fn)
    @test personality(fn) == pers_fn
    personality!(fn, nothing)
    @test personality(fn) === nothing
    unsafe_delete!(mod, pers_fn)

    @test !isintrinsic(fn)

    @test callconv(fn) == LLVM.API.LLVMCCallConv
    callconv!(fn, LLVM.API.LLVMFastCallConv)
    @test callconv(fn) == LLVM.API.LLVMFastCallConv

    @test LLVM.gc(fn) == ""
    gc!(fn, "SomeGC")
    @test LLVM.gc(fn) == "SomeGC"

    let fns = functions(mod)
        @test fn in fns
        unsafe_delete!(mod, fn)
        @test isempty(fns)
    end
end
end

# non-overloaded intrinsic
Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    intr_ft = LLVM.FunctionType(LLVM.VoidType(ctx))
    intr_fn = LLVM.Function(mod, "llvm.trap", intr_ft)
    @test isintrinsic(intr_fn)

    intr = Intrinsic(intr_fn)
    show(devnull, intr)

    @test !isoverloaded(intr)

    @test name(intr) == "llvm.trap"

    ft = LLVM.FunctionType(intr; ctx=ctx)
    @test ft isa LLVM.FunctionType
    @test return_type(ft) == LLVM.VoidType(ctx)

    fn = LLVM.Function(mod, intr)
    @test fn isa LLVM.Function
    @test llvmeltype(fn) == ft
    @test isintrinsic(fn)

    @test intr == Intrinsic("llvm.trap")
end
end

# overloaded intrinsic
Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    intr_ft = LLVM.FunctionType(LLVM.DoubleType(ctx), [LLVM.DoubleType(ctx)])
    intr_fn = LLVM.Function(mod, "llvm.sin.f64", intr_ft)
    @test isintrinsic(intr_fn)

    intr = Intrinsic(intr_fn)
    show(devnull, intr)

    @test isoverloaded(intr)

    @test name(intr, [LLVM.DoubleType(ctx)]) == "llvm.sin.f64"

    ft = LLVM.FunctionType(intr, [LLVM.DoubleType(ctx)])
    @test ft isa LLVM.FunctionType
    @test return_type(ft) == LLVM.DoubleType(ctx)

    fn = LLVM.Function(mod, intr, [LLVM.DoubleType(ctx)])
    @test fn isa LLVM.Function
    @test llvmeltype(fn) == ft
    @test isintrinsic(fn)

    @test intr == Intrinsic("llvm.sin")
end
end

# attributes
Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    ft = LLVM.FunctionType(LLVM.VoidType(ctx), [LLVM.Int32Type(ctx)])
    fn = LLVM.Function(mod, "SomeFunction", ft)

    let attrs = function_attributes(fn)
        @test eltype(attrs) == Attribute

        @test length(attrs) == 0

        let attr = EnumAttribute("sspreq", 0; ctx)
            @test kind(attr) != 0
            @test value(attr) == 0
            push!(attrs, attr)
            @test collect(attrs) == [attr]

            delete!(attrs, attr)
            @test length(attrs) == 0
        end

        let attr = StringAttribute("nounwind", ""; ctx)
            @test kind(attr) == "nounwind"
            @test value(attr) == ""
            push!(attrs, attr)
            @test collect(attrs) == [attr]

            delete!(attrs, attr)
            @test length(attrs) == 0
        end

        if LLVM.version() >= v"12"
            let attr = TypeAttribute("sret", LLVM.Int32Type(ctx); ctx)
                @test kind(attr) != 0
                @test value(attr) ==  LLVM.Int32Type(ctx)

                push!(attrs, attr)
                @test collect(attrs) == [attr]

                delete!(attrs, attr)
                @test length(attrs) == 0
            end
        end
    end

    for i in 1:length(parameters(fn))
        let attrs = parameter_attributes(fn, i)
            @test eltype(attrs) == Attribute
            @test length(attrs) == 0
        end
    end

    let attrs = return_attributes(fn)
        @test eltype(attrs) == Attribute
        @test length(attrs) == 0
    end
end
end

# parameter iteration
Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    ft = LLVM.FunctionType(LLVM.VoidType(ctx), [LLVM.Int32Type(ctx)])
    fn = LLVM.Function(mod, "SomeFunction", ft)

    let params = parameters(fn)
        @test eltype(params) == Argument

        @test length(params) == 1

        intparam = params[1]
        @test first(params) == intparam
        @test last(params) == intparam

        for param in params
            @test param == intparam
        end

        @test collect(params) == [intparam]
    end
end
end

# basic block iteration
Context() do ctx
LLVM.Module("SomeModule"; ctx) do mod
    ft = LLVM.FunctionType(LLVM.VoidType(ctx))
    fn = LLVM.Function(mod, "SomeFunction", ft)
    @test isempty(blocks(fn))

    entrybb = BasicBlock(fn, "SomeBasicBlock"; ctx)
    @test entry(fn) == entrybb
    let bbs = blocks(fn)
        @test eltype(bbs) == BasicBlock

        @test !isempty(bbs)
        @test length(bbs) == 1

        @test first(bbs) == entrybb
        @test last(bbs) == entrybb

        for bb in bbs
            @test bb == entrybb
        end

        @test collect(bbs) == [entrybb]
    end

    empty!(fn)
    @test isempty(blocks(fn))
end
end

end

@testset "basic blocks" begin

Context() do ctx
Builder(ctx) do builder
LLVM.Module("SomeModule"; ctx) do mod
    ft = LLVM.FunctionType(LLVM.VoidType(ctx))
    fn = LLVM.Function(mod, "SomeFunction", ft)

    bb2 = BasicBlock(fn, "SomeOtherBasicBlock"; ctx)
    @test LLVM.parent(bb2) == fn
    @test isempty(instructions(bb2))

    bb1 = BasicBlock(bb2, "SomeBasicBlock"; ctx)
    @test LLVM.parent(bb2) == fn
    position!(builder, bb1)
    brinst = br!(builder, bb2)
    position!(builder, bb2)
    retinst = ret!(builder)
    @test !isempty(instructions(bb2))

    @test terminator(bb1) == brinst
    @test terminator(bb2) == retinst

    # instruction iteration
    let insts = instructions(bb1)
        @test eltype(insts) == Instruction

        @test first(insts) == brinst
        @test last(insts) == brinst

        for inst in insts
            @test inst == brinst
        end

        @test collect(insts) == [brinst]
    end

    unsafe_delete!(bb1, brinst)    # we'll be deleting bb2, so remove uses of it

    # basic block iteration
    let bbs = blocks(fn)
        @test collect(bbs) == [bb1, bb2]

        move_before(bb2, bb1)
        @test collect(bbs) == [bb2, bb1]

        move_after(bb2, bb1)
        @test collect(bbs) == [bb1, bb2]

        @test bb1 in bbs
        @test bb2 in bbs
        delete!(fn, bb1)
        unsafe_delete!(fn, bb2)
        @test isempty(bbs)
    end
end
end
end

end

@testset "instructions" begin

Context() do ctx
Builder(ctx) do builder
LLVM.Module("SomeModule"; ctx) do mod
    ft = LLVM.FunctionType(LLVM.VoidType(ctx))
    fn = LLVM.Function(mod, "SomeFunction", ft)
    @test isempty(parameters(fn))

    ft = LLVM.FunctionType(LLVM.VoidType(ctx), [LLVM.Int1Type(ctx), LLVM.Int1Type(ctx)])
    fn = LLVM.Function(mod, "SomeOtherFunction", ft)
    @test !isempty(parameters(fn))

    bb1 = BasicBlock(fn, "entry"; ctx)
    bb2 = BasicBlock(fn, "then"; ctx)
    bb3 = BasicBlock(fn, "else"; ctx)

    position!(builder, bb1)
    brinst = br!(builder, parameters(fn)[1], bb2, bb3)
    @test opcode(brinst) == LLVM.API.LLVMBr

    position!(builder, bb2)
    retinst = ret!(builder)

    position!(builder, bb3)
    retinst = ret!(builder)

    # terminators

    @test isterminator(brinst)
    @test isconditional(brinst)
    @test condition(brinst) == parameters(fn)[1]
    condition!(brinst, parameters(fn)[2])
    @test condition(brinst) == parameters(fn)[2]

    let succ = successors(terminator(bb1))
        @test eltype(succ) == BasicBlock

        @test length(succ) == 2

        @test succ[1] == bb2
        @test succ[2] == bb3
        @test_throws BoundsError succ[3]

        @test collect(succ) == [bb2, bb3]
        @test first(succ) == bb2
        @test last(succ) == bb3

        i = 1
        for bb in succ
            @test 1 <= i <= 2
            if i == 1
                @test bb == bb2
            elseif i == 2
                @test bb == bb3
            end
            i += 1
        end

        succ[2] = bb3
        @test succ[2] == bb3
    end

    # general stuff

    @test LLVM.parent(brinst) == bb1

    # metadata
    mdval = MDNode([MDString("whatever"; ctx)]; ctx)
    let md = metadata(brinst)
        @test keytype(md) == LLVM.MD
        @test valtype(md) == LLVM.MetadataAsValue

        @test isempty(md)
        @test !haskey(md, LLVM.MD_dbg)

        md[LLVM.MD_dbg] = mdval
        @test md[LLVM.MD_dbg] == mdval

        @test !isempty(md)
        @test haskey(md, LLVM.MD_dbg)

        @test !haskey(md, LLVM.MD_tbaa)
        @test_throws KeyError md[LLVM.MD_tbaa]

        delete!(md, LLVM.MD_dbg)

        @test isempty(md)
        @test !haskey(md, LLVM.MD_dbg)
    end

    @test retinst in instructions(bb3)
    delete!(bb3, retinst)
    @test !(retinst in instructions(bb3))
    @test opcode(retinst) == LLVM.API.LLVMRet   # make sure retinst is still alive

    @test brinst in instructions(bb1)
    unsafe_delete!(bb1, brinst)
    @test !(brinst in instructions(bb1))
end
end
end

# new freeze instruction (used in 1.7 with JuliaLang/julia#38977)
Context() do ctx
    mod = parse(LLVM.Module,  """
        define i64 @julia_f_246(i64 %0) {
        top:
            %1 = freeze i64 undef
            ret i64 %1
        }"""; ctx)
    f = first(functions(mod))
    bb = first(blocks(f))
    inst = first(instructions(bb))
    @test inst isa LLVM.FreezeInst
    dispose(mod)
end

end

end


text:
# Copyright (c) 2020: Matthew Wilhelm & Matthew Stuber.
# This work is licensed under the Creative Commons Attribution-NonCommercial-
# ShareAlike 4.0 International License. To view a copy of this license, visit
# http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to Creative
# Commons, PO Box 1866, Mountain View, CA 94042, USA.
#############################################################################
# Dynamic Bounds - pODEs Discrete
# A package for discretize and relax methods for bounding pODEs.
# See https://github.com/PSORLab/DynamicBoundspODEsDiscrete.jl
#############################################################################
# src/DiscretizeRelax/method/single_step.jl
# Defines a single step of the integration method.
#############################################################################

"""
StepParams

LEPUS and Integration parameters.

$(TYPEDFIELDS)
"""
Base.@kwdef struct StepParams
    "Error tolerance of integrator"
    tol::Float64 = 1E-5
    "Minimum stepsize"
    hmin::Float64 = 1E-8
    "Number of repetitions allowed for refinement"
    repeat_limit::Int64 = 3
    "Indicates an adaptive stepsize is used"
    is_adaptive::Bool = true
    "Indicates the contractor step should be skipped"
    skip_step2::Bool = false
end

"""
StepResult{S}

Results passed to the next step.

$(TYPEDFIELDS)
"""
mutable struct StepResult{S}
    "nominal value of the state variables"
    xⱼ::Vector{Float64}
    "relaxations/bounds of the state variables"
    Xⱼ::Vector{S}
    "storage for parallelepid enclosure of `xⱼ`"
    A_Q::FixedCircularBuffer{Matrix{Float64}}
    A_inv::FixedCircularBuffer{Matrix{Float64}}
    "storage for parallelepid enclosure of `xⱼ`"
    Δ::FixedCircularBuffer{Vector{S}}
    "predicted step size for next step"
    predicted_hj::Float64
    "new time"
    time::Float64
end

"""
ExistStorage{F,K,S,T}

Storage used in the existence and uniqueness tests.
"""
mutable struct ExistStorage{F,K,S,T}
    status_flag::TerminationStatusCode
    hj::Float64
    hmin::Float64
    is_adaptive::Bool
    ϵ::Float64
    αfrac::Float64
    k::Int64
    predicted_hj::Float64
    computed_hj::Float64
    hj_max::Float64
    nx::Int64
    f_coeff::Vector{Vector{T}}
    f_temp_PU::Vector{Vector{T}}
    f_temp_tilde::Vector{Vector{T}}
    f_flt::Vector{Float64}
    hfk::Vector{T}
    fk::Vector{T}
    β::Vector{Float64}
    poly_term::Vector{T}
    Xj_0::Vector{T}
    Xj_apriori::Vector{T}
    Vⱼ::Vector{T}
    Uⱼ::Vector{T}
    Z::Vector{T}
    P::Vector{T}
    tf!::TaylorFunctor!{F,K,S,T}
end

function ExistStorage(tf!::TaylorFunctor!{F,K,S,T}, s::T, P, nx::Int64, np::Int64, k::Int64, h::Float64, cap::Int64) where {F,K,S,T}

    flag = RELAXATION_NOT_CALLED
    Xapriori = zeros(T, nx)
    Xj_0 = zeros(T, nx)
    Xj_apriori = zeros(T, nx)

    f_coeff = Vector{T}[]
    ftilde = Vector{T}[]
    fPU = Vector{T}[]
    for i in 1:(k + 1)
        push!(f_coeff, zeros(T, nx))
        push!(ftilde, zeros(T, nx))
        push!(fPU, zeros(T, nx))
    end

    hfk = zeros(T, nx)
    fk = zeros(T, nx)
    Z = zeros(T, nx)

    ϵ = 0.5
    αfrac = 0.5

    poly_term = zeros(T, nx)
    β = zeros(Float64, nx)
    f_flt = zeros(Float64, nx)
    Vⱼ = zeros(T, nx)
    Uⱼ = zeros(T, nx)
    Z = zeros(T, nx)

    return ExistStorage{F,K,S,T}(flag, h, h, (h === 0.0), ϵ, αfrac, k, h, h, Inf, nx,
                                 f_coeff, fPU, ftilde, f_flt, hfk, fk, β, poly_term,
                                 Xj_0, Xj_apriori, Vⱼ, Uⱼ, Z, P, tf!)
end

"""
ContractorStorage{S}

Storage used to hold inputs to the contractor method used.
"""
mutable struct ContractorStorage{S}
    is_adaptive::Bool
    times::FixedCircularBuffer{Float64}
    steps::FixedCircularBuffer{Float64}
    Xj_0::Vector{S}
    Xj_apriori::Vector{S}
    xval::Vector{Float64}
    A_Q::FixedCircularBuffer{Matrix{Float64}}
    A_inv::FixedCircularBuffer{Matrix{Float64}}
    Δ::FixedCircularBuffer{Vector{S}}
    P::Vector{S}
    rP::Vector{S}
    pval::Vector{Float64}
    fk_apriori::Vector{S}
    hj_computed::Float64
    X_computed::Vector{S}
    xval_computed::Vector{Float64}
    B::Matrix{Float64}
    γ::Float64
    step_count::Int64
    nx::Int
end
function ContractorStorage(style::S, nx, np, k, h, method_steps) where S
    is_adaptive = h <= 0.0
    # add initial storage
    Xj_0 = zeros(S, nx)
    Xj_apriori = zeros(S, nx)
    xval = zeros(Float64, nx)
    xval_computed = zeros(Float64, nx)
    P = zeros(S, np)
    rP = zeros(S, np)
    pval = zeros(Float64, np)
    fk_apriori = zeros(S, nx)
    hj_computed = 0.0
    X_computed = zeros(S, nx)
    xval_computed = zeros(Float64, nx)
    B = zeros(Float64, nx, nx)
    γ = 0.0
    step_count = 1

    # add to buffer
    times = FixedCircularBuffer{Float64}(method_steps);  append!(times, zeros(nx))
    steps = FixedCircularBuffer{Float64}(method_steps);  append!(steps, zeros(nx))
    Δ = FixedCircularBuffer{Vector{S}}(method_steps)
    A_Q = FixedCircularBuffer{Matrix{Float64}}(method_steps)
    A_inv = FixedCircularBuffer{Matrix{Float64}}(method_steps)
    for i = 1:method_steps
        push!(Δ, zeros(S, nx))
        push!(A_Q, Float64.(Matrix(I, nx, nx)))
        push!(A_inv, Float64.(Matrix(I, nx, nx)))
    end

    return ContractorStorage{S}(is_adaptive, times, steps, Xj_0, Xj_apriori, xval,
                                A_Q, A_inv, Δ, P, rP, pval, fk_apriori,
                                hj_computed, X_computed, xval_computed, B, γ, step_count, nx)
end

function advance_contract_storage!(d::ContractorStorage{S}) where {S <: Number}
    cycle!(d.A_Q)
    cycle!(d.A_inv)
    cycle!(d.Δ)
    return nothing
end

function set_xX!(result::StepResult{S}, contract::ContractorStorage{S}) where {S <: Number}
    result.Xⱼ .= contract.X_computed
    result.xⱼ .= contract.xval_computed
    contract.Xj_0 .= contract.X_computed
    contract.xval .= contract.xval_computed
    return nothing
end

"""
excess_error

Computes the excess error using a norm-∞ of the diameter of the vectors.
"""
function excess_error(Z::Vector{S}, hj::Float64, hj_eu::Float64, γ::Float64, k::Int64, nx::Int64) where S
    errⱼ = 0.0; dₜ = 0.0
    for i = 1:nx
        dₜ = (hj/hj_eu)*diam(@inbounds Z[i])
        errⱼ = (dₜ > errⱼ) ? dₜ : errⱼ
    end
    γ*errⱼ
end

function affine_contract!(X::Vector{Interval{Float64}}, P::Vector{Interval{Float64}},
                          pval::Vector{Float64}, np::Int, nx::Int)
    return nothing
end

function affine_contract!(X::Vector{MC{N,T}}, P::Vector{MC{N,T}}, pval::Vector{Float64},
                          np::Int, nx::Int) where {N,T<:RelaxTag}
    x_Intv_cv = 0.0
    x_Intv_cc = 0.0
    for i = 1:nx
        Xt = @inbounds X[i]
        x_Intv_cv = Xt.cv
        x_Intv_cc = Xt.cc
        for j = 1:np
            p = @inbounds pval[j]
            pL = @inbounds P[j].Intv.lo
            pU = @inbounds P[j].Intv.hi
            cv_gradj = Xt.cv_grad[j]
            cc_gradj = Xt.cc_grad[j]
            x_Intv_cv += (cv_gradj > 0.0) ? cv_gradj*(pL - p) : cv_gradj*(pU - p)
            x_Intv_cc += (cc_gradj < 0.0) ? cc_gradj*(pL - p) : cc_gradj*(pU - p)
        end
        x_Intv_cv = max(x_Intv_cv, Xt.Intv.lo)
        x_Intv_cc = min(x_Intv_cc, Xt.Intv.hi)
        X[i] = MC{N,T}(Xt.cv, Xt.cc, Interval(x_Intv_cv, x_Intv_cc), Xt.cv_grad, Xt.cc_grad, Xt.cnst)
    end
    return nothing
end

contract_apriori!(exist::ExistStorage{F,K,S,T}, n::Nothing) where {F, K, S <: Real, T} = nothing
function contract_apriori!(exist::ExistStorage{F,K,S,T}, p::PolyhedralConstraint) where {F, K, S <: Real, T}
    nothing
end
function contract_apriori!(exist::ExistStorage{F,K,S,T}, c::ConstantStateBounds) where {F, K, S <: Real, T}
    for i = 1:exist.nx
        exist.Xj_apriori[i] = exist.Xj_apriori[i] ∩ Interval(c.xL[i], c.xU[i])
    end
    nothing
end

"""
single_step!

Performs a single-step of the validated integrator. Input stepsize is out.step.
"""
function single_step!(exist::ExistStorage{F,K,S,T}, contract::ContractorStorage{T},
                      params::StepParams, result::StepResult{T}, sc::M,
                      j::Int64, hj_limit::Float64, delT,
                      constant_state_bounds::C,
                      polyhedral_constraint::P) where {M <: AbstractStateContractor, F, K, S <: Real, T, C, P}

    contract.is_adaptive = params.is_adaptive

    # validate existence & uniqueness (returns if E&U cannot be shown)
    existence_uniqueness!(exist, params, result.time, j)
    if exist.status_flag === NUMERICAL_ERROR
        if delT > 1E-6
            return nothing
        else
            exist.status_flag = COMPLETED
        end
    end

    advance_contractor_buffer!(sc)::Nothing
    advance_contract_storage!(contract)

    # copy info from existence to contractor storage
    contract_apriori!(exist, constant_state_bounds)::Nothing
    contract_apriori!(exist, polyhedral_constraint)::Nothing
    contract.Xj_apriori .= exist.Xj_apriori
    contract.hj_computed = min(exist.computed_hj, hj_limit)
    contract.fk_apriori .= exist.fk

    hj = contract.hj_computed
    hj_eu = hj
    predicted_hj = 0.0

    # begin contractor step
    count = 0
    if !params.skip_step2
        if params.is_adaptive
            while hj > params.hmin && count < params.repeat_limit
                sc(contract, result, count)
                # LEPUS STEPSIZE PREDICTION
                errj = excess_error(exist.Z, hj, hj_eu, contract.γ, exist.k, exist.nx)
                if errj < hj*params.tol
                    contract.hj_computed = 0.9*hj*(0.5*hj*params.tol/errj)^(1/(exist.k-1))
                    break
                else
                    hj_reduced = hj*(hj*params.tol/errj)^(1/(exist.k-1))
                    exist.Z *= (hj_reduced/hj)^exist.k
                    hj = hj_reduced
                    contract.hj_computed = hj
                end
                count += 1
            end

            set_xX!(result, contract)::Nothing
        else
            # perform corrector step
            sc(contract, result, 0)
            set_xX!(result, contract)::Nothing
        end
    else
        result.xⱼ .= mid.(exist.Xapriori)
        result.Xⱼ .= exist.Xapriori
    end

    # update parallelepid enclosure
    cycle_copyto!(result.A_Q, contract.A_Q[1], j)
    cycle_copyto!(result.A_inv, contract.A_inv[1], j)
    cycle_copyto!(result.Δ, contract.Δ[1], j)

    # store times and step sizes to time/step buffer
    # and updated prediced step size
    result.time += hj
    result.predicted_hj = predicted_hj

    exist.Xj_0 .= result.Xⱼ
    exist.predicted_hj = contract.hj_computed

    return nothing
end

ext: jl


text:
# Used to allow custom dispatches to `_http_request`
abstract type AbstractBackend end

"""
    AWS.HTTPBackend <: AWS.AbstractBackend

An `HTTPBackend` can hold default `http_options::AbstractDict{Symbol,<:Any}`
to pass to HTTP.jl, which can be overwritten per-request by any `http_options`
supplied there.
"""
struct HTTPBackend <: AbstractBackend
    http_options::AbstractDict{Symbol,<:Any}
end

function HTTPBackend(; kwargs...)
    isempty(kwargs) ? HTTPBackend(LittleDict{Symbol, Any}()) : HTTPBackend(LittleDict(kwargs))
end

# populated in `__init__`
const DEFAULT_BACKEND = Ref{AbstractBackend}()

Base.@kwdef mutable struct Request
    service::String
    api_version::String
    request_method::String

    headers::AbstractDict{String, String}=LittleDict{String, String}()
    content::Union{String, Vector{UInt8}}=""
    resource::String=""
    url::String=""

    return_stream::Bool=false
    response_stream::Union{<:IO, Nothing}=nothing
    http_options::AbstractDict{Symbol,<:Any}=LittleDict{Symbol,String}()
    return_raw::Bool=false
    response_dict_type::Type{<:AbstractDict}=LittleDict
    backend::AbstractBackend=DEFAULT_BACKEND[]
end

"""
    submit_request(aws::AbstractAWSConfig, request::Request; return_headers::Bool=false)

Submit the request to AWS.

# Arguments
- `aws::AbstractAWSConfig`: AWSConfig containing credentials and other information for fulfilling the request, default value is the global configuration
- `request::Request`: All the information about making a request to AWS

# Keywords
- `return_headers::Bool=false`: True if you want the headers from the response returned back

# Returns
- `Tuple or Dict`: Tuple if returning_headers, otherwise just return a Dict of the response body
"""
function submit_request(aws::AbstractAWSConfig, request::Request; return_headers::Bool=false)
    response = nothing
    TOO_MANY_REQUESTS = 429
    EXPIRED_ERROR_CODES = ["ExpiredToken", "ExpiredTokenException", "RequestExpired"]
    REDIRECT_ERROR_CODES = [301, 302, 303, 304, 305, 307, 308]
    THROTTLING_ERROR_CODES = [
        "Throttling",
        "ThrottlingException",
        "ThrottledException",
        "RequestThrottledException",
        "TooManyRequestsException",
        "ProvisionedThroughputExceededException",
        "LimitExceededException",
        "RequestThrottled",
        "PriorRequestNotComplete"
    ]

    request.headers["User-Agent"] = user_agent[]
    request.headers["Host"] = HTTP.URI(request.url).host

    @repeat 3 try
        credentials(aws) === nothing || sign!(aws, request)

        response = @mock _http_request(request.backend, request)

        if response.status in REDIRECT_ERROR_CODES
            if HTTP.header(response, "Location") != ""
                request.url = HTTP.header(response, "Location")
            else
                e = HTTP.StatusError(response.status, response)
                throw(AWSException(e))
            end
        end
    catch e
        if e isa HTTP.StatusError
            e = AWSException(e)
        end

        @retry if :message in fieldnames(typeof(e)) && occursin("Signature expired", e.message) end

        # Handle ExpiredToken...
        # https://github.com/aws/aws-sdk-go/blob/v1.31.5/aws/request/retryer.go#L98
        @retry if ecode(e) in EXPIRED_ERROR_CODES
            check_credentials(credentials(aws), force_refresh=true)
        end

        # Throttle handling
        # https://github.com/boto/botocore/blob/1.16.17/botocore/data/_retry.json
        # https://docs.aws.amazon.com/general/latest/gr/api-retries.html
        @delay_retry if e isa AWSException &&
            (_http_status(e.cause) == TOO_MANY_REQUESTS || ecode(e) in THROTTLING_ERROR_CODES)
        end

        # Handle BadDigest error and CRC32 check sum failure
        @retry if e isa AWSException && (
            _header(e.cause, "crc32body") == "x-amz-crc32" ||
            ecode(e) in ("BadDigest", "RequestTimeout", "RequestTimeoutException")
        )
        end

        if e isa AWSException && occursin("Missing Authentication Token", e.message) && aws.credentials === nothing
            return throw(NoCredentials("You're attempting to perform a request without credentials set."))
        end
    end

    response_dict_type = request.response_dict_type

    # For HEAD request, return headers...
    if request.request_method == "HEAD"
        return response_dict_type(response.headers)
    end

    # Return response stream if requested...
    if request.return_stream
        return request.response_stream
    end

    # Return raw data if requested...
    if request.return_raw
        return (return_headers ? (response.body, response.headers) : response.body)
    end

    # Parse response data according to mimetype...
    mime = HTTP.header(response, "Content-Type", "")

    if isempty(mime)
        if length(response.body) > 5 && response.body[1:5] == b"<?xml"
            mime = "text/xml"
        end
    end

    body = String(copy(response.body))

    if occursin(r"/xml", mime)
        xml_dict_type = response_dict_type{Union{Symbol, String}, Any}
        body = parse_xml(body)
        root = XMLDict.root(body.x)

        return (return_headers ? (xml_dict(root, xml_dict_type), response_dict_type(response.headers)) : xml_dict(root, xml_dict_type))
    elseif occursin(r"/x-amz-json-1.[01]$", mime) || endswith(mime, "json")
        info = isempty(response.body) ? nothing : JSON.parse(body, dicttype=response_dict_type)
        return (return_headers ? (info, response_dict_type(response.headers)) : info)
    elseif startswith(mime, "text/")
        return (return_headers ? (body, response_dict_type(response.headers)) : body)
    else
        return (return_headers ? (response.body, response.headers) : response.body)
    end
end

function _http_request(http_backend::HTTPBackend, request::Request)
    http_options = merge(http_backend.http_options, request.http_options)

    @repeat 4 try
        http_stack = HTTP.stack(redirect=false, retry=false, aws_authorization=false)

        if request.return_stream && request.response_stream === nothing
            request.response_stream = Base.BufferStream()
        end

        return @mock HTTP.request(
            http_stack,
            request.request_method,
            HTTP.URI(request.url),
            HTTP.mkheaders(request.headers),
            request.content;
            require_ssl_verification=false,
            response_stream=request.response_stream,
            http_options...
        )
    catch e
        # Base.IOError is needed because HTTP.jl can often have errors that aren't
        # caught and wrapped in an HTTP.IOError
        # https://github.com/JuliaWeb/HTTP.jl/issues/382
        @delay_retry if isa(e, Sockets.DNSError) ||
                        isa(e, HTTP.ParseError) ||
                        isa(e, HTTP.IOError) ||
                        isa(e, Base.IOError) ||
                        (isa(e, HTTP.StatusError) && _http_status(e) >= 500)
        end
    end
end

_http_status(e::HTTP.StatusError) = e.status
_header(e::HTTP.StatusError, k, d="") = HTTP.header(e.response, k, d)


text:
__precompile__()

module DiffEqSensitivity

using DiffEqBase, ForwardDiff, Tracker, DiffEqDiffTools, Statistics
using DiffEqCallbacks, QuadGK, RecursiveArrayTools, LinearAlgebra
using DataFrames, GLM, RecursiveArrayTools
using Parameters: @unpack

abstract type SensitivityFunction end
abstract type GSAMethod end

include("derivative_wrappers.jl")
include("local_sensitivity.jl")
include("adjoint_sensitivity.jl")
include("morris_sensitivity.jl")
include("sobol_sensitivity.jl")
include("regression_sensitivity.jl")
include("DGSM.jl")

export extract_local_sensitivities

export ODELocalSensitivityFunction, ODELocalSensitivityProblem, SensitivityFunction,
       ODEAdjointSensitivityProblem, ODEAdjointProblem, AdjointSensitivityIntegrand,
       adjoint_sensitivities, adjoint_sensitivities_u0, Sobol, Morris, gsa,
       SensitivityAlg, regression_sensitivity, DGSM

end # module


text:
import ThermofluidQuantities: Unitful, 𝐋, 𝐌, 𝐓

@testset "Define unit types" begin

  @displayedunits MyVelocityType "m/s" 𝐋/𝐓
  @test MyVelocityType == Union{Unitful.Quantity{T,𝐋*𝐓^-1,U}, Unitful.Level{L,S,Unitful.Quantity{T,𝐋*𝐓^-1,U}} where S where L} where U where T

  @dimvar MyVelocityVar MyVelocityType

  @test value(MyVelocityVar(5)) == 5.0u"m/s"

  v = MyVelocityVar(10) + Velocity(20)
  @test value(v) == 30.0u"m/s"

  @nondimvar MyNondimVar

  @test value(MyNondimVar(10)) == 10.0

  @test_throws Unitful.DimensionError MyVelocityVar(10) + MyNondimVar(5)

end

@testset "Operations" begin

  p1 = Pressure(5u"Pa")
  p2 = Pressure(20)

  @test p1 + p2 == 25u"Pa"
  @test value(Pressure(p1+p2)) == 25u"Pa"
  @test ustrip(Pressure(p1+p2)) == 25.0

  ṁ = MassFlowRate(5)
  @test value(ṁ) == 5u"kg/s"
  @test 2*ṁ == 10u"kg/s"
  @test ṁ*2 == 10u"kg/s"
  @test ṁ/2 == 2.5u"kg/s"
  @test 2/ṁ == 0.4u"s/kg"
  @test ṁ^3 == 125u"kg^3/s^3"

  @test TemperatureRatio(4.0)^(1//2) == 2.0

  @test Length(50) > Length(40) > Diameter(5)
  @test_throws Unitful.DimensionError Length(40) > Area(30)

  @test sinh(ReynoldsNumber(5)) == sinh(5)
  @test log(ReynoldsNumber(5)) == log(Velocity(5)*Length(4)/KinematicViscosity(4))

  @test_throws MethodError sin(Velocity(5))

end

max_stars_repo_name: UCLAMAEThreads/ThermofluidQuantities

max_issues_repo_name: UCLAMAEThreads/ThermofluidQuantities

max_forks_repo_name: UCLAMAEThreads/ThermofluidQuantities


text:
function ic!(model, filename; ArrayType = Array)
    file = jldopen(filename)
    b = ArrayType(file["tracers"]["b"]["data"])
    u = ArrayType(file["velocities"]["u"]["data"])
    v = ArrayType(file["velocities"]["v"]["data"])
    w = ArrayType(file["velocities"]["w"]["data"])
    model.tracers.b.data[:] .= view(b, : )
    model.velocities.u.data[:] .= view(u, : )
    model.velocities.v.data[:] .= view(v, : )
    model.velocities.w.data[:] .= view(w, : )
    close(file)
    return nothing
end

function archarray(arch::CPU)
    return Array
end
function archarray(arch::GPU)
    return CuArray
end

function getlatest(filename)
    files = readdir()
    bools = [occursin(filename*"_checkpoint", file)*occursin("iteration", file) for file in files]
    ffiles = files[bools]
    bools2 = [filename == ffile[1:length(filename)] for ffile in ffiles]
    ffiles2 = ffiles[bools2]
    iterations = [parse(Int, split(ffile, "iteration")[2][1:end-length(".jld2")]) for ffile in ffiles2]
    p = sortperm(iterations)
    return latest_file = ffiles2[p[end]]
end

max_stars_repo_name: sandreza/Mesoscale

max_issues_repo_name: sandreza/Mesoscale

max_forks_repo_name: sandreza/Mesoscale


text:
function isnorm_fac_elem(K::NfRel{nf_elem}, a::nf_elem)
  Ka, mKa, mkK = absolute_field(K)

  ZKa = lll(maximal_order(Ka))
  C, mC = class_group(ZKa)

  S = collect(keys(factor(mkK(a)*ZKa)))

  c = _get_ClassGrpCtx_of_order(ZKa)
  FB = c.FB.ideals
  i = length(FB)
  q, mq = quo(C, [preimage(mC, I) for I = S], false)
  while length(q) > 1
    while FB[i] in S || iszero(mq(preimage(mC, FB[i])))
      i -= 1
    end
    push!(S, FB[i])
    q, mmq = quo(q, [mq(preimage(mC, FB[i]))], false)
    mq = mq*mmq
  end

  s = Set([minimum(mkK, I) for I = S])
  #make S relative Galois closed:
  PS = IdealSet(ZKa)
  S = vcat([collect(keys(factor(PS(mkK, p)))) for p = s]...)

  if length(S) == 0
    U, mU = unit_group_fac_elem(ZKa)
  else
    U, mU = sunit_group_fac_elem(collect(S))
  end
  class_group(parent(a))
  if length(s) == 0
    u, mu = unit_group_fac_elem(maximal_order(parent(a)))
  else
    u, mu = sunit_group_fac_elem(collect(s))
  end
  No = hom(U, u, elem_type(u)[preimage(mu, norm(mkK, mU(g))) for g = gens(U)])
  aa = preimage(mu, FacElem(a))
  fl, so = haspreimage(No, aa)
  fl || return fl, FacElem(Dict(K(1)=>1))
  return true, FacElem(K, Dict([image(mKa, k) => v for (k,v) = mU(so)]))
end

function isnorm(K::NfRel{nf_elem}, a::nf_elem)
  fl, s = isnorm_fac_elem(K, a)
  return fl, evaluate(s)
end

function norm_equation(K::NfRel{nf_elem}, a::nf_elem)
  fl, s = isnorm(K, a)
  fl || error("no solution")
  return s
end


text:
using Documenter, FractalTools

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

makedocs(;
    modules=[FractalTools],
    format=Documenter.HTML(),
    pages=[
        "API" => "index.md",
        # "Notes" => [
        #     "Interpolation" => [
        #         "1D Fractal Interpolation" => "manual/interpolation/one_dimensional_interpolation.md",
        #         "2D Fractal Interpolation" => "manual/interpolation/two_dimensional_interpolation.md",
        #         "1D Hidden Fractal Interpolation" => "manual/interpolation/one_dimensional_hidden_interpolation.md",
        #         "2D Hidden Fractal Interpolation" => "manual/interpolation/two_dimensional_hidden_interpolation.md",
        #         ],
        #     "Integration" => [
        #         "1D Fractal Integration" => "manual/integration/one_dimensional_integration.md",
        #         "2D Fractal Integration" => "manual/integration/two_dimensional_integration.md",
        #         "1D Hidden Fractal Integration" => "manual/integration/one_dimensional_hidden_integration.md",
        #         "2D Hidden Fractal Integration" => "manual/integration/two_dimensional_hidden_integration.md",
        #         ],
        # ],
    ],
    repo="https://github.com/zekeriyasari/FractalTools.jl/blob/{commit}{path}#L{line}",
    sitename="FractalTools.jl",
    authors="Zekeriya Sarı, Gizem Kalender"
)

deploydocs(;
    repo="github.com/zekeriyasari/FractalTools.jl",
)


text:
# No consts, no duplication
export while2,
       fdupl
function f_easy(x, y, z)
  a = x + y
  b = a * z
end

f_easy2(x, y, z) = x * y + z

function f_easy3(x, y, z)
  a = x * y + z
end

function f_easy4(x, y, z)
  x * y + z
end

# Single Block
function g(x)
  2 * x
end

function f(a, b, c)
  x = a + b + 3
  y = x * c
  g(y)
end

# Single Block With Duplication

function f4(a)
  x = a + a
end

function fdupl(a, b)
  z = a / b
  a + z
end

# Multiple Blocks
function f2(a, b, c)
  x = a + b + 3
  y = a * c
  if x > 3
    g(x + x/y)
  else
    g(y + x)
  end
end

## Recursion

function while1(a, b)
  i = 0
  for i = 1:5
    a = a + b
  end
  a
end

function while2(a, n)
  i = 0
  while i < n
    i += 1
    a = a * n
  end
  a
end


text:
using Test, Statistics, Random, LinearAlgebra, SparseArrays, Plots
using DiffEqBiological, DiffEqBase
using OrdinaryDiffEq, StochasticDiffEq, DiffEqJump, SteadyStateDiffEq

# fetches a set of networks which can be used for tests.
include("test_networks.jl")

# full macro tests
@time begin
  @time @testset "Model Macro" begin include("make_model_test.jl") end
  @time @testset "Gillespie Tests" begin include("gillespie.jl") end
  @time @testset "Test Solvers" begin include("solver_test.jl") end
  @time @testset "Higher Order" begin include("higher_order_reactions.jl") end
  @time @testset "Additional Functions" begin include("func_test.jl") end
  @time @testset "Steady State Solver" begin include("steady_state.jl") end
  @time @testset "Mass Action Jumps" begin include("mass_act_jump_tests.jl") end
  @time @testset "Equilibrate (1)" begin include("equilibrate_test_1.jl") end
  @time @testset "Equilibrate (2)" begin include("equilibrate_test_2.jl") end
  @time @testset "Equilibrate (3)" begin include("equilibrate_test_3.jl") end
  @time @testset "Equilibrate (4)" begin include("equilibrate_test_4.jl") end
  @time @testset "Other Tests" begin include("misc_tests.jl") end
  @time @testset "Network query tests" begin include("networkquery_test.jl") end
end

# min macro tests
@time begin
  @time @testset "Model Macro (Min)" begin include("make_model_test_min.jl") end
  @time @testset "Gillespie Tests (Min)" begin include("gillespie_min.jl") end
  @time @testset "Test Solvers (Min)" begin include("solver_test_min.jl") end
  @time @testset "Higher Order (Min)" begin include("higher_order_reactions_min.jl") end
  @time @testset "Additional Functions (Min)" begin include("func_test_min.jl") end
  @time @testset "Steady State Solver (Min)" begin include("steady_state_min.jl") end
  @time @testset "Equilibrate (Min)" begin include("equilibrate_test_min.jl") end
  @time @testset "Mass Action Jumps (Min)" begin include("mass_act_jump_tests_min.jl") end
end

# tests that handle both macros
@time begin
  @time @testset "Discrete Problem" begin include("discreteproblem_test.jl") end
  @time @testset "Add Reactions API" begin include("addreactions_test.jl") end
end

# Test integration with latexify.
@time begin
    @time @testset "Latexify recipe" begin include("latexify_test.jl") end
end