text:
# MathOptInterface results
MOI.get(model::Optimizer, ::MOI.RawStatusString) = string(get_status(model.ctx))

function MOI.get(model::Optimizer, ::MOI.TerminationStatus)
    if model.number_solved == 0
        return MOI.OPTIMIZE_NOT_CALLED
    end
    (solver_code, model_code) = get_status(model.ctx)
    if solver_code == :Optimal
        if model_code == :OptimalGlobal
            return MOI.OPTIMAL
        elseif model_code == :OptimalLocal || model_code == :Feasible
            return MOI.LOCALLY_SOLVED
        elseif model_code == :Unbounded
            return MOI.INFEASIBLE_OR_UNBOUNDED
        elseif model_code == :InfeasibleGlobal
            return MOI.INFEASIBLE
        elseif model_code == :InfeasibleLocal
            return MOI.LOCALLY_INFEASIBLE
        # :InfeasibleIntermed should not match here
        elseif model_code == :Integer
            # This actually means that a feasible (integer) solution has been found
            return MOI.ALMOST_LOCALLY_SOLVED
        elseif model_code == :IntegerInfeasible
            return MOI.INFEASIBLE
        elseif model_code == :InfeasibleNoSolution
            return MOI.INFEASIBLE_OR_UNBOUNDED
        elseif model_code == :UnboundedNoSolution
            return MOI.INFEASIBLE_OR_UNBOUNDED
# DUAL_INFEASIBLE
        else
            MOI.OTHER_ERROR
        end
    # Solved to relaxed tolerances
# ALMOST_OPTIMAL
# ALMOST_INFEASIBLE
# ALMOST_DUAL_INFEASIBLE
# ALMOST_LOCALLY_SOLVED
    # Limits
    elseif solver_code == :IterationLimit
        return MOI.ITERATION_LIMIT
    elseif solver_code == :TimeLimit
        return MOI.TIME_LIMIT
# NODE_LIMIT
# SOLUTION_LIMIT
# MEMORY_LIMIT
# OBJECTIVE_LIMIT
# NORM_LIMIT
# OTHER_LIMIT
    # Problematic
    elseif solver_code == :SolverInterrupt
        # TODO Check that it is correct
        return MOI.SLOW_PROGRESS
    elseif solver_code == :EvalError
        return MOI.NUMERICAL_ERROR
# INVALID_MODEL
# INVALID_OPTION
    elseif solver_code == :UserInterrupt
        return MOI.INTERRUPTED
    elseif solver_code == :Capability
        gams_solver = ctx_get_solvername(m.reshop_ctx_dest)
        println("ReSHOP: GAMS solver $(gams_solver) cannot solve the specified problem")
        return MOI.OTHER_ERROR
    elseif solver_code in (:SystemError, :LicenseIssue, :SetupError)
        return MOI.OTHER_ERROR
    elseif solver_code == :SolverError && model_code == :ErrorNoSolution
        return MOI.OTHER_ERROR
    else
        println("ReSHOP: unhandle case: solver code is $(solver_code) and model code is $(model_code)")
        return MOI.OTHER_ERROR
    end
end

# TODO
function MOI.get(model::Optimizer, ::MOI.ResultCount)
    has_result = [MOI.OPTIMAL, MOI.LOCALLY_SOLVED, MOI.ALMOST_LOCALLY_SOLVED, MOI.ITERATION_LIMIT, MOI.TIME_LIMIT]
    if (MOI.get(model, MOI.TerminationStatus()) in has_result)
        return 1
    else
        return 0
    end
end

