text:
"""
    test_quadratic_integration(model::MOI.ModelLike, config::Config{T}) where {T}

Test the problem:
```
Min x^2 + xy + y^2 + yz + z^2
st  x + 2y + 3z >= 4 (c1)
    x +  y      >= 1 (c2)
    x, y, z in R
```
"""
function test_quadratic_integration(
    model::MOI.ModelLike,
    config::Config{T},
) where {T}
    @requires MOI.supports_incremental_interface(model)
    @requires MOI.supports(
        model,
        MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}(),
    )
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarAffineFunction{T},
        MOI.GreaterThan{T},
    )
    v = MOI.add_variables(model, 3)
    @test MOI.get(model, MOI.NumberOfVariables()) == 3
    cf1 = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(T[1, 2, 3], v), T(0))
    c1 = MOI.add_constraint(model, cf1, MOI.GreaterThan(T(4)))
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{
                MOI.ScalarAffineFunction{T},
                MOI.GreaterThan{T},
            }(),
        ) == 1
    end
    c2 = MOI.add_constraint(
        model,
        MOI.ScalarAffineFunction(
            MOI.ScalarAffineTerm.(T(1), [v[1], v[2]]),
            T(0),
        ),
        MOI.GreaterThan(T(1)),
    )
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{
                MOI.ScalarAffineFunction{T},
                MOI.GreaterThan{T},
            }(),
        ) == 2
    end
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
    @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MIN_SENSE
    obj = MOI.ScalarQuadraticFunction(
        MOI.ScalarQuadraticTerm.(
            T[2, 1, 2, 1, 2],
            v[[1, 1, 2, 2, 3]],
            v[[1, 2, 2, 3, 3]],
        ),
        MOI.ScalarAffineTerm{T}[],
        T(0),
    )
    MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}(), obj)
    if _supports(config, MOI.ObjectiveFunction)
        @test obj ≈ MOI.get(
            model,
            MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}(),
        )
        @test cf1 ≈ MOI.get(model, MOI.ConstraintFunction(), c1)
        @test MOI.GreaterThan(T(4)) == MOI.get(model, MOI.ConstraintSet(), c1)
    end
    if _supports(config, MOI.optimize!)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
        @test ≈(MOI.get(model, MOI.ObjectiveValue()), T(13 // 7), config)
        @test ≈(
            MOI.get(model, MOI.VariablePrimal(), v),
            T[4//7, 3//7, 6//7],
            config,
        )
        if _supports(config, MOI.ConstraintDual)
            @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT
            # The dual constraint gives
            # [λ_c1 + λ_c2, 2λ_c1 + λ_c2, 3λ_c1] = [2 1 0; 1 2 1; 0 1 2] * x
            #                                    = [11, 16, 15] // 7
            # hence λ_c1 = 5/7 and λ_c2 = 6/7.
            @test ≈(MOI.get(model, MOI.ConstraintDual(), c1), T(5 // 7), config)
            @test ≈(MOI.get(model, MOI.ConstraintDual(), c2), T(6 // 7), config)
        end
    end
    return
end

function setup_test(
    ::typeof(test_quadratic_integration),
    model::MOIU.MockOptimizer,
    ::Config{T},
) where {T}
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            T[4//7, 3//7, 6//7],
            (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}) =>
                T[5//7, 6//7],
        ),
    )
    return
end

"""
    test_quadratic_duplicate_terms(model::MOI.ModelLike, config::Config{T}) where {T}

Test the problem:
```
Min x^2 + xy + y^2 + yz + z^2
st  x + 2y + 3z >= 4 (c1)
    x +  y      >= 1 (c2)
    x, y, z in R
```
"""
function test_quadratic_duplicate_terms(
    model::MOI.ModelLike,
    config::Config{T},
) where {T}
    @requires MOI.supports_incremental_interface(model)
    @requires MOI.supports(
        model,
        MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}(),
    )
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarAffineFunction{T},
        MOI.GreaterThan{T},
    )
    v = MOI.add_variables(model, 3)
    @test MOI.get(model, MOI.NumberOfVariables()) == 3
    c1f = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(T[1, 2, 3], v), T(0))
    c1 = MOI.add_constraint(model, c1f, MOI.GreaterThan(T(4)))
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{
                MOI.ScalarAffineFunction{T},
                MOI.GreaterThan{T},
            }(),
        ) == 1
    end
    c2 = MOI.add_constraint(
        model,
        MOI.ScalarAffineFunction(
            MOI.ScalarAffineTerm.(T(1), [v[1], v[2]]),
            T(0),
        ),
        MOI.GreaterThan(T(1)),
    )
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{
                MOI.ScalarAffineFunction{T},
                MOI.GreaterThan{T},
            }(),
        ) == 2
    end
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
    @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MIN_SENSE
    obj = MOI.ScalarQuadraticFunction(
        MOI.ScalarQuadraticTerm.(
            T[2, 1//2, 1//2, 2, 1, 1, 1],
            [v[1], v[1], v[1], v[2], v[2], v[3], v[3]],
            [v[1], v[2], v[2], v[2], v[3], v[3], v[3]],
        ),
        MOI.ScalarAffineTerm.(T(0), v),
        T(0),
    )
    MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}(), obj)
    if _supports(config, MOI.ObjectiveFunction)
        @test obj ≈ MOI.get(
            model,
            MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}(),
        )
        @test c1f ≈ MOI.get(model, MOI.ConstraintFunction(), c1)
        @test MOI.GreaterThan(T(4)) == MOI.get(model, MOI.ConstraintSet(), c1)
    end
    if _supports(config, MOI.optimize!)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
        @test ≈(MOI.get(model, MOI.ObjectiveValue()), T(13 // 7), config)
        @test ≈(
            MOI.get(model, MOI.VariablePrimal(), v),
            T[4//7, 3//7, 6//7],
            config,
        )
        if _supports(config, MOI.ConstraintDual)
            @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT
            @test ≈(MOI.get(model, MOI.ConstraintDual(), c1), T(5 // 7), config)
            @test ≈(MOI.get(model, MOI.ConstraintDual(), c2), T(6 // 7), config)
        end
    end
    # change objective to Max -2(x^2 + xy + y^2 + yz + z^2)
    # First clear the objective, this is needed if a `Bridges.Objective.SlackBridge` is used.
    MOI.set(model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE)
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
    @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE
    obj2 = MOI.ScalarQuadraticFunction(
        MOI.ScalarQuadraticTerm.(
            T[-4, -1, -1, -4, -2, -2, -2],
            [v[1], v[1], v[1], v[2], v[2], v[3], v[3]],
            [v[1], v[2], v[2], v[2], v[3], v[3], v[3]],
        ),
        MOI.ScalarAffineTerm.(T(0), v),
        T(0),
    )
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}(),
        obj2,
    )
    if _supports(config, MOI.ObjectiveFunction)
        @test obj2 ≈ MOI.get(
            model,
            MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}(),
        )
    end
    if _supports(config, MOI.optimize!)
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
        @test ≈(MOI.get(model, MOI.ObjectiveValue()), T(-2 * 13 // 7), config)
        @test ≈(
            MOI.get(model, MOI.VariablePrimal(), v),
            T[4//7, 3//7, 6//7],
            config,
        )
        if _supports(config, MOI.ConstraintDual)
            @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT
            @test ≈(
                MOI.get(model, MOI.ConstraintDual(), c1),
                T(10 // 7),
                config,
            )
            @test ≈(
                MOI.get(model, MOI.ConstraintDual(), c2),
                T(12 // 7),
                config,
            )
        end
    end
    return
end

function setup_test(
    ::typeof(test_quadratic_duplicate_terms),
    model::MOIU.MockOptimizer,
    ::Config{T},
) where {T}
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            T[4//7, 3//7, 6//7],
            (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}) =>
                T[5//7, 6//7],
        ),
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            T[4//7, 3//7, 6//7],
            (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}) =>
                T[10//7, 12//7],
        ),
    )
    return
end

"""
    test_quadratic_nonhomogeneous(
        model::MOI.ModelLike,
        config::Config{T},
    ) where {T}

Test the problem:
```
minimize 2 x^2 + y^2 + xy + x + y + 1
    s.t.  x, y >= 0
          x + y = 1
```
"""
function test_quadratic_nonhomogeneous(
    model::MOI.ModelLike,
    config::Config{T},
) where {T}
    @requires MOI.supports_incremental_interface(model)
    @requires MOI.supports(
        model,
        MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}(),
    )
    @requires MOI.supports_constraint(
        model,
        MOI.VariableIndex,
        MOI.GreaterThan{T},
    )
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarAffineFunction{T},
        MOI.EqualTo{T},
    )
    x = MOI.add_variable(model)
    y = MOI.add_variable(model)
    c1 = MOI.add_constraint(
        model,
        MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(T(1), [x, y]), T(0)),
        MOI.EqualTo(T(1)),
    )
    vc1 = MOI.add_constraint(model, x, MOI.GreaterThan(T(0)))
    # We test this after the creation of every `VariableIndex` constraint
    # to ensure a good coverage of corner cases.
    @test vc1.value == x.value
    vc2 = MOI.add_constraint(model, y, MOI.GreaterThan(T(0)))
    @test vc2.value == y.value
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
    obj = MOI.ScalarQuadraticFunction(
        MOI.ScalarQuadraticTerm.(T[4, 2, 1], [x, y, x], [x, y, y]),
        MOI.ScalarAffineTerm.(T(1), [x, y]),
        T(1),
    )
    MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}(), obj)
    if _supports(config, MOI.optimize!)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
        @test ≈(MOI.get(model, MOI.ObjectiveValue()), T(23 // 8), config)
        @test ≈(
            MOI.get(model, MOI.VariablePrimal(), [x, y]),
            T[1//4, 3//4],
            config,
        )
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), vc1), T(1 // 4), config)
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), vc2), T(3 // 4), config)
        if _supports(config, MOI.ConstraintDual)
            @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT
            # The dual constraint gives
            # [λ_vc1 + λ_c1, λ_vc2 + λ_c1] = [4 1; 1 2] * x + [1, 1] = [11, 11] / 4
            # since `vc1` and `vc2` are not active, `λ_vc1` and `λ_vc2` are
            # zero so `λ_c1 = 11/4`.
            @test ≈(
                MOI.get(model, MOI.ConstraintDual(), c1),
                T(11 // 4),
                config,
            )
            @test ≈(MOI.get(model, MOI.ConstraintDual(), vc1), T(0), config)
            @test ≈(MOI.get(model, MOI.ConstraintDual(), vc2), T(0), config)
        end
    end
    # change back to linear
    #        max 2x + y + 1
    #       s.t.  x, y >= 0
    #             x + y = 1
    # (x,y) = (1,0), obj = 3
    # First clear the objective, this is needed if a
    # `Bridges.Objective.SlackBridge` is used.
    MOI.set(model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE)
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
    objf =
        MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(T[2, 1], [x, y]), T(1))
    MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(), objf)
    if _supports(config, MOI.optimize!)
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
        @test ≈(MOI.get(model, MOI.ObjectiveValue()), T(3), config)
        @test ≈(MOI.get(model, MOI.VariablePrimal(), [x, y]), T[1, 0], config)
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), vc1), T(1), config)
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), vc2), T(0), config)
        if _supports(config, MOI.ConstraintDual)
            @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT
            @test ≈(MOI.get(model, MOI.ConstraintDual(), c1), T(-2), config)
            @test ≈(MOI.get(model, MOI.ConstraintDual(), vc1), T(0), config)
            @test ≈(MOI.get(model, MOI.ConstraintDual(), vc2), T(1), config)
        end
    end
    return