function MOI.get(model::Optimizer, ::MOI.PrimalStatus)
    if model.number_solved == 0
        return MOI.NO_SOLUTION
    end

    (solver_code, model_code) = get_status(model.ctx)
    if solver_code == :Optimal
        if model_code == :OptimalGlobal
            return MOI.FEASIBLE_POINT
        elseif model_code == :OptimalLocal || model_code == :Feasible
            return MOI.FEASIBLE_POINT
        elseif model_code == :Unbounded
            return MOI.NO_SOLUTION
        elseif model_code == :InfeasibleGlobal
            return MOI.INFEASIBLE_POINT
        elseif model_code == :InfeasibleLocal
            return MOI.INFEASIBLE_POINT
        # :InfeasibleIntermed should not match here
        elseif model_code == :Integer
            # This actually means that a feasible (integer) solution has been found
            return MOI.FEASIBLE_POINT
        elseif model_code == :IntegerInfeasible
            return MOI.INFEASIBLE_POINT
        elseif model_code == :InfeasibleNoSolution
            return MOI.INFEASIBLE_POINT
        elseif model_code == :UnboundedNoSolution
            return MOI.NO_SOLUTION
# DUAL_INFEASIBLE
        else
            MOI.UNKNOWN_RESULT_STATUS
        end
    # Solved to relaxed tolerances
# ALMOST_OPTIMAL
# ALMOST_INFEASIBLE
# ALMOST_DUAL_INFEASIBLE
# ALMOST_LOCALLY_SOLVED
    # Limits
    elseif solver_code == :IterationLimit
        return MOI.UNKNOWN_RESULT_STATUS
    elseif solver_code == :TimeLimit
        return MOI.UNKNOWN_RESULT_STATUS
# NODE_LIMIT
# SOLUTION_LIMIT
# MEMORY_LIMIT
# OBJECTIVE_LIMIT
# NORM_LIMIT
# OTHER_LIMIT
    # Problematic
    elseif solver_code == :SolverInterrupt
        # TODO Check that it is correct
        return MOI.UNKNOWN_RESULT_STATUS
    elseif solver_code == :EvalError
        return MOI.UNKNOWN_RESULT_STATUS
# INVALID_MODEL
# INVALID_OPTION
    elseif solver_code == :UserInterrupt
        return MOI.UNKNOWN_RESULT_STATUS
    elseif solver_code == :Capability
        gams_solver = ctx_get_solvername(m.reshop_ctx_dest)
        println("ReSHOP: GAMS solver $(gams_solver) cannot solve the specified problem")
        return MOI.UNKNOWN_RESULT_STATUS
    elseif solver_code in (:SystemError, :LicenseIssue, :SetupError)
        return MOI.UNKNOWN_RESULT_STATUS
    elseif solver_code == :SolverError && model_code == :ErrorNoSolution
        return MOI.NO_SOLUTION
    else
        println("ReSHOP: unhandle case: solver code is $(solver_code) and model code is $(model_code)")
        return MOI.UNKNOWN_RESULT_STATUS
    end
end

function MOI.get(model::Optimizer, ::MOI.DualStatus)
    if model.number_solved == 0
        return MOI.NO_SOLUTION
    end

    (solver_code, model_code) = get_status(model.ctx)
    if solver_code == :Optimal
        if model_code == :OptimalGlobal
            return MOI.FEASIBLE_POINT
        elseif model_code == :OptimalLocal || model_code == :Feasible
            return MOI.FEASIBLE_POINT
        elseif model_code == :Unbounded
            return MOI.FEASIBLE_POINT
        elseif model_code == :InfeasibleGlobal
            return MOI.NO_SOLUTION
        elseif model_code == :InfeasibleLocal
            return MOI.NO_SOLUTION
        # :InfeasibleIntermed should not match here
        elseif model_code == :Integer
            # This actually means that a feasible (integer) solution has been found
            return MOI.FEASIBLE_POINT
        elseif model_code == :IntegerInfeasible
            return MOI.NO_SOLUTION
        elseif model_code == :InfeasibleNoSolution
            return MOI.NO_SOLUTION
        elseif model_code == :UnboundedNoSolution
            return MOI.FEASIBLE_POINT
# DUAL_INFEASIBLE
        else
            MOI.UNKNOWN_RESULT_STATUS
        end
    # Solved to relaxed tolerances
# ALMOST_OPTIMAL
# ALMOST_INFEASIBLE
# ALMOST_DUAL_INFEASIBLE
# ALMOST_LOCALLY_SOLVED
    # Limits
    elseif solver_code == :IterationLimit
        return MOI.UNKNOWN_RESULT_STATUS
    elseif solver_code == :TimeLimit
        return MOI.UNKNOWN_RESULT_STATUS
# NODE_LIMIT
# SOLUTION_LIMIT
# MEMORY_LIMIT
# OBJECTIVE_LIMIT
# NORM_LIMIT
# OTHER_LIMIT
    # Problematic
    elseif solver_code == :SolverInterrupt
        # TODO Check that it is correct
        return MOI.UNKNOWN_RESULT_STATUS
    elseif solver_code == :EvalError
        return MOI.UNKNOWN_RESULT_STATUS
# INVALID_MODEL
# INVALID_OPTION
    elseif solver_code == :UserInterrupt
        return MOI.UNKNOWN_RESULT_STATUS
    elseif solver_code == :Capability
        gams_solver = ctx_get_solvername(m.reshop_ctx_dest)
        println("ReSHOP: GAMS solver $(gams_solver) cannot solve the specified problem")
        return MOI.UNKNOWN_RESULT_STATUS
    elseif solver_code in (:SystemError, :LicenseIssue, :SetupError)
        return MOI.UNKNOWN_RESULT_STATUS
    elseif solver_code == :SolverError && model_code == :ErrorNoSolution
        return MOI.NO_SOLUTION
    else
        println("ReSHOP: unhandle case: solver code is $(solver_code) and model code is $(model_code)")
        return MOI.UNKNOWN_RESULT_STATUS
    end
end

function MOI.get(model::Optimizer, ::S) where S <: MOI.ObjectiveValue
    if model.number_solved == 0
        error("ObjectiveValue not available.")
    end
    obj_equ = ctx_getobjequ(model.ctx)
    if is_valid_index(obj_equ)
        return ctx_getequval(model.ctx, obj_equ)
    else
        objvar = ctx_getobjvar(model.ctx)
        if is_valid_index(objvar)
            return ctx_getvarval(model.ctx, objvar)
        else
            error("No valid objective function of variable found")
        end
    end
end

function MOI.get(model::Optimizer,
                 ::MOI.VariablePrimal, vi::MOI.VariableIndex)
    if model.number_solved == 0
        error("VariablePrimal not available.")
    end
    check_inbounds(model, vi)
    return ctx_getvarval(model.ctx, vi.value-1)
end
function MOI.get(model::Optimizer,
                 ::MOI.VariablePrimal, vi::Vector{MOI.VariableIndex})
    if model.number_solved == 0
        error("VariablePrimal not available.")
    end
    return [ctx_getvarval(model.ctx, v.value-1) for v in vi]
end

macro checkcons(model, ci)
    quote
        if $(esc(model)).number_solved == 0
            error("Solve problem before accessing solution.")
        end
        if !(1 <= $(esc(ci)).value <= number_constraints($(esc(model))))
            error("Invalid constraint index ", $(esc(ci)).value)
        end
    end
end

##################################################
## ConstraintPrimal
## This is the value of the function at the current iterate
function _get_equval(ctx, eidx)
    return ctx_getequval(ctx, eidx)
end

function MOI.get(model::Optimizer, ::MOI.ConstraintPrimal,
                 ci::MOI.ConstraintIndex{S, T}) where {S <: SF, T <: SS}
    @checkcons(model, ci)
    return _get_equval(model.ctx, ci.value-1)
end

function MOI.get(model::Optimizer, ::MOI.ConstraintPrimal,
                 ci::MOI.ConstraintIndex{S, T}) where {S <: VAF, T <: Union{MOI.Nonnegatives, MOI.Nonpositives}}
    @checkcons(model, ci)
    return [_get_equval(model.ctx, eidx) - ctx_getrhs(model.ctx, eidx) for eidx in model.vaf_mapping[ci]]