end

function setup_test(
    ::typeof(test_quadratic_nonhomogeneous),
    model::MOIU.MockOptimizer,
    ::Config{T},
) where {T}
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            T[1//4, 3//4],
            (MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}) => T[11//4],
        ),
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            T[1, 0],
            (MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}) => T[-2],
        ),
    )
    return
end

"""
    test_quadratic_constraint_integration(
        model::MOI.ModelLike,
        config::Config{T},
    ) where {T}

Test the problem:
```
Max x  + y
st -x  + y >= 0 (c1[1])
    x  + y >= 0 (c1[2])
    x² + y <= 2 (c2)
Optimal solution
x = 1/2, y = 7/4
```
"""
function test_quadratic_constraint_integration(
    model::MOI.ModelLike,
    config::Config{T},
) where {T}
    @requires MOI.supports_incremental_interface(model)
    @requires MOI.supports(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
    )
    @requires MOI.supports_constraint(
        model,
        MOI.VectorAffineFunction{T},
        MOI.Nonnegatives,
    )
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarQuadraticFunction{T},
        MOI.LessThan{T},
    )
    x = MOI.add_variable(model)
    y = MOI.add_variable(model)
    @test MOI.get(model, MOI.NumberOfVariables()) == 2
    c1 = MOI.add_constraint(
        model,
        MOI.VectorAffineFunction(
            MOI.VectorAffineTerm.(
                [1, 1, 2, 2],
                MOI.ScalarAffineTerm.(T[-1, 1, 1, 1], [x, y, x, y]),
            ),
            T[0, 0],
        ),
        MOI.Nonnegatives(2),
    )
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{
                MOI.VectorAffineFunction{T},
                MOI.Nonnegatives,
            }(),
        ) == 1
    end
    c2f = MOI.ScalarQuadraticFunction(
        [MOI.ScalarQuadraticTerm(T(2), x, x)],
        [MOI.ScalarAffineTerm(T(1), y)],
        T(0),
    )
    c2 = MOI.add_constraint(model, c2f, MOI.LessThan(T(2)))
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{
                MOI.ScalarQuadraticFunction{T},
                MOI.LessThan{T},
            }(),
        ) == 1
    end
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
        MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(T(1), [x, y]), T(0)),
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
    @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE
    if _supports(config, MOI.ConstraintFunction)
        @test ≈(MOI.get(model, MOI.ConstraintFunction(), c2), c2f, config)
    end
    if _supports(config, MOI.optimize!)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
        @test ≈(MOI.get(model, MOI.ObjectiveValue()), T(9 // 4), config)
        @test ≈(
            MOI.get(model, MOI.VariablePrimal(), [x, y]),
            T[1//2, 7//4],
            config,
        )
        @test ≈(
            MOI.get(model, MOI.ConstraintPrimal(), c1),
            T[5//4, 9//4],
            config,
        )
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), c2), 2, config)
        if _supports(config, MOI.ConstraintDual)
            @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT
            # The dual constraint gives
            # [-1, -1] = [-λ_c1[1] + λ_c1[2], λ_c1[1] + λ_c1[2]] + λ_c2 * ([2 0; 0 0] * x + [0, 1])
            #          = [-λ_c1[1] + λ_c1[2] + λ_c2, λ_c1[1] + λ_c1[2] + λ_c2]
            # By complementary slackness, we have `λ_c1 = [0, 0]` so
            # `λ_c2 = -1`.
            @test ≈(MOI.get(model, MOI.ConstraintDual(), c1), T[0, 0], config)
            @test ≈(MOI.get(model, MOI.ConstraintDual(), c2), T(-1), config)
        end
    end
    # try delete quadratic constraint and go back to linear
    # FIXME why is this commented ?
    # MOI.delete(model, c2)
    #
    # MOI.optimize!(model)
    #
    # @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
    #
    # @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
    #
    # @test MOI.get(model, MOI.ObjectiveValue()), 0.0 atol=atol rtol=rtol
    return
end

function setup_test(
    ::typeof(test_quadratic_constraint_integration),
    model::MOIU.MockOptimizer,
    ::Config{T},
) where {T}
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            T[1//2, 7//4],
            (MOI.VectorAffineFunction{T}, MOI.Nonnegatives) => [T[0, 0]],
            (MOI.ScalarQuadraticFunction{T}, MOI.LessThan{T}) => T[-1],
        ),
    )
    return
end

"""
    test_quadratic_constraint_basic(
        model::MOI.ModelLike,
        config::Config{T},
    ) where {T}

Test the problem:
```
 Max x
s.t. x^2 <= 2 (c)
```
"""
function test_quadratic_constraint_basic(
    model::MOI.ModelLike,
    config::Config{T},
) where {T}
    @requires MOI.supports_incremental_interface(model)
    @requires MOI.supports(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
    )
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarQuadraticFunction{T},
        MOI.LessThan{T},
    )
    x = MOI.add_variable(model)
    @test MOI.get(model, MOI.NumberOfVariables()) == 1
    cf = MOI.ScalarQuadraticFunction(
        [MOI.ScalarQuadraticTerm(T(2), x, x)],
        [MOI.ScalarAffineTerm(T(0), x)],
        T(0),
    )
    c = MOI.add_constraint(model, cf, MOI.LessThan(T(2)))
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{
                MOI.ScalarQuadraticFunction{T},
                MOI.LessThan{T},
            }(),
        ) == 1
    end
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(T(1), x)], T(0)),
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
    @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE
    if _supports(config, MOI.ConstraintFunction)
        @test ≈(MOI.get(model, MOI.ConstraintFunction(), c), cf, config)
    end
    if _supports(config, MOI.optimize!)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
        @test ≈(MOI.get(model, MOI.ObjectiveValue()), sqrt(T(2)), config)
        @test ≈(MOI.get(model, MOI.VariablePrimal(), x), sqrt(T(2)), config)
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), c), 2, config)
        if _supports(config, MOI.ConstraintDual)
            @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT
            # The dual constraint gives
            # -1 = λ * (2 * x) = λ * 2sqrt(T(2))
            # hence λ = -1/(2sqrt(T(2))).
            @test ≈(
                MOI.get(model, MOI.ConstraintDual(), c),
                T(-1) / (2 * sqrt(T(2))),
                config,
            )
        end
    end
    return
end

function setup_test(
    ::typeof(test_quadratic_constraint_basic),
    model::MOIU.MockOptimizer,
    ::Config{T},
) where {T}
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            [sqrt(T(2))],
            (MOI.ScalarQuadraticFunction{T}, MOI.LessThan{T}) =>
                [T(-1) / (2 * sqrt(T(2)))],
        ),
    )
    return
end

"""
    test_quadratic_constraint_minimize(
        model::MOI.ModelLike,
        config::Config,
    )

Test the problem:
```
 Min -x
s.t.  x^2 <= 2
```
"""
function test_quadratic_constraint_minimize(
    model::MOI.ModelLike,
    config::Config{T},
) where {T}
    @requires MOI.supports_incremental_interface(model)
    @requires MOI.supports(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
    )
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarQuadraticFunction{T},
        MOI.LessThan{T},
    )
    x = MOI.add_variable(model)
    @test MOI.get(model, MOI.NumberOfVariables()) == 1
    cf = MOI.ScalarQuadraticFunction(
        [MOI.ScalarQuadraticTerm(T(2), x, x)],
        MOI.ScalarAffineTerm{T}[],
        T(0),
    )
    c = MOI.add_constraint(model, cf, MOI.LessThan(T(2)))
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{
                MOI.ScalarQuadraticFunction{T},
                MOI.LessThan{T},
            }(),
        ) == 1
    end
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(T(-1), x)], T(0)),
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
    @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MIN_SENSE
    if _supports(config, MOI.ConstraintFunction)
        @test ≈(MOI.get(model, MOI.ConstraintFunction(), c), cf, config)
    end
    if _supports(config, MOI.optimize!)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
        if _supports(config, MOI.ConstraintDual)
            @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT
        end
        @test ≈(MOI.get(model, MOI.ObjectiveValue()), -sqrt(T(2)), config)
        @test ≈(MOI.get(model, MOI.VariablePrimal(), x), sqrt(T(2)), config)
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), c), 2, config)
        if _supports(config, MOI.ConstraintDual)
            @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT
            # The dual constraint gives
            # -1 = λ * (2 * x) = λ * 2sqrt(T(2))
            # hence λ = -1/(2sqrt(T(2))).
            @test ≈(
                MOI.get(model, MOI.ConstraintDual(), c),
                T(-1) / (2 * sqrt(T(2))),
                config,
            )
        end
    end
    return
end

function setup_test(
    ::typeof(test_quadratic_constraint_minimize),
    model::MOIU.MockOptimizer,
    ::Config{T},
) where {T}
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            [sqrt(T(2))],
            (MOI.ScalarQuadraticFunction{T}, MOI.LessThan{T}) =>
                [T(-1) / (2 * sqrt(T(2)))],
        ),
    )
    return
end