end

function MOI.get(model::Optimizer, ::MOI.ConstraintPrimal,
                 ci::MOI.ConstraintIndex{S, T}) where {S <: VOV, T <: Union{MOI.Nonnegatives, MOI.Nonpositives}}
    @checkcons(model, ci)
    return [ctx_getvarval(model.ctx, vidx) for vidx in model.vaf_mapping[ci]]
end

# delete
function MOI.get(model::Optimizer, ::MOI.ConstraintPrimal,
                 ci::MOI.ConstraintIndex{S, T}) where {S <: VAF, T <: MOI.Zeros}
    @checkcons(model, ci)
    ncons = length(model.vaf_mapping[ci])
    return zeros(ncons)
end

function MOI.get(model::Optimizer, ::MOI.ConstraintPrimal,
                 ci::MOI.ConstraintIndex{S, T}) where {S <: VOV, T <: MOI.Zeros}
    @checkcons(model, ci)
    ncons = length(model.vov_mapping[ci])
    return zeros(ncons)
end

#function MOI.get(model::Optimizer, ::MOI.ConstraintPrimal,
#                 ci::MOI.ConstraintIndex{S, T}) where {S <: Union{VAF, VOV}, T <: MOI.SecondOrderCone}
#    @checkcons(model, ci)
#    x = get_solution(model.inner)
#    index = model.constraint_mapping[ci] .+ 1
#    return x[index]
#end

function MOI.get(model::Optimizer, ::MOI.ConstraintPrimal,
                 ci::MOI.ConstraintIndex{MOI.SingleVariable, S}) where S <: SS
    if model.number_solved == 0
        error("ConstraintPrimal not available.")
    end
    return ctx_getvarval(model.ctx, ci.value-1)
end

function MOI.get(model::Optimizer, ::MOI.ConstraintPrimal,
                 ci::Vector{MOI.ConstraintIndex{MOI.SingleVariable, S}}) where S <: SS
    if model.number_solved == 0
        error("ConstraintPrimal not available.")
    end
    return [ctx_getvarval(model.ctx, c.value-1) for c in ci]
end

function MOI.get(model::Optimizer, ::MOI.ConstraintPrimal, vi::MOI.VariableIndex)
    check_inbounds(model, vi)
    return ctx_getvarval(model.ctx, vi.value-1)
end

##################################################
## ConstraintDual
#

sense_to_sign(model::Optimizer) = model.sense == MOI.MAX_SENSE ? -1. : 1.

function MOI.get(model::Optimizer, ::MOI.ConstraintDual,
                 ci::MOI.ConstraintIndex{S, T}) where {S <: SF, T <: LS}
    @checkcons(model, ci)
    return sense_to_sign(model) * ctx_getequmult(model.ctx, ci.value-1)
end

function MOI.get(model::Optimizer, ::MOI.ConstraintDual,
                 ci::MOI.ConstraintIndex{S, T}) where {S <: VAF, T <: VLS}
    @checkcons(model, ci)
    return sense_to_sign(model) * [ctx_getequmult(model.ctx, eidx) for eidx in model.vaf_mapping[ci]]
end

function MOI.get(model::Optimizer, ::MOI.ConstraintDual,
                 ci::MOI.ConstraintIndex{S, T}) where {S <: VOV, T <: VLS}
    @checkcons(model, ci)
    return sense_to_sign(model) * [ctx_getvarmult(model.ctx, vidx) for vidx in model.vov_mapping[ci]]
end

function MOI.get(model::Optimizer, ::MOI.ConstraintDual, vi::MOI.VariableIndex)
    check_inbounds(model, vi)
    return sense_to_sign(model) * ctx_getvarmult(model.ctx, vi.value-1)
end