"""
Max  x
s.t. x^2 + x * y + y^2 <= 3 (c)
     y == 1
"""
function _test_quadratic_constraint_helper(
    model::MOI.ModelLike,
    config::Config{T},
    less_than::Bool,
) where {T}
    @requires MOI.supports_incremental_interface(model)
    @requires MOI.supports(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
    )
    quad_set = less_than ? MOI.LessThan(T(3)) : MOI.GreaterThan(T(-3))
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarQuadraticFunction{T},
        typeof(quad_set),
    )
    x = MOI.add_variable(model)
    y = MOI.add_variable(model)
    @test MOI.get(model, MOI.NumberOfVariables()) == 2
    vc = MOI.add_constraint(model, y, MOI.EqualTo(T(1)))
    @test vc.value == y.value
    cf = MOI.ScalarQuadraticFunction(
        MOI.ScalarQuadraticTerm.(T[2, 1, 2], [x, x, y], [x, y, y]),
        [MOI.ScalarAffineTerm(T(0), x)],
        T(0),
    )
    if !less_than
        MOIU.operate!(-, T, cf)
    end
    c = MOI.add_constraint(model, cf, quad_set)
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{
                MOI.ScalarQuadraticFunction{T},
                typeof(quad_set),
            }(),
        ) == 1
    end
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(T(1), x)], T(0)),
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
    @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE
    if _supports(config, MOI.ConstraintFunction)
        @test ≈(MOI.get(model, MOI.ConstraintFunction(), c), cf, config)
        @test quad_set == MOI.get(model, MOI.ConstraintSet(), c)
    end
    if _supports(config, MOI.optimize!)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
        @test ≈(MOI.get(model, MOI.ObjectiveValue()), T(1), config)
        @test ≈(MOI.get(model, MOI.VariablePrimal(), x), T(1), config)
        @test ≈(MOI.get(model, MOI.VariablePrimal(), y), T(1), config)
        @test ≈(
            MOI.get(model, MOI.ConstraintPrimal(), c),
            MOI.constant(quad_set),
            config,
        )
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), vc), T(1), config)
        if _supports(config, MOI.ConstraintDual)
            @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT
            # The dual constraint gives
            # [-1, 0] = λ_c * [2 1; 1 2] * x + [0, λ_vc]
            #         = [3λ_c, 3λ_c + λ_vc]
            # hence `λ_c = -1/3` and `λ_vc = 1`.
            λ_c = less_than ? T(-1 // 3) : T(1 // 3)
            @test ≈(MOI.get(model, MOI.ConstraintDual(), c), λ_c, config)
            @test ≈(MOI.get(model, MOI.ConstraintDual(), vc), 1, config)
        end
    end
    return
end

function test_quadratic_constraint_LessThan(
    model::MOI.ModelLike,
    config::Config{T},
) where {T}
    _test_quadratic_constraint_helper(model, config, true)
    return
end

function setup_test(
    ::typeof(test_quadratic_constraint_LessThan),
    model::MOIU.MockOptimizer,
    ::Config{T},
) where {T}
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            T[1, 1],
            (MOI.ScalarQuadraticFunction{T}, MOI.LessThan{T}) => T[-1//3],
        ),
    )
    return
end

function test_quadratic_constraint_GreaterThan(
    model::MOI.ModelLike,
    config::Config{T},
) where {T}
    _test_quadratic_constraint_helper(model, config, false)
    return
end

function setup_test(
    ::typeof(test_quadratic_constraint_GreaterThan),
    model::MOIU.MockOptimizer,
    ::Config{T},
) where {T}
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            T[1, 1],
            (MOI.ScalarQuadraticFunction{T}, MOI.GreaterThan{T}) => T[1//3],
        ),
    )
    return
end

"""
    test_quadratic_nonconvex_constraint_integration(
        model::MOI.ModelLike,
        config::Config,
    )

Test the problem:
```
Max 2x + y
s.t. x * y <= 4 (c)
     x, y >= 1
```
"""
function test_quadratic_nonconvex_constraint_integration(
    model::MOI.ModelLike,
    config::Config{T},
) where {T}
    @requires MOI.supports_incremental_interface(model)
    @requires MOI.supports(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
    )
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarQuadraticFunction{T},
        MOI.LessThan{T},
    )
    x = MOI.add_variable(model)
    y = MOI.add_variable(model)
    @test MOI.get(model, MOI.NumberOfVariables()) == 2
    vc1 = MOI.add_constraint(model, x, MOI.GreaterThan(T(1)))
    @test vc1.value == x.value
    vc2 = MOI.add_constraint(model, y, MOI.GreaterThan(T(1)))
    @test vc2.value == y.value
    cf = MOI.ScalarQuadraticFunction(
        [MOI.ScalarQuadraticTerm(T(1), x, y)],
        [MOI.ScalarAffineTerm(T(0), x)],
        T(0),
    )
    c = MOI.add_constraint(model, cf, MOI.LessThan(T(4)))
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{
                MOI.ScalarQuadraticFunction{T},
                MOI.LessThan{T},
            }(),
        ) == 1
    end
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
        MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(T[2, 1], [x, y]), T(0)),
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
    @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE
    if _supports(config, MOI.ConstraintFunction)
        @test cf ≈ MOI.get(model, MOI.ConstraintFunction(), c)
    end
    if _supports(config, MOI.optimize!)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
        @test ≈(MOI.get(model, MOI.ObjectiveValue()), T(9), config)
        @test ≈(MOI.get(model, MOI.VariablePrimal(), x), T(4), config)
        @test ≈(MOI.get(model, MOI.VariablePrimal(), y), T(1), config)
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), c), T(4), config)
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), vc1), T(4), config)
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), vc2), T(1), config)
    end
    return
end

function setup_test(
    ::typeof(test_quadratic_nonconvex_constraint_integration),
    model::MOIU.MockOptimizer,
    ::Config{T},
) where {T}
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) ->
            MOIU.mock_optimize!(mock, T[4, 1], MOI.FEASIBLE_POINT),
    )
    return
end

"""
    test_quadratic_nonconvex_constraint_basic(
        model::MOI.ModelLike,
        config::Config,
    )

Test the problem:
```
Find x, y
s.t. x * y == 4 (c)
     x * x == 4 (c2)
     x, y >= 0
```
"""
function test_quadratic_nonconvex_constraint_basic(
    model::MOI.ModelLike,
    config::Config{T},
) where {T}
    @requires MOI.supports_incremental_interface(model)
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarQuadraticFunction{T},
        MOI.EqualTo{T},
    )
    x = MOI.add_variable(model)
    y = MOI.add_variable(model)
    @test MOI.get(model, MOI.NumberOfVariables()) == 2
    vc1 = MOI.add_constraint(model, x, MOI.GreaterThan(T(0)))
    @test vc1.value == x.value
    vc2 = MOI.add_constraint(model, y, MOI.GreaterThan(T(0)))
    @test vc2.value == y.value
    cf = MOI.ScalarQuadraticFunction(
        [MOI.ScalarQuadraticTerm(T(1), x, y)],
        [MOI.ScalarAffineTerm(T(0), x)],
        T(0),
    )
    c = MOI.add_constraint(model, cf, MOI.EqualTo(T(4)))
    cf2 = MOI.ScalarQuadraticFunction(
        [MOI.ScalarQuadraticTerm(T(2), x, x)],
        [MOI.ScalarAffineTerm(T(0), x)],
        T(0),
    )
    c2 = MOI.add_constraint(model, cf2, MOI.EqualTo(T(4)))
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{
                MOI.ScalarQuadraticFunction{T},
                MOI.EqualTo{T},
            }(),
        ) == 2
    end
    if _supports(config, MOI.ConstraintFunction)
        @test cf ≈ MOI.get(model, MOI.ConstraintFunction(), c)
        @test cf2 ≈ MOI.get(model, MOI.ConstraintFunction(), c2)
    end
    if _supports(config, MOI.optimize!)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
        @test ≈(MOI.get(model, MOI.VariablePrimal(), x), T(2), config)
        @test ≈(MOI.get(model, MOI.VariablePrimal(), y), T(2), config)
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), c), T(4), config)
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), c2), T(4), config)
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), vc1), T(2), config)
        @test ≈(MOI.get(model, MOI.ConstraintPrimal(), vc2), T(2), config)
    end
    return
end

function setup_test(
    ::typeof(test_quadratic_nonconvex_constraint_basic),
    model::MOIU.MockOptimizer,
    ::Config{T},
) where {T}
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) ->
            MOIU.mock_optimize!(mock, T[2, 2], MOI.FEASIBLE_POINT),
    )
    return
end

"""
    test_quadratic_SecondOrderCone_basic(model::MOI.ModelLike, config::Config{T}) where {T}

Test the non-convex formulation of a SecondOrderCone constraint:
```
min t
s.t. x + y >= 1 (c1)
     x^2 + y^2 <= t^2 (c2)
     t >= 0 (bound)
```

This is a useful test because some solvers have support for detecting that it is
actually a convex problem due to the `t >= 0` bound.

If your solver does not have this support (and therefore fails this test), you
can safely exclude it.
"""
function test_quadratic_SecondOrderCone_basic(
    model::MOI.ModelLike,
    config::Config{T},
) where {T}
    @requires MOI.supports_incremental_interface(model)
    @requires MOI.supports(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
    )
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarQuadraticFunction{T},
        MOI.LessThan{T},
    )
    @requires MOI.supports_constraint(
        model,
        MOI.ScalarAffineFunction{T},
        MOI.GreaterThan{T},
    )
    @requires MOI.supports_constraint(
        model,
        MOI.VariableIndex,
        MOI.GreaterThan{T},
    )
    x = MOI.add_variable(model)
    y = MOI.add_variable(model)
    t = MOI.add_variable(model)
    @test MOI.get(model, MOI.NumberOfVariables()) == 3
    c1f = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(T(1), [x, y]), T(0))
    c1 = MOI.add_constraint(model, c1f, MOI.GreaterThan(T(1)))
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{
                MOI.ScalarAffineFunction{T},
                MOI.GreaterThan{T},
            }(),
        ) == 1
    end
    c2f = MOI.ScalarQuadraticFunction(
        MOI.ScalarQuadraticTerm.(T[2, 2, -2], [x, y, t], [x, y, t]),
        MOI.ScalarAffineTerm{T}[],
        T(0),
    )
    c2 = MOI.add_constraint(model, c2f, MOI.LessThan(T(0)))
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{
                MOI.ScalarQuadraticFunction{T},
                MOI.LessThan{T},
            }(),
        ) == 1
    end
    bound = MOI.add_constraint(model, t, MOI.GreaterThan(T(0)))
    @test bound.value == t.value
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{MOI.VariableIndex,MOI.GreaterThan{T}}(),
        ) == 1
    end
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
        MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(T(1), t)], T(0)),
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
    @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MIN_SENSE
    if _supports(config, MOI.ConstraintFunction)
        @test c1f ≈ MOI.get(model, MOI.ConstraintFunction(), c1)
        @test c2f ≈ MOI.get(model, MOI.ConstraintFunction(), c2)
    end
    if _supports(config, MOI.optimize!)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
        @test ≈(MOI.get(model, MOI.ObjectiveValue()), 1 / sqrt(T(2)), config)
        @test ≈(
            MOI.get(model, MOI.VariablePrimal(), [x, y, t]),
            T[1//2, 1//2, 1/sqrt(T(2))],
            config,
        )
        @test ≈(
            MOI.get(model, MOI.VariablePrimal(), [t, x, y, t]),
            T[1/sqrt(T(2)), 1//2, 1//2, 1/sqrt(T(2))],
            config,
        )
        if _supports(config, MOI.ConstraintDual)
            @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT
            # The dual constraint gives
            # [1, 0, 0] = [λ_bound, λ_c1, λ_c1] + λ_c2 * [-2 0 0; 0 2 0; 0 0 2] * x
            #           = [λ_bound - sqrt(T(2))*λ_c2, λ_c1 + λ_c2, λ_c1 + λ_c2]
            # and since `bound` is not active, `λ_bound = 0`.
            # This gives `λ_c2 = -1/sqrt(T(2))` and `λ_c1 = 1/sqrt(T(2))`.
            @test ≈(
                MOI.get(model, MOI.ConstraintDual(), c1),
                1 / sqrt(T(2)),
                config,
            )
            @test ≈(
                MOI.get(model, MOI.ConstraintDual(), c2),
                -1 / sqrt(T(2)),
                config,
            )
            @test ≈(MOI.get(model, MOI.ConstraintDual(), bound), T(0), config)
        end
    end
    return
end

function setup_test(
    ::typeof(test_quadratic_SecondOrderCone_basic),
    model::MOIU.MockOptimizer,
    ::Config{T},
) where {T}
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
            mock,
            T[1//2, 1//2, 1/sqrt(T(2))],
            (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}) =>
                [1 / sqrt(T(2))],
            (MOI.ScalarQuadraticFunction{T}, MOI.LessThan{T}) =>
                [-1 / sqrt(T(2))],
        ),
    )
    return