###
# Get constraint of a SOC constraint.
#
# Use the following mathematical property.  Let
#
#   ||u_i || <= t_i      with dual constraint    || z_i || <= w_i
#
# At optimality, we have
#
#   w_i * u_i  = - t_i z_i
#
###
## TODO investigate
#function MOI.get(model::Optimizer, ::MOI.ConstraintDual,
#                 ci::MOI.ConstraintIndex{S, T}) where {S <: Union{VAF, VOV}, T <: MOI.SecondOrderCone}
#    @checkcons(model, ci)
#    index_var = model.constraint_mapping[ci] .+ 1
#    index_con = ci.value
#    x =  get_solution(model.inner)[index_var]
#    # By construction.
#    t_i = x[1]
#    u_i = x[2:end]
#    w_i = get_dual(model.inner)[index_con]
#
#    dual = [-w_i; 1/t_i * w_i * u_i]
#
#    return sense_to_sign(model) * dual
#end

## Reduced costs.
function MOI.get(model::Optimizer, ::MOI.ConstraintDual,
                 ci::MOI.ConstraintIndex{MOI.SingleVariable, <: SS})
    if model.number_solved == 0
        error("ConstraintDual not available.")
    end
    if is_constraint_active(model.ctx, ci)
        return sense_to_sign(model) * ctx_getvarmult(model.ctx, ci.value-1)
    else
        return 0.
    end
end

function MOI.get(model::Optimizer, ::MOI.NLPBlockDual)
    if model.number_solved == 0
        error("NLPBlockDual not available.")
    end
    return [sense_to_sign(model) * ctx_getequmult(model.ctx, eidx) for eidx in model.start_nl_cons:model.start_nl_cons+model.len_nl_cons]
end

###
# TODO
# MOI.get(model::Optimizer, ::MOI.SolveTime) = TODO
# Additional getters
#MOI.get(model::Optimizer, ::MOI.NodeCount)  = TODO
#MOI.get(model::Optimizer, ::MOI.BarrierIterations)  = TODO
#MOI.get(model::Optimizer, ::MOI.RelativeGap)  = TODO
#MOI.get(model::Optimizer, ::MOI.ObjectiveBound)  = TODO


text:
let
    parser = OBOParser("$testdir/data/go.obo")
    @test parser.filepath == "$testdir/data/go.obo"
    @test parser.version == "1.2"
    @test parser.headertags["data-version"] == "releases/2014-08-09"
    @test parser.headertags["auto-generated-by"] == "TermGenie 1.0"
end

let
    parser = OBOParser("$testdir/data/go.obo")
    terms = Array(Term, 0)
    typedefs = Array(Typedef, 0)

    for term in eachterm(parser)
        push!(terms, term)
    end

    for typedef in eachtypedef(parser)
        push!(typedefs, typedef)
    end

    # NOTE: the equality of two terms is defined by their GO id only
    @test isequal(terms[1], Term("GO:0000001", "mitochondrion inheritance"))
    @test terms[1] == Term("GO:0000001", "mitochondrion inheritance")
    @test terms[2] == Term("GO:0000002", "mitochondrial genome maintenance")
    @test terms[4] == Term("GO:0000005", "ribosomal chaperone activity")
    # ...
    @test terms[end] == Term("GO:0000086", "G2/M transition of mitotic cell cycle")

    # is_obsolete flag
    @test !isobsolete(terms[2])
    @test isobsolete(terms[4])

    # [Term]
    # id: GO:0000001
    # name: mitochondrion inheritance
    # namespace: biological_process
    # def: "The distribution of mitochondria, including the mitochondrial genome, into daughter cells after mitosis or meiosis, mediated by interactions between mitochondria and the cytoskeleton." [GOC:mcc, PMID:10873824, PMID:11389764]
    # synonym: "mitochondrial inheritance" EXACT []
    # is_a: GO:0048308 ! organelle inheritance
    # is_a: GO:0048311 ! mitochondrion distribution
    @test is(terms[1].namespace, biological_process)
    @test terms[1].def == "The distribution of mitochondria, including the mitochondrial genome, into daughter cells after mitosis or meiosis, mediated by interactions between mitochondria and the cytoskeleton."

    # [Typedef]
    # id: ends_during
    # name: ends_during
    # namespace: external
    # xref: RO:0002093
    @test typedefs[1] == Typedef("ends_during", "ends_during", "external", "RO:0002093")
    @test typedefs[end] == Typedef("happens_during", "happens_during", "external", "RO:0002092")
end

ext: jl


text:
export basemeasure

"""
    basemeasure(μ)

"""
function basemeasure end

include("basemeasures/trivial.jl")
include("basemeasures/lebesgue.jl")

export isprimitive

"""
    isprimitive(μ)

Most measures are defined in terms of other measures, for example using a
density or a pushforward. Those that are not are considered (in this library,
it's not a general measure theory thing) to be _primitive_. The canonical
example of a primitive measure is `Lebesgue{X}()` for some `X`.

The default method is
    isprimitive(μ) = false

So when adding a new primitive measure, it's necessary to add a method for its type
that returns `true`.
"""
isprimitive(μ) = false


text:
# I'm going to reshape the mesh to allow for each localization testing!
include("../foamLia.jl")
using foamLia
baseCase = "/users/a/r/areagan/OpenFOAM/areagan-2.2.1/run/juliabase"

caseFolder = "/users/a/r/areagan/scratch/run/reshapeMesh-deleteme"
case = OpenFoam(caseFolder)
# initCase(case,baseCase)

function reshapeMeshPoints(case)
    # given an openfoam case
    # generate two matrices
    # one to store the x,y location of each point on the flattened loop
    # and another, that shape, referencing the point
    # the latter is more compact

    # don't need the result, but I do want to read the mesh
    readMesh(case)

    x = case.meshParameters["x"]*4
    y = case.meshParameters["y"]+2*case.meshParameters["refinements"]
    points = zeros(Int64,x,y)
    indices = zeros(Int64,x*y,2)

    # println("the mesh is $(x) by $(y)")

    theta = [y for y in 2*pi/x/2:2*pi/x:2*pi-2*pi/x/2]
    # println(size(theta))
    # println(theta[1:10])
    # println(theta[end-10:end])

    # println(size(case.fullMesh["cellCenters"]))
    # println(case.fullMesh["cellCenters"][:,1])

    TOL = 1e-3
    # println(size(case.fullMesh["cellCenters"])[2])
    # println(case.fullMesh["cellCenters"][:,size(case.fullMesh["cellCenters"])[2]])
    for i in 1:size(case.fullMesh["cellCenters"])[2]
        # println("i is $(i)")
        # start them at the right side, go counter clockwise
        # arctan(z/y) where y is right the right (adjacent),
        # z is up (opposite)
        th = atan2(case.fullMesh["cellCenters"][3,i],case.fullMesh["cellCenters"][2,i])
        # wrap around
        if th<0
            th = th+2*pi
        end
        r = sqrt(case.fullMesh["cellCenters"][3,i]^2+case.fullMesh["cellCenters"][2,i]^2)
        # println("th is $(th)")
        # println("r is $(r)")
        # find the index of theta
        j = 1
        # while abs(th-theta[j])>abs(th-theta[j+1]) > TOL
        while abs(th-theta[j]) > abs(th-theta[j+1]) # && j <= 999
            # println(j)
            # println(abs(th-theta[j]))
            j += 1
            # this can't be the best way
            if j == 1000
                break
            end
        end
        # println("j is $(j)")
        # println(points[j,:])
        k = 1
        while points[j,k] != 0.0
            k += 1
        end
        # println("k is $(k)")
        points[j,k] = i
        # (no point in filling indices until sorted)
    end
    # sneakily return a tuple (don't need parens)
    # since divided up the function, for memory
    points
end