end

"""
    test_quadratic_Integer_SecondOrderCone(
        model::MOI.ModelLike,
        config::Config{T},
    ) where {T}

Run an integration test on the problem:
```
 min    - 2y - 1z
s.t.   x           == 1
      [x,  y,   z] in SecondOrderCone()
           y,   z  in ZeroOne()
```
"""
function test_quadratic_Integer_SecondOrderCone(
    model::MOI.ModelLike,
    config::Config{T},
) where {T}
    @requires MOI.supports_incremental_interface(model)
    @requires MOI.supports(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
    )
    @requires MOI.supports_constraint(
        model,
        MOI.VectorAffineFunction{T},
        MOI.Zeros,
    )
    @requires MOI.supports_constraint(model, MOI.VariableIndex, MOI.ZeroOne)
    @requires MOI.supports_constraint(
        model,
        MOI.VectorOfVariables,
        MOI.SecondOrderCone,
    )
    x, y, z = MOI.add_variables(model, 3)
    MOI.set(
        model,
        MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(),
        MOI.ScalarAffineFunction(
            MOI.ScalarAffineTerm.(T[-2, -1], [y, z]),
            T(0),
        ),
    )
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
    MOI.add_constraint(
        model,
        MOI.VectorAffineFunction(
            [MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(T(1), x))],
            T[-1],
        ),
        MOI.Zeros(1),
    )
    MOI.add_constraint(
        model,
        MOI.VectorOfVariables([x, y, z]),
        MOI.SecondOrderCone(3),
    )
    if _supports(config, MOI.NumberOfConstraints)
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{MOI.VectorAffineFunction{T},MOI.Zeros}(),
        ) == 1
        @test MOI.get(
            model,
            MOI.NumberOfConstraints{MOI.VectorOfVariables,MOI.SecondOrderCone}(),
        ) == 1
    end
    loc = MOI.get(model, MOI.ListOfConstraintTypesPresent())
    @test length(loc) == 2
    @test (MOI.VectorAffineFunction{T}, MOI.Zeros) in loc
    @test (MOI.VectorOfVariables, MOI.SecondOrderCone) in loc
    bin1 = MOI.add_constraint(model, y, MOI.ZeroOne())
    # We test this after the creation of every `VariableIndex` constraint
    # to ensure a good coverage of corner cases.
    @test bin1.value == y.value
    bin2 = MOI.add_constraint(model, z, MOI.ZeroOne())
    @test bin2.value == z.value
    if _supports(config, MOI.optimize!)
        @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
        MOI.optimize!(model)
        @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
        @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
        @test ≈(MOI.get(model, MOI.ObjectiveValue()), -2, config)
        @test ≈(MOI.get(model, MOI.VariablePrimal(), x), 1, config)
        @test ≈(MOI.get(model, MOI.VariablePrimal(), y), 1, config)
        @test ≈(MOI.get(model, MOI.VariablePrimal(), z), 0, config)
    end
    return
end

function setup_test(
    ::typeof(test_quadratic_Integer_SecondOrderCone),
    model::MOI.Utilities.MockOptimizer,
    config::Config{T},
) where {T}
    MOIU.set_mock_optimize!(
        model,
        (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, T[1, 1, 0]),
    )
    return
end


text:
#######################
### Named Functions ###
#######################

struct NamedFunction{F} <: Function
    name :: String
    f    :: F
end

show(io::IO, nf::NamedFunction) = print(io, nf.name)
(nf::NamedFunction)(x)  = (nf.f)(x)

#####################
### Grammar Class ###
#####################

isapplicable(r, c) = r(c) !== nothing

Completion = Tuple{Int, Int} # category indices and rule indices

mutable struct Grammar{C, D, F, S, T}
    categories       :: Vector{C}
    start_categories :: Vector{C}
    depcomp          :: F
    depcomps         :: Vector{D}
    catidx2dcompidx  :: Vector{Int}
    binary_rules     :: Vector{Function}
    terminal_rules   :: Vector{Function}
    all_rules        :: Vector{Function}
    binary_dict      :: Dict{Tuple{Int, Int}, Vector{Completion}}
    terminal_dict    :: Dict{T, Vector{Completion}}
    scores           :: S
end

function Grammar(
        categories       :: Vector,
        start_categories :: Vector,
        binary_rules_    :: Vector,
        terminal_rules_  :: Vector,
        depcomp,
        scores_          :: NamedTuple = (count = count_score,)
    )

    terminal_rules = convert(Vector{Function}, terminal_rules_)
    binary_rules   = convert(Vector{Function}, binary_rules_)

    depcomps  = unique(map(depcomp, categories))
    all_rules = vcat(binary_rules, terminal_rules)

    function find_elem_in(x, xs)
        i = findfirst(y->x==y, xs)
        @assert i > 0 "$x not in $xs"
        i
    end

    catidx   = @closure c  -> find_elem_in(c,  categories)
    dcompidx = @closure dc -> find_elem_in(dc, depcomps)
    ruleidx  = @closure r  -> find_elem_in(r,  all_rules)

    catidx2dcompidx = map(dcompidx ∘ depcomp, categories)

    binary_rhss = collect(
        (map(catidx, r(c)), (catidx(c), ruleidx(r)))
        for r in binary_rules for c in categories if isapplicable(r, c)
    )

    terminal_rhss = collect(
        (r(c), (catidx(c), ruleidx(r)))
        for r in terminal_rules for c in categories if isapplicable(r, c)
    )

    function multidict_from_list(list::AbstractVector{Tuple{K, V}}) where {K, V}
        foldr(list, init = Dict{K, Vector{V}}()) do (k, v), d
            haskey(d, k) ? push!(d[k], v) : d[k] = [v]
            d
        end
    end

    binary_dict   = multidict_from_list(binary_rhss)
    terminal_dict = multidict_from_list(terminal_rhss)

    one_dcomp_per_category = unique(depcomp, categories)
    score_matrix(s) = [s(c, r) for c in one_dcomp_per_category, r in all_rules]
    score_matrices  = map(score_matrix, scores_)

    scores = map(score_matrices) do M
        @closure (catidx, ruleidx) -> M[catidx2dcompidx[catidx], ruleidx]
    end

    Grammar(
        categories,
        start_categories,
        depcomp,
        depcomps,
        catidx2dcompidx,
        binary_rules,
        terminal_rules,
        all_rules,
        binary_dict,
        terminal_dict,
        scores
    )
end

score(g::Grammar, catidx, ruleidx) = map(eval_at(catidx, ruleidx), g.scores)
mergeable(g::Grammar, rhs)         = haskey(g.binary_dict, rhs)
merge(g::Grammar, rhs)             = g.binary_dict[rhs]

function categorize(g::Grammar, terminal)
    if haskey(g.terminal_dict, terminal)
        g.terminal_dict[terminal]
    else
        Vector{Completion}()
    end
end

##################
### The Parser ###
##################

"""
    parse(grammar, terminals)

parse the sequence of terminals according to the grammar, return a dictionary
that maps head categories to their semiring values

The metod first creates the diagonal of the chart as a dictionary,
then updates this dictionary dynammicaly using a `LazyDict`,
applying dynamic programming under the hood automatically.
"""
function parse(g::Grammar, terminals)
    initial_chart = Dict(
        (i, i) => Dict(
            lhs => score(g, lhs, r)
            for (lhs, r) in categorize(g, t)
        )
        for (i, t) in enumerate(terminals)
    )

    chart = LazyDict(initial_chart) do i, j
        cell = valtype(initial_chart)()
        for k in i : j-1
            for (rhs1, s1) in chart[i, k]
                for (rhs2, s2) in chart[k+1, j]
                    if mergeable(g, (rhs1, rhs2))
                        for (lhs, r) in merge(g, (rhs1, rhs2))
                            if haskey(cell, lhs)
                                cell[lhs] += score(g, lhs, r) * s1 * s2
                            else
                                cell[lhs] =  score(g, lhs, r) * s1 * s2
                            end
                        end
                    end
                end
            end
        end
        cell
    end

    heads = chart[1, length(terminals)]
    Dict(
        startcat => haskey(heads, catidx) ? heads[catidx] : zero(valtype(heads))
        for (catidx, startcat) in enumerate(g.start_categories)
    )
end

ext: jl


text:
# =======================================================================
# 2KP

using vOptSpecific

# ==========================================
# instance

id = load2UKP("../examples/2KP500-1A.DAT")

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

z1, z2, w, solutions = vSolve(id)

for i = 1:length(z1)
    println("(" , z1[i], " | ", z2[i], ") : ", solutions[i,:])
end

max_stars_repo_name: vOptSolver/vOptSpecific

max_issues_repo_name: vOptSolver/vOptSpecific

max_forks_repo_name: vOptSolver/vOptSpecific


text:
using Base.Test
using JLab

# #############################################################################
#Test significant figures
println("########## Testing \"to_sf\" ##########")

#Non-positive sigfig error
println("Testing for error on non-positive argument num_sf ...")
@test_throws to_sf(1,0)
@test_throws to_sf(1,-1)

#Input types
println("Testing input types ...")
@test to_sf(1,3) == "1.00"
@test to_sf(1.0,3) == "1.00"
@test to_sf(1e0,3) == "1.00"
@test to_sf(pi,3) == "3.14"

#Zero
println("Testing 0 as input ...")
@test to_sf(0,3) == "0"

#Rounding
println("Testing rounding ...")
@test to_sf(3.141593,6) == "3.14159"
@test to_sf(3.141593,5) == "3.1416"
@test to_sf(3.141593,4) == "3.142"
@test to_sf(3.141593,3) == "3.14"
@test to_sf(3.141593,2) == "3.1"
@test to_sf(3.141593,1) == "3"

#Exponents
println("Testing exponent notation ...")
@test to_sf(3.142e4,5) == "3.1420e4"
@test to_sf(3.142e4,4) == "3.142e4"

@test to_sf(3.142e3,5) == "3.1420e3"
@test to_sf(3.142e3,4) == "3142"
@test to_sf(3.142e3,3) == "3140"
@test to_sf(3.142e3,2) == "3100"
@test to_sf(3.142e3,1) == "3000"
@test to_sf(3.142e3,4,force=true) == "3.142e3"
@test to_sf(3.142e3,3,force=true) == "3.14e3"
@test to_sf(3.142e3,2,force=true) == "3.1e3"
@test to_sf(3.142e3,1,force=true) == "3e3"