function reshapeMeshIndices(case,points)
    # don't need the result, but I do want to read the mesh
    readMesh(case)

    x = case.meshParameters["x"]*4
    y = case.meshParameters["y"]+2*case.meshParameters["refinements"]
    indices = zeros(Int64,x*y,2)

    # now go and sort the points by their r value
    for i in 1:x
        ps = points[i,:]
        # go get the r for each of the points
        rs = [sqrt(case.fullMesh["cellCenters"][3,p]^2+case.fullMesh["cellCenters"][2,p]^2) for p in ps]
        perm = sortperm(rs)
        points[i,:] = ps[perm]
        j = 1
        for p in ps
            indices[p,:] = [i,j]
            j+=1
        end
    end

    # println(points[1:10,:])
    # println(indices[1:10,:])
    # println(indices[30001:30010,:])

    indices
end

# # usage:
# include("reshapeMesh.jl")
# points = reshapeMeshPoints(case)
# indices = reshapeMeshIndices(case,points)


text:
using Base.Broadcast: broadcasted
bstyle = Base.Broadcast.DefaultArrayStyle{1}()

@testset "broadcast" begin
    for (mr,br,sr) in ((OneToMRange(10),         OneTo(10),              OneToSRange(10)),
                       (UnitMRange(1, 10),       UnitRange(1, 10),       UnitSRange(1, 10)),
                       (StepMRange(1, 1, 10),    StepRange(1, 1, 10),    StepSRange(1, 1, 10)),
                       (LinMRange(1, 10, 10),    LinRange(1, 10, 10),    LinSRange(1, 10, 10)),
                       (StepMRangeLen(1., 1., 10), StepRangeLen(1., 1., 10), StepSRangeLen(1., 1., 10))
                      )
        @test broadcasted(bstyle, +, mr) == broadcast(+, br)
        @test broadcasted(bstyle, +, sr) == broadcast(+, br)

        @test broadcasted(bstyle, -, mr) ==  broadcast(-, br)
        @test broadcasted(bstyle, -, sr) ==  broadcast(-, br)

        @test broadcasted(bstyle, +, 1, mr) == broadcast(+, 1, br)
        @test broadcasted(bstyle, +, 1, sr) == broadcast(+, 1, br)

        @test broadcasted(bstyle, -, 1, mr) == broadcast(-, 1, br)
        @test broadcasted(bstyle, -, 1, sr) == broadcast(-, 1, br)

        @test broadcasted(bstyle, +, mr, 1) == broadcast(+, br, 1)
        @test broadcasted(bstyle, +, sr, 1) == broadcast(+, br, 1)

        @test broadcasted(bstyle, -, mr, 1)  == broadcast(-, br, 1)
        @test broadcasted(bstyle, -, sr, 1)  == broadcast(-, br, 1)

        @test broadcasted(bstyle, *, 1, mr) == broadcast(*, 1, br)
        @test broadcasted(bstyle, *, 1, sr) == broadcast(*, 1, br)

        @test broadcasted(bstyle, *, mr, 1) == broadcast(*, br, 1)
        @test broadcasted(bstyle, *, sr, 1) == broadcast(*, br, 1)

        @test broadcasted(bstyle, \, 1, mr) == broadcast(\, 1, br)
        @test broadcasted(bstyle, \, 1, sr) == broadcast(\, 1, br)

        @test /(mr, 2) == /(br, 2)
        @test /(sr, 2) == /(br, 2)

        @test -(mr, (2 .* br)) == -(br, (2 .* br))
        @test -(sr, (2 .* br)) == -(br, (2 .* br))
        @test -((2 .* br), mr) == -((2 .* br), br)
        @test -((2 .* br), sr) == -((2 .* br), br)

        @test +(mr, (1 .+ br)) == +(br, (1 .+ br))
        @test +(sr, (1 .+ br)) == +(br, (1 .+ br))
        @test +((1 .+ br), mr) == +((1 .+ br), br)
        @test +((1 .+ br), sr) == +((1 .+ br), br)

        @test *(mr, 2) == *(sr, 2) == *(br, 2)
        @test *(2, mr) == *(2, sr) == *(2, br)
    end
end