@test to_sf(3.142e2,4) == "3.142e2"
@test to_sf(3.142e2,3) == "314"
@test to_sf(3.142e2,2) == "310"
@test to_sf(3.142e2,1) == "300"
@test to_sf(3.142e2,3,force=true) == "3.14e2"
@test to_sf(3.142e2,2,force=true) == "3.1e2"
@test to_sf(3.142e2,1,force=true) == "3e2"

@test to_sf(3.142e1,3) == "3.14e1"
@test to_sf(3.142e1,2) == "31"
@test to_sf(3.142e1,1) == "30"
@test to_sf(3.142e1,2,force=true) == "3.1e1"
@test to_sf(3.142e1,1,force=true) == "3e1"

@test to_sf(3.142,2) == "3.1"
@test to_sf(3.142,1,force=true) == "3"
@test to_sf(3.142,1) == "3"
@test to_sf(3.142,1,force=true) == "3"

@test to_sf(3.142e-1,3) == "0.314"
@test to_sf(3.142e-1,3,force=true) == "3.14e-1"

@test to_sf(3.142e-2,3) == "3.14e-2"

#Exponent format
println("Testing exponent format ...")
@test to_sf(3.14e-2,3,expformat="\\times10^{%}") == "3.14\\times10^{-2}"

println("Function \"to_sf\" passes tests.")

# #############################################################################
#Test decimal places
println("########## Testing \"to_dp\" ##########")

#Input types
println("Testing input types ...")
@test to_dp(1,2) == "1.00"
@test to_dp(1.0,2) == "1.00"
@test to_dp(1e0,2) == "1.00"
@test to_dp(pi,2) == "3.14"

#Rounding
println("Testing rounding ...")
@test to_dp(3.141593,7) == "3.1415930"
@test to_dp(3.141593,6) == "3.141593"
@test to_dp(3.141593,5) == "3.14159"
@test to_dp(3.141593,4) == "3.1416"
@test to_dp(3.141593,3) == "3.142"

#Decimal places
println("Testing decimal places ...")
@test to_dp(3.142e4,2) == "31420.00"
@test to_dp(3.142e3,2) == "3142.00"
@test to_dp(3.142e3,0) == "3142"
@test to_dp(3.142e3,-1) == "3140"
@test to_dp(3.142e3,-3) == "3000"
@test to_dp(3.142e3,-4) == "0"

println("Function \"to_dp\" passes tests.")


text:
# File   : spherical_cylinder_approx.jl
# License: GNU v3.0
# Author : Andrei Leonard Nicusan <a.l.nicusan@bham.ac.uk>
# Date   : 28.08.2021

using DelimitedFiles
using LinearAlgebra

using GLMakie
using GeometryBasics

# Vertices and face indices generated using AutoDesk; two spherical caps were created, then the
# convex hull was saved in the "spherical_cylinder.obj" file
verts = [
    -0.945817 0.324700 0.000000
    -0.947808 0.078538 -0.309017
    -1.000000 -0.000000 0.000000
    -0.921954 -0.233471 -0.309017
    -0.945817 -0.324700 0.000000
    -0.796193 -0.520179 -0.309017
    -0.789141 -0.614213 0.000000
    -0.584151 -0.750517 -0.309017
    -0.546948 -0.837166 -0.000000
    -0.308807 -0.899526 -0.309017
    -0.245485 -0.969400 -0.000000
    0.000000 -0.951056 -0.309017
    0.082579 -0.996584 -0.000000
    0.308807 -0.899526 -0.309017
    0.401695 -0.915773 -0.000000
    0.584151 -0.750517 -0.309017
    0.677282 -0.735724 -0.000000
    0.796193 -0.520179 -0.309017
    0.879474 -0.475947 0.000000
    0.921954 -0.233471 -0.309017
    0.986361 -0.164595 0.000000
    0.947808 0.078538 -0.309017
    0.986361 0.164595 0.000000
    0.870952 0.382035 -0.309017
    0.879474 0.475947 0.000000
    0.699715 0.644133 -0.309017
    0.677282 0.735724 0.000000
    0.452653 0.836429 -0.309017
    0.401695 0.915773 0.000000
    0.156539 0.938085 -0.309017
    0.082579 0.996584 0.000000
    -0.156539 0.938085 -0.309017
    -0.245485 0.969400 0.000000
    -0.452653 0.836429 -0.309017
    -0.546948 0.837166 0.000000
    -0.699715 0.644133 -0.309017
    -0.789141 0.614213 0.000000
    -0.870952 0.382035 -0.309017
    0.000000 -0.309017 -0.951056
    0.000000 0.000000 -1.000000
    0.100338 -0.292274 -0.951056
    0.189802 -0.243858 -0.951056
    0.258699 -0.169016 -0.951056
    0.299561 -0.075859 -0.951056
    0.307961 0.025518 -0.951056
    0.282989 0.124131 -0.951056
    0.227351 0.209292 -0.951056
    0.147076 0.271772 -0.951056
    0.050863 0.304802 -0.951056
    -0.050863 0.304802 -0.951056
    -0.147076 0.271772 -0.951056
    -0.227351 0.209292 -0.951056
    -0.282989 0.124131 -0.951056
    -0.307961 0.025518 -0.951056
    -0.299561 -0.075859 -0.951056
    -0.258699 -0.169016 -0.951056
    -0.189802 -0.243858 -0.951056
    -0.100338 -0.292274 -0.951056
    0.000000 -0.809017 -0.587785
    0.262687 -0.765182 -0.587785
    0.496909 -0.638428 -0.587785
    0.677282 -0.442490 -0.587785
    0.784261 -0.198602 -0.587785
    0.806254 0.066808 -0.587785
    0.740876 0.324978 -0.587785
    0.595213 0.547932 -0.587785
    0.385050 0.711509 -0.587785
    0.133160 0.797983 -0.587785
    -0.133160 0.797983 -0.587785
    -0.385050 0.711509 -0.587785
    -0.595213 0.547932 -0.587785
    -0.740876 0.324978 -0.587785
    -0.806254 0.066808 -0.587785
    -0.784261 -0.198602 -0.587785
    -0.677282 -0.442490 -0.587785
    -0.496909 -0.638428 -0.587785
    -0.262687 -0.765182 -0.587785
    0.000000 -0.587785 -0.809017
    0.190854 -0.555937 -0.809017
    0.361025 -0.463845 -0.809017
    0.492074 -0.321488 -0.809017
    0.569799 -0.144293 -0.809017
    0.585778 0.048539 -0.809017
    0.538278 0.236111 -0.809017
    0.432448 0.398096 -0.809017
    0.279755 0.516942 -0.809017
    0.096746 0.579769 -0.809017
    -0.096746 0.579769 -0.809017
    -0.279755 0.516942 -0.809017
    -0.432448 0.398096 -0.809017
    -0.538278 0.236111 -0.809017
    -0.585778 0.048539 -0.809017
    -0.569799 -0.144293 -0.809017
    -0.492074 -0.321488 -0.809017
    -0.361025 -0.463845 -0.809017
    -0.190854 -0.555937 -0.809017
    -1.000000 -0.000000 1.000000
    -0.945817 0.324700 1.000000
    -0.789141 0.614213 1.000000
    -0.546948 0.837166 1.000000
    -0.245485 0.969400 1.000000
    0.082579 0.996584 1.000000
    0.401695 0.915773 1.000000
    0.677282 0.735724 1.000000
    0.879474 0.475947 1.000000
    0.986361 0.164595 1.000000
    0.986361 -0.164595 1.000000
    0.879474 -0.475947 1.000000
    0.677282 -0.735724 1.000000
    0.401695 -0.915773 1.000000
    0.082579 -0.996584 1.000000
    -0.245485 -0.969400 1.000000
    -0.546948 -0.837166 1.000000
    -0.789141 -0.614213 1.000000
    -0.945817 -0.324700 1.000000
    -0.921954 -0.233471 1.309017
    -0.947808 0.078538 1.309017
    -0.870952 0.382035 1.309017
    -0.699715 0.644133 1.309017
    -0.452653 0.836429 1.309017
    -0.156539 0.938085 1.309017
    0.156539 0.938085 1.309017
    0.452653 0.836429 1.309017
    0.699715 0.644133 1.309017
    0.870952 0.382035 1.309017
    0.947808 0.078538 1.309017
    0.921954 -0.233471 1.309017
    0.796193 -0.520179 1.309017
    0.584151 -0.750517 1.309017
    0.308807 -0.899526 1.309017
    -0.000000 -0.951056 1.309017
    -0.308807 -0.899526 1.309017
    -0.584151 -0.750517 1.309017
    -0.796193 -0.520179 1.309017
    -0.000000 -0.309017 1.951057
    -0.000000 -0.000000 2.000000
    -0.100338 -0.292274 1.951057
    -0.189802 -0.243858 1.951057
    -0.258699 -0.169016 1.951057
    -0.299561 -0.075859 1.951057
    -0.307961 0.025518 1.951057
    -0.282989 0.124131 1.951057
    -0.227351 0.209292 1.951057
    -0.147076 0.271772 1.951057
    -0.050863 0.304802 1.951057
    0.050863 0.304802 1.951057
    0.147076 0.271772 1.951057
    0.227351 0.209292 1.951057
    0.282989 0.124131 1.951057
    0.307961 0.025518 1.951057
    0.299561 -0.075859 1.951057
    0.258699 -0.169016 1.951057
    0.189802 -0.243858 1.951057
    0.100338 -0.292274 1.951057
    -0.000000 -0.809017 1.587785
    -0.262687 -0.765182 1.587785
    -0.496909 -0.638428 1.587785
    -0.677282 -0.442490 1.587785
    -0.784261 -0.198602 1.587785
    -0.806254 0.066808 1.587785
    -0.740876 0.324978 1.587785
    -0.595213 0.547932 1.587785
    -0.385050 0.711509 1.587785
    -0.133160 0.797983 1.587785
    0.133160 0.797983 1.587785
    0.385050 0.711509 1.587785
    0.595213 0.547932 1.587785
    0.740876 0.324978 1.587785
    0.806254 0.066808 1.587785
    0.784261 -0.198602 1.587785
    0.677282 -0.442490 1.587785
    0.496909 -0.638428 1.587785
    0.262687 -0.765182 1.587785
    -0.000000 -0.587785 1.809017
    -0.190854 -0.555937 1.809017
    -0.361025 -0.463845 1.809017
    -0.492074 -0.321488 1.809017
    -0.569799 -0.144293 1.809017
    -0.585778 0.048539 1.809017
    -0.538278 0.236111 1.809017
    -0.432448 0.398096 1.809017
    -0.279755 0.516942 1.809017
    -0.096746 0.579769 1.809017
    0.096746 0.579769 1.809017
    0.279755 0.516942 1.809017
    0.432448 0.398096 1.809017
    0.538278 0.236111 1.809017
    0.585778 0.048539 1.809017
    0.569799 -0.144293 1.809017
    0.492074 -0.321488 1.809017
    0.361025 -0.463845 1.809017
    0.190854 -0.555937 1.809017
]

faces = [
    1 2 3
    3 2 4
    3 4 5
    5 4 6
    5 6 7
    7 6 8
    7 8 9
    9 8 10
    9 10 11
    11 10 12
    11 12 13
    13 12 14
    13 14 15
    15 14 16
    15 16 17
    17 16 18
    17 18 19
    19 18 20
    19 20 21
    21 20 22
    21 22 23
    23 22 24
    23 24 25
    25 24 26
    25 26 27
    27 26 28
    27 28 29
    29 28 30
    29 30 31
    31 30 32
    31 32 33
    33 32 34
    33 34 35
    35 34 36
    35 36 37
    37 36 38
    37 38 1
    1 38 2
    39 40 41
    41 40 42
    42 40 43
    43 40 44
    44 40 45
    45 40 46
    47 40 48
    48 40 49
    49 40 50
    50 40 51
    51 40 52
    53 40 54
    54 40 55
    55 40 56
    56 40 57
    57 40 58
    58 40 39
    12 59 14
    14 59 60
    14 60 16
    16 60 61
    16 61 18
    18 61 62
    18 62 20
    20 62 63
    20 63 22
    22 63 64
    22 64 24
    24 64 65
    24 65 26
    26 65 66
    26 66 28
    28 66 67
    28 67 30
    30 67 68
    30 68 32
    32 68 69
    32 69 34
    34 69 70
    34 70 36
    36 70 71
    36 71 38
    38 71 72
    38 72 2
    2 72 73
    2 73 4
    4 73 74
    4 74 6
    6 74 75
    6 75 8
    8 75 76
    8 76 10
    10 76 77
    10 77 12
    12 77 59
    59 78 60
    60 78 79
    60 79 61
    61 79 80
    61 80 62
    62 80 81
    62 81 63
    63 81 82
    63 82 64
    64 82 83
    64 83 65
    65 83 84
    65 84 66
    66 84 85
    66 85 67
    67 85 86
    67 86 68
    68 86 87
    68 87 69
    69 87 88
    69 88 70
    70 88 89
    70 89 71
    71 89 90
    71 90 72
    72 90 91
    72 91 73
    73 91 92
    73 92 74
    74 92 93
    74 93 75
    75 93 94
    75 94 76
    76 94 95
    76 95 77
    77 95 96
    77 96 59
    59 96 78
    78 39 79
    79 39 41
    79 41 80
    80 41 42
    80 42 81
    81 42 43
    81 43 82
    82 43 44
    82 44 83
    83 44 45
    83 45 84
    84 45 46
    84 46 85
    85 46 47
    85 47 86
    86 47 48
    86 48 87
    87 48 49
    87 49 88
    88 49 50
    88 50 89
    89 50 51
    89 51 90
    90 51 52
    90 52 91
    91 52 53
    91 53 92
    92 53 54
    92 54 93
    93 54 55
    93 55 94
    94 55 56
    94 56 95
    95 56 57
    95 57 96
    96 57 58
    96 58 78
    78 58 39
    46 40 47
    52 40 53
    5 97 3
    3 97 98
    3 98 1
    1 98 99
    1 99 37
    37 99 100
    37 100 35
    35 100 101
    35 101 33
    33 101 102
    33 102 31
    31 102 103
    31 103 29
    29 103 104
    29 104 27
    27 104 105
    27 105 25
    25 105 106
    25 106 23
    23 106 107
    23 107 21
    21 107 108
    21 108 19
    19 108 109
    19 109 17
    17 109 110
    17 110 15
    15 110 111
    15 111 13
    13 111 112
    13 112 11
    11 112 113
    11 113 9
    9 113 114
    9 114 7
    7 114 115
    7 115 5
    5 115 97
    115 116 97
    97 116 117
    97 117 98
    98 117 118
    98 118 99
    99 118 119
    99 119 100
    100 119 120
    100 120 101
    101 120 121
    101 121 102
    102 121 122
    102 122 103
    103 122 123
    103 123 104
    104 123 124
    104 124 105
    105 124 125
    105 125 106
    106 125 126
    106 126 107
    107 126 127
    107 127 108
    108 127 128
    108 128 109
    109 128 129
    109 129 110
    110 129 130
    110 130 111
    111 130 131
    111 131 112
    112 131 132
    112 132 113
    113 132 133
    113 133 114
    114 133 134
    114 134 115
    115 134 116
    135 136 137
    137 136 138
    138 136 139
    139 136 140
    140 136 141
    141 136 142
    142 136 143
    144 136 145
    145 136 146
    146 136 147
    147 136 148
    148 136 149
    150 136 151
    151 136 152
    152 136 153
    153 136 154
    154 136 135
    131 155 132
    132 155 156
    132 156 133
    133 156 157
    133 157 134
    134 157 158
    134 158 116
    116 158 159
    116 159 117
    117 159 160
    117 160 118
    118 160 161
    118 161 119
    119 161 162
    119 162 120
    120 162 163
    120 163 121
    121 163 164
    121 164 122
    122 164 165
    122 165 123
    123 165 166
    123 166 124
    124 166 167
    124 167 125
    125 167 168
    125 168 126
    126 168 169
    126 169 127
    127 169 170
    127 170 128
    128 170 171
    128 171 129
    129 171 172
    129 172 130
    130 172 173
    130 173 131
    131 173 155
    155 174 156
    156 174 175
    156 175 157
    157 175 176
    157 176 158
    158 176 177
    158 177 159
    159 177 178
    159 178 160
    160 178 179
    160 179 161
    161 179 180
    161 180 162
    162 180 181
    162 181 163
    163 181 182
    163 182 164
    164 182 183
    164 183 165
    165 183 184
    165 184 166
    166 184 185
    166 185 167
    167 185 186
    167 186 168
    168 186 187
    168 187 169
    169 187 188
    169 188 170
    170 188 189
    170 189 171
    171 189 190
    171 190 172
    172 190 191
    172 191 173
    173 191 192
    173 192 155
    155 192 174
    174 135 175
    175 135 137
    175 137 176
    176 137 138
    176 138 177
    177 138 139
    177 139 178
    178 139 140
    178 140 179
    179 140 141
    179 141 180
    180 141 142
    180 142 181
    181 142 143
    181 143 182
    182 143 144
    182 144 183
    183 144 145
    183 145 184
    184 145 146
    184 146 185
    185 146 147
    185 147 186
    186 147 148
    186 148 187
    187 148 149
    187 149 188
    188 149 150
    188 150 189
    189 150 151
    189 151 190
    190 151 152
    190 152 191
    191 152 153
    191 153 192
    192 153 154
    192 154 174
    174 154 135
    136 150 149
    136 144 143
]

# Index from 0, as expected by the C code
faces .-= 1

# Split vertices into the bottom and top caps
half = size(verts)[1] ÷ 2

verts_bottom = zeros(half, 3)
index_bottom = 1

verts_top = zeros(half, 3)
index_top = 1

faces_split = copy(faces)

for (i, v) in enumerate(eachrow(verts))
    if v[3] < 0.5
        verts_bottom[index_bottom, :] .= v
        faces_split[faces .== i - 1] .= index_bottom - 1

        global index_bottom += 1
    else
        verts_top[index_top, :] .= v
        faces_split[faces .== i - 1] .= index_top - 1 + half

        global index_top += 1
    end
end

verts_split = [verts_bottom ; verts_top]

# Move top cap back to (0, 0, 0) and flip it
verts_split[half + 1:end, 3] .-= 1
verts_split[half + 1:end, 3] .*= -1

# Write processed vertices and face indices to CSV files
open("spherical_cylinder_verts.csv", "w") do io
    writedlm(io, verts_split, ',')
end

open("spherical_cylinder_faces.csv", "w") do io
    writedlm(io, faces_split, ',')
end

# Create spherical cylinder spanning two points p1 and p2 on a particle trajectory
p1 = [5, 4, 3]
r1 = 2

p2 = [6, 7, 8]
r2 = 4

# Expand vertices to given particles' radii
verts_split[1:half, :] .*= r1
verts_split[half + 1:end, :] .*= r2

# Create rotation matrix from vertical +Z to |p2 - p1|
a = [0, 0, 1]
b = normalize(p2 .- p1)

v = cross(a, b)
c = dot(a, b)
vx = [  0    -v[3]   v[2]
       v[3]    0    -v[1]
      -v[2]   v[1]    0  ]

rot = I(3) + vx + vx * vx / (1 + c)

# Rotate all vertices
verts_split = (rot * verts_split')'

# Translate vertices
verts_split[1:half, :] .+= p1'
verts_split[half + 1:end, :] .+= p2'

# Plot mesh and direction from p1 to p2
m = GeometryBasics.Mesh(
    Point3.(eachrow(verts_split)),
    TriangleFace.(eachrow(faces_split .+ 1)),
)

wireframe(m)
lines!([p1 p2], color = :red)

current_figure()

max_stars_repo_name: anicusan/KonigCell

max_issues_repo_name: anicusan/KonigCell

max_forks_repo_name: anicusan/KonigCell


text:
@testset "SVD" begin
    @testset "Default" begin
        tester = ImputorTester(SVD)
        test_hashing(tester)
        test_equality(tester)

        # test_matrix(tester)
        # Default transpose test uses `isequal`, but the SVD imputor will have a
        # small floating point error.
        @testset "Matrix" begin
            a = allowmissing(1.0:1.0:20.0)
            a[[2, 3, 7]] .= missing
            m = collect(reshape(a, 5, 4))

            result = impute(m, tester.imp(; tester.kwargs...); dims=:cols)

            @testset "Base" begin
                # Test that we have fewer missing values
                @test count(ismissing, result) < count(ismissing, m)
                @test isa(result, Matrix)
                @test eltype(result) <: eltype(m)

                # Test that functional form behaves the same way
                @test result == tester.f(m; dims=:cols, tester.kwargs...)
            end

            @testset "In-place" begin
                # Test that the in-place function return the new results and logs whether it
                # successfully did it in-place
                m2 = deepcopy(m)
                m2_ = tester.f!(m2; dims=:cols, tester.kwargs...)
                @test m2_ == result
                if m2 != result
                    @warn "$(tester.f!) did not mutate input data of type Matrix"
                end
            end

            @testset "Transpose" begin
                m_ = collect(m')
                result_ = collect(result')
                @test isapprox(tester.f(m_; dims=:rows, tester.kwargs...), result_)
                @test isapprox(tester.f!(m_; dims=:rows, tester.kwargs...), result_)
            end

            @testset "No missing" begin
                # Test having no missing data
                b = collect(reshape(allowmissing(1.0:1.0:20.0), 5, 4))
                @test impute(b, tester.imp(; tester.kwargs...); dims=:cols) == b
            end

            @testset "All missing" begin
                # Test having only missing data
                c = missings(5, 2)
                @test isequal(impute(c, tester.imp(; tester.kwargs...); dims=:cols), c)
                c_ = tester.f!(deepcopy(c); dims=:cols)
                @test isequal(c_, c)
            end
        end
        # Internal `svd` call isn't supported by these type, but maybe they should be?
        # test_axisarray(tester)
        # test_nameddimsarray(tester)
        # test_keyedarray(tester)
    end

    # Test a case where we expect SVD to perform well (e.g., many variables, )
    @testset "Data match" begin
        data = mapreduce(hcat, 1:1000) do i
            seeds = [sin(i), cos(i), tan(i), atan(i)]
            mapreduce(vcat, combinations(seeds)) do args
                [
                    +(args...),
                    *(args...),
                    +(args...) * 100,
                    +(abs.(args)...),
                    (+(args...) * 10) ^ 2,
                    (+(abs.(args)...) * 10) ^ 2,
                    log(+(abs.(args)...) * 100),
                    +(args...) * 100 + rand(-10:0.1:10),
                ]
            end
        end

        # println(svd(data').S)
        X = add_missings(data')

        svd_imputed = Impute.svd(X; dims=:cols)
        mean_imputed = Impute.fill(copy(X); dims=:cols)

        # With sufficient correlation between the variables and enough observation we
        # expect the svd imputation to perform severl times better than mean imputation.
        @test nrmsd(svd_imputed, data') < nrmsd(mean_imputed, data') * 0.5
    end

    # Test a case where we know SVD imputation won't perform well
    # (e.g., only a few variables, only )
    @testset "Data mismatch - too few variables" begin
        data = Tables.matrix(Impute.dataset("test/table/electricity"))
        X = add_missings(data)

        svd_imputed = Impute.svd(X; dims=:cols)
        mean_imputed = Impute.fill(copy(X); dims=:cols)

        # If we don't have enough variables then SVD imputation will probably perform
        # about as well as mean imputation.
        @test nrmsd(svd_imputed, data) > nrmsd(mean_imputed, data) * 0.9
    end

    @testset "Data mismatch - poor low rank approximations" begin
        M = rand(100, 200)
        data = M * M'
        X = add_missings(data)

        svd_imputed = Impute.svd(X; dims=:cols)
        mean_imputed = Impute.fill(copy(X); dims=:cols)

        # If most of the variance in the original data can't be explained by a small
        # subset of the eigen values in the svd decomposition then our low rank approximations
        # won't perform very well.
        @test nrmsd(svd_imputed, data) > nrmsd(mean_imputed, data) * 0.9
    end
end


text:
# accessors
function stage_reports end
function recruit_reports end

module StatsData

using JSON
using HTTP
using DataFrames

using AKCalc

abstract type Source end

struct SourceInMem <: Source
  stage_reports::DataFrame
  recruit_reports::DataFrame
end

stage_reports(pd::SourceInMem) = pd.stage_reports::DataFrame
recruit_reports(pd::SourceInMem) = pd.recruit_reports::DataFrame

end


text:
function aliquotclassifier{T<:Integer}(n::T)
    a = T[n]
    b = divisorsum(a[end])
    len = 1
    while len < 17 && !(b in a) && 0 < b && b < 2^47+1
        push!(a, b)
        b = divisorsum(a[end])
        len += 1
    end
    if b in a
        1 < len || return ("Perfect", a)
        if b == a[1]
            2 < len || return ("Amicable", a)
            return ("Sociable", a)
        elseif b == a[end]
            return ("Aspiring", a)
        else
            return ("Cyclic", push!(a, b))
        end
    end
    push!(a, b)
    b != 0 || return ("Terminating", a)
    return ("Non-terminating", a)
end

max_stars_repo_name: ethansaxenian/RosettaDecode

max_issues_repo_name: ethansaxenian/RosettaDecode

max_forks_repo_name: ethansaxenian/RosettaDecode


text:
# ---
# jupyter:
#   jupytext:
#     formats: ipynb,jl:hydrogen
#     text_representation:
#       extension: .jl
#       format_name: hydrogen
#       format_version: '1.3'
#       jupytext_version: 1.11.2
#   kernelspec:
#     display_name: Julia 1.8.0-DEV
#     language: julia
#     name: julia-1.8
# ---

# %%
using PolynomialRoots
p = [1; -1; fill(0, 98); 1]
a = roots(p)
@show [(k, round(real(sum(a .^ k)); digits=6)) for k in 1:100];

# %%

max_stars_repo_path: 0015/PolynomialRoots example.jl

max_stars_repo_name: genkuroki/public

max_issues_repo_path: 0015/PolynomialRoots example.jl

max_issues_repo_name: genkuroki/public

max_forks_repo_path: 0015/PolynomialRoots example.jl

max_forks_repo_name: genkuroki/public


text:
using KernelAbstractions
using ClimateMachine.MPIStateArrays: array_device, weightedsum
using KernelAbstractions.Extras: @unroll

function entropy_integral(dg, entropy, state_prognostic)
    balance_law = dg.balance_law
    state_auxiliary = dg.state_auxiliary
    device = array_device(state_prognostic)
    grid = dg.grid
    topology = grid.topology
    Np = dofs_per_element(grid)
    dim = dimensionality(grid)
    # XXX: Needs updating for multiple polynomial orders
    N = polynomialorders(grid)
    # Currently only support single polynomial order
    @assert all(N[1] .== N)
    N = N[1]

    realelems = topology.realelems

    event = Event(device)
    event = esdg_compute_entropy!(device, min(Np, 1024))(
        balance_law,
        Val(dim),
        Val(N),
        entropy.data,
        state_prognostic.data,
        state_auxiliary.data,
        realelems,
        ndrange = Np * length(realelems),
        dependencies = event,
    )
    wait(event)

    weightedsum(entropy)
end

@kernel function esdg_compute_entropy!(
    balance_law::BalanceLaw,
    ::Val{dim},
    ::Val{N},
    entropy,
    state_prognostic,
    state_auxiliary,
    elems,
) where {dim, N}

    FT = eltype(state_prognostic)
    num_state_prognostic = number_states(balance_law, Prognostic())
    num_state_auxiliary = number_states(balance_law, Auxiliary())

    Nq = N + 1

    Nqk = dim == 2 ? 1 : Nq

    Np = Nq * Nq * Nqk

    local_state_prognostic = MArray{Tuple{num_state_prognostic}, FT}(undef)
    local_state_auxiliary = MArray{Tuple{num_state_auxiliary}, FT}(undef)

    I = @index(Global, Linear)
    eI = (I - 1) ÷ Np + 1
    n = (I - 1) % Np + 1

    @inbounds begin
        e = elems[eI]
        @unroll for s in 1:num_state_prognostic
            local_state_prognostic[s] = state_prognostic[n, s, e]
        end

        @unroll for s in 1:num_state_auxiliary
            local_state_auxiliary[s] = state_auxiliary[n, s, e]
        end

        entropy[n, 1, e] = state_to_entropy(
            balance_law,
            Vars{vars_state(balance_law, Prognostic(), FT)}(
                local_state_prognostic,
            ),
            Vars{vars_state(balance_law, Auxiliary(), FT)}(
                local_state_auxiliary,
            ),
        )
    end
end

function entropy_product(dg, entropy, state_prognostic, tendency)
    balance_law = dg.balance_law
    state_auxiliary = dg.state_auxiliary
    device = array_device(state_prognostic)
    grid = dg.grid
    topology = grid.topology
    Np = dofs_per_element(grid)
    dim = dimensionality(grid)
    # XXX: Needs updating for multiple polynomial orders
    N = polynomialorders(grid)
    # Currently only support single polynomial order
    @assert all(N[1] .== N)
    N = N[1]

    realelems = topology.realelems

    event = Event(device)
    event = esdg_compute_entropy_product!(device, min(Np, 1024))(
        balance_law,
        Val(dim),
        Val(N),
        entropy.data,
        state_prognostic.data,
        tendency.data,
        state_auxiliary.data,
        realelems,
        ndrange = Np * length(realelems),
        dependencies = event,
    )
    wait(event)

    weightedsum(entropy)
end

@kernel function esdg_compute_entropy_product!(
    balance_law::BalanceLaw,
    ::Val{dim},
    ::Val{N},
    entropy,
    state_prognostic,
    tendency,
    state_auxiliary,
    elems,
) where {dim, N}

    FT = eltype(state_prognostic)
    num_state_prognostic = number_states(balance_law, Prognostic())
    num_state_entropy = number_states(balance_law, Entropy())
    num_state_auxiliary = number_states(balance_law, Auxiliary())

    Nq = N + 1

    Nqk = dim == 2 ? 1 : Nq

    Np = Nq * Nq * Nqk

    local_state_entropy = MArray{Tuple{num_state_entropy}, FT}(undef)
    local_state_prognostic = MArray{Tuple{num_state_prognostic}, FT}(undef)
    local_tendency = MArray{Tuple{num_state_prognostic}, FT}(undef)
    local_state_auxiliary = MArray{Tuple{num_state_auxiliary}, FT}(undef)

    I = @index(Global, Linear)
    eI = (I - 1) ÷ Np + 1
    n = (I - 1) % Np + 1

    @inbounds begin
        e = elems[eI]

        @unroll for s in 1:num_state_prognostic
            local_state_prognostic[s] = state_prognostic[n, s, e]
        end

        @unroll for s in 1:num_state_prognostic
            local_tendency[s] = tendency[n, s, e]
        end

        @unroll for s in 1:num_state_auxiliary
            local_state_auxiliary[s] = state_auxiliary[n, s, e]
        end

        state_to_entropy_variables!(
            balance_law,
            Vars{vars_state(balance_law, Entropy(), FT)}(local_state_entropy),
            Vars{vars_state(balance_law, Prognostic(), FT)}(
                local_state_prognostic,
            ),
            Vars{vars_state(balance_law, Auxiliary(), FT)}(
                local_state_auxiliary,
            ),
        )

        local_product = -zero(FT)
        # not that tendency related to the last entropy variable is assumed zero
        @unroll for s in 1:num_state_prognostic
            local_product += local_state_entropy[s] * local_tendency[s]
        end
        entropy[n, 1, e] = local_product
    end
end


text:
using HypertextLiteral

"""
    @bind width ScreenWidthGrabber()

Detect screen width from pluto notebook.
(see https://discourse.julialang.org/t/cell-width-in-pluto-notebook/49761/3)
"""
struct ScreenWidthGrabber end

function Base.show(io::IO, m::MIME"text/html", ::ScreenWidthGrabber)
    show(io,m, @htl("""
	<div>
	<script>
		var div = currentScript.parentElement
		div.value = screen.width
	</script>
	</div>
    """))
end

"""
    PlutoCellWidener(px)

Set pluto cell width to a given number of pixels.

(see https://discourse.julialang.org/t/cell-width-in-pluto-notebook/49761/3)

Setting width larger than standard may interfer with other Pluto functionality.
"""
struct PlutoCellWidener
	width
end

function Base.show(io::IO, m::MIME"text/html", widener::PlutoCellWidener)
    show(io,m, @htl("""
		<style>
			pluto-notebook {
				margin: auto;
				width: $(widener.width)px;
			}
		</style>
	    """)
	 )
end


text:
using Reexport
@reexport using Plots

_ignore_syms = (:eval, Symbol("#eval"), :include, Symbol("#include"), :Plots)

for sym in names(Plots, all = true)
    if !(sym in _ignore_syms)
        @eval import Plots.$sym
    end
end

using Plots.PlotMeasures
import Plots.PlotMeasures: Length, AbsoluteLength, Measure, width, height

# problem: https://github.com/tbreloff/Plots.jl/issues/308
# solution: hack from @stevengj: https://github.com/stevengj/PyPlot.jl/pull/223#issuecomment-229747768
otherdisplays = splice!(Base.Multimedia.displays, 2:length(Base.Multimedia.displays))
import PyPlot, PyCall
export PyPlot, PyCall
import LaTeXStrings: latexstring
append!(Base.Multimedia.displays, otherdisplays)

pycolors = PyPlot.pyimport("matplotlib.colors")
pypath = PyPlot.pyimport("matplotlib.path")
mplot3d = PyPlot.pyimport("mpl_toolkits.mplot3d")
pypatches = PyPlot.pyimport("matplotlib.patches")
pyfont = PyPlot.pyimport("matplotlib.font_manager")
pyticker = PyPlot.pyimport("matplotlib.ticker")
pycmap = PyPlot.pyimport("matplotlib.cm")
pynp = PyPlot.pyimport("numpy")
pynp["seterr"](invalid="ignore")
pytransforms = PyPlot.pyimport("matplotlib.transforms")
pycollections = PyPlot.pyimport("matplotlib.collections")
pyart3d = PyPlot.art3D

# "support" matplotlib v1.5
set_facecolor_sym = if PyPlot.version < v"2"
    warn("You are using Matplotlib $(PyPlot.version), which is no longer officialy supported by the Plots community. To ensure smooth Plots.jl integration update your Matplotlib library to a version >= 2.0.0")
    :set_axis_bgcolor
else
    :set_facecolor
end

# we don't want every command to update the figure
PyPlot.ioff()

_attr[:pyplot] = merge_with_base_supported([
    :annotations,
    :background_color_legend, :background_color_inside, :background_color_outside,
    :foreground_color_grid, :foreground_color_legend, :foreground_color_title,
    :foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
    :label,
    :linecolor, :linestyle, :linewidth, :linealpha,
    :markershape, :markercolor, :markersize, :markeralpha,
    :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
    :fillrange, :fillcolor, :fillalpha,
    :bins, :bar_width, :bar_edges, :bar_position,
    :title, :title_location, :titlefont,
    :window_title,
    :guide, :lims, :ticks, :scale, :flip, :rotation,
    :titlefontfamily, :titlefontsize, :titlefontcolor,
    :legendfontfamily, :legendfontsize, :legendfontcolor,
    :tickfontfamily, :tickfontsize, :tickfontcolor,
    :guidefontfamily, :guidefontsize, :guidefontcolor,
    :grid, :gridalpha, :gridstyle, :gridlinewidth,
    :legend, :legendtitle, :colorbar,
    :marker_z, :line_z, :fill_z,
    :levels,
    :ribbon, :quiver, :arrow,
    :orientation,
    :overwrite_figure,
    :polar,
    :normalize, :weights,
    :contours, :aspect_ratio,
    :match_dimensions,
    :clims,
    :inset_subplots,
    :dpi,
    :colorbar_title,
    :stride,
    :framestyle,
    :tick_direction,
    :camera,
    :contour_labels,
  ])
_seriestype[:pyplot] = [
        :path, :steppre, :steppost, :shape, :straightline,
        :scatter, :hexbin, #:histogram2d, :histogram,
        # :bar,
        :heatmap, :pie, :image,
        :contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
    ]
_style[:pyplot] = [:auto, :solid, :dash, :dot, :dashdot]
_marker[:pyplot] = vcat(_allMarkers, :pixel)
_scale[:pyplot] = [:identity, :ln, :log2, :log10]
is_marker_supported(::PyPlotBackend, shape::Shape) = true


text:
abstract type AbstractDeviceArray{T, N} <: AbstractArray{T, N} end

Base.IndexStyle(::AbstractDeviceArray) = IndexLinear()

@inline function Base.iterate(A::AbstractDeviceArray, i=1)
    if (i % UInt) - 1 < length(A)
        (@inbounds A[i], i + 1)
    else
        nothing
    end
end

function Base.sum(A::AbstractDeviceArray{T}) where T
    acc = zero(T)
    for elem in A
        acc += elem
    end
    acc
end

const shmem_counter = Ref{Int}(0)

"""
Creates a local static memory shared inside one block.
Equivalent to `__local` of OpenCL or `__shared__ (<variable>)` of CUDA.
"""
macro LocalMemory(state, T, N)
    id = (shmem_counter[] += 1)
    quote
        lémem = LocalMemory($(esc(state)), $(esc(T)), Val($(esc(N))), Val($id))
        AbstractDeviceArray(lémem, $(esc(N)))
    end
end

export @LocalMemory

"""
Creates a block local array pointer with `T` being the element type
and `N` the length. Both T and N need to be static! C is a counter for
approriately get the correct Local mem id in CUDAnative.
This is an internal method which needs to be overloaded by the GPU Array backends
"""
function LocalMemory(state, ::Type{T}, ::Val{N}, ::Val{C}) where {N, T, C}
    error("Not implemented")
end


text:
# This file is part of JuliaFMI.
# Licensed under MIT: https://github.com/AnHeuermann/JuliaFMI/blob/master/LICENSE.txt

# This file contains functionalities to test FMI Cross-Check
# See fmi-cross-check on Github for more details: https://github.com/modelica/fmi-cross-check

"""
Run tests from fmi-cross-check Git repository.
"""

using Test
using LibGit2
using DataFrames

thisDir = dirname(Base.source_path())
fmiCrossCheckDir = joinpath(thisDir,"fmi-cross-check")

include("$(dirname(thisDir))/src/FMUSimulator.jl")

if Sys.iswindows()
    fmiCrossCheckFMUDir = joinpath(thisDir, "fmi-cross-check", "fmus", "2.0", "me", "win$(Sys.WORD_SIZE)")
elseif Sys.islinux()
    fmiCrossCheckFMUDir = joinpath(thisDir, "fmi-cross-check", "fmus", "2.0", "me", "linux$(Sys.WORD_SIZE)")
elseif Sys.isapple()
    fmiCrossCheckFMUDir = joinpath(thisDir, "fmi-cross-check", "fmus", "2.0", "me", "darwin$(Sys.WORD_SIZE)")
else
    error("OS not supported for this tests.")
end

"""
    updateFMICrossTest()

Clone or fetch and pull modelica/fmi-cross-check repository from
https://github.com/modelica/fmi-cross-check.git.
"""
function updateFmiCrossCheck()

    if isdir(fmiCrossCheckDir)
        # Update repository
        println("Updating repository modelica/fmi-cross-check.")
        repo = GitRepo(fmiCrossCheckDir)
        LibGit2.fetch(repo)
        LibGit2.merge!(repo, fastforward=true)

    else
        # Clone repository
        println("Cloning repository modelica/fmi-cross-check.")
        println("This may take some minutes.")
        LibGit2.clone("https://github.com/modelica/fmi-cross-check.git", fmiCrossCheckDir )
        println("Cloned modelica/fmi-cross-check successfully.")
    end
end

"""
    cleanFmiCrossCheck()

Resets fmi-cross-check repository. All changes will get lost!
"""
function cleanFmiCrossCheck()

    if isdir(fmiCrossCheckDir)
        repo = GitRepo(fmiCrossCheckDir)
        head_oid = LibGit2.head_oid(repo)
        mode = LibGit2.Consts.RESET_HARD
        LibGit2.reset!(repo, head_oid, mode)
    end
end

"""
    runFMICrossTests()

Run and test all fmi-cross-test that are supportet on current system.
"""
function runFMICrossTests()
    # Check if repository is up to date
    updateFmiCrossCheck()

    # Collect all tests for current system
    df = DataFrame(toolName=String[], version=String[], test=String[], isCompliant=Bool[], pathToTestFMU=String[])

    for (root, dirs, files) in walkdir(fmiCrossCheckFMUDir)
        for dir in dirs
            fmuFiles = searchdir(joinpath(root,dir),r".fmu")
            if (length(fmuFiles) > 0)
                pathToFmu = joinpath(joinpath(root,dir),first(fmuFiles))

                toolName = basename(dirname(dirname(dirname((pathToFmu)))))
                version = basename(dirname(dirname(pathToFmu)))
                test = basename(first(splitext(pathToFmu)))

                # Check if test is compliant with latest fmi-cross-check rules
                isCompliant = !isfile(joinpath(dirname(pathToFmu),"notCompliantWithLatestRules"))

                # Add test to data frame
                push!(df, (toolName, version, test, isCompliant, pathToFmu))
            end
        end
    end

    # Run tests
    @testset "FMI Cross Check" begin
        for toolName in unique(df.toolName)
            @testset "$toolName" begin
                versions = unique(df[df.toolName.==toolName,:].version)
                tests = Array{String}(undef, length(versions), 10)          # ToDo: Replace 10 with maximum number of tests of all versions for current tool
                tests[:,:].=""
                compliances = Array{Bool}(undef, length(versions), 10)
                for (index,version) in enumerate(versions)
                    testOfVersion = df[(df.toolName.==toolName) .& (df.version.==version),:].test
                    compliance = df[(df.toolName.==toolName) .& (df.version.==version),:].isCompliant
                    tests[index,1:length(testOfVersion)] = testOfVersion
                    compliances[index,1:length(testOfVersion)] = compliance
                end

                testTool(toolName, versions, tests, compliances)
            end;
        end
    end;
end

"""
    searchdir(path,key)

Searches path for expression key and returns found files and folders.
"""
function searchdir(path,key)
    return filter(x->occursin(key,x), readdir(path))
end

"""
    function testTool(toolName::String, versions::Array{String,1}, tests)

Heler function to test for generic tools, versions and test cases.

#Example
```julia
julia> toolName = "CATIA"
julia> versions = ["R2015x", "R2016x"]
julia> tests = ["BooleanNetwork1" "ControlledTemperature" "CoupledClutches" "DFFREG" "Rectifier" "";
                "BooleanNetwork1" "ControlledTemperature" "CoupledClutches" "DFFREG" "MixtureGases" "Rectifier"]
julia> compliances = ["false" "false" "false" "false" "false" undef;
                     "true" "true" "true" "true" "true" "true"]
julia> testTool(toolName, versions, tests, compliances)
```
"""
function testTool(toolName::String, versions::Array{String,1}, tests, compliances)

    for (i,version) in enumerate(versions)
        @testset "$version" begin
            for (j,test) in enumerate(tests[i,:])
                if test != ""
                    model = joinpath(fmiCrossCheckFMUDir, "$toolName", "$version", "$test", "$test.fmu")
                    if compliances[i,j]
                        @test main(model)
                    else
                        try
                            main(model)
                        catch
                            @test_broken main(model)
                            continue
                        end
                        @test main(model)
                    end
                end
            end
        end;
    end
end

max_stars_repo_name: JKRT/JuliaFMI

max_issues_repo_name: JKRT/JuliaFMI

max_forks_repo_name: JKRT/JuliaFMI