text:
using Wavelets
using Base.Test

print("util ...\n")

let
    @test dyadicdetailn(0) == 1
    @test dyadicdetailn(1) == 2
    @test dyadicdetailindex(0,1) == 2

    j = 5
    @test dyadicdetailn(j)+1 == dyadicdetailrange(j)[1]
    @test dyadicdetailindex(j,3) == dyadicdetailrange(j)[3]
    J = 7
    @test ndyadicscales(2^J) == J
    @test dyadicdetailn(maxdyadiclevel(2^J)) == 2^(J-1)
    @test ndyadicscales(rand(2^J)) == J
    @test maxdyadiclevel(rand(2^J)) == J-1

    @test tl2dyadiclevel(512,1) == 8
    @test dyadiclevel2tl(512,9) == 0
    @test tl2dyadiclevel(rand(2^9),1) == 8
    @test dyadiclevel2tl(rand(2^9),9) == 0

    n = 64
    L = 2
    @test maxtransformlevels(n) == ndyadicscales(n)
    @test maxtransformlevels(n) == maxmodwttransformlevels(n)
    @test detailindex(n, L, 3) == dyadicdetailindex(tl2dyadiclevel(n,L), 3)
    @test detailrange(n, L) == dyadicdetailrange(tl2dyadiclevel(n,L))
    @test detailn(n, L) == dyadicdetailn(tl2dyadiclevel(n,L))
end

# UTILITY FUNCTIONS
let
    @test mirror([1]) == [1]
    @test mirror([2,3]) == [2,-3]
    @test mirror([2,3,4]) == [2,-3,4]
    @test mirror([4.9,5,6,7]) == [4.9,-5,6,-7]

    @test upsample([1]) == [1,0]
    @test upsample([1],1) == [0,1]
    @test upsample([1,2]) == [1,0,2,0]
    @test upsample([1,2],1) == [0,1,0,2]
    @test downsample([1,2]) == [1]
    @test downsample([1,2],1) == [2]
    @test downsample([1,2,3,4]) == [1,3]
    @test downsample([1,2,3,4],1) == [2,4]

    @test iscube(rand(4)) == true
    @test iscube(rand(4,4,4,4)) == true
    @test iscube(rand(4,5)) == false
    @test isdyadic(rand(4)) == true
    @test isdyadic(rand(32,32)) == true
    @test isdyadic(rand(6)) == false

    n = 64
    @test wcount(randn(n)) == n
    @test wcount(rand(n),0) == n
    @test wcount(rand(n),1.01) == 0
    @test wcount(rand(n).-2.5,1.01) == n
    @test wcount([0,2,6,7,8],6.6) == 2
    @test wcount([10,-11,6,7,8.0,-5,-8,0],3,level=2) == 3
    @test wcount(randn(n,n)) == n*n
    @test wcount(rand(n,n),1.01) == 0
    @test wcount([-1 2;3 4],2.5) == 2

    n = 32
    x = randn(n)
    y = randn(n)
    for sh in (-43, -5, 0, 1, 28, 41)
        @test circshift(x,sh) == Util.circshift!(copy(x),sh)
        @test circshift(x,-sh) == Util.circshift!(copy(x),-sh)
        @test x == Util.circshift!(Util.circshift!(copy(x),sh),-sh)
        @test circshift(x,sh) == Util.circshift!(similar(x),x,sh)
        @test circshift(x,-sh) == Util.circshift!(similar(x),x,-sh)
    end

    a0 = [1,2]
    exp = [1,2]
    n = 2
    @test Util.split!(copy(a0)) == exp
    @test Util.split!(copy(a0), n, similar(a0)) == exp
    @test Util.split!(similar(a0), copy(a0), n) == exp
    @test Util.merge!(copy(a0)) == exp
    @test Util.merge!(copy(a0), n, similar(a0)) == exp
    @test Util.merge!(similar(a0), copy(a0), n) == exp

    a0 = [-1,2,3.3,4]
    exp = [-1,3.3,2,4]
    n = 4
    @test Util.split!(copy(a0)) == exp
    @test Util.split!(copy(a0), n, similar(a0)) == exp
    @test Util.split!(similar(a0), copy(a0), n) == exp
    a0 = [-1,3.3,2,4]
    exp = [-1,2,3.3,4]
    @test Util.merge!(copy(a0)) == exp
    @test Util.merge!(copy(a0), n, similar(a0)) == exp
    @test Util.merge!(similar(a0), copy(a0), n) == exp

    a0 = randn(128)
    @test a0 == Util.merge!(Util.split!(a0))

    # with strides
    a0 = [1,2,3,4,5,6,7,8,9,10,11,12,13,14]
    ia = 3
    inca = 2
    @test Util.split!(similar(a0), copy(a0), ia, inca, 2)[1:2] == [3,5]
    @test Util.split!(similar(a0), copy(a0), ia, inca, 4)[1:4] == [3,7,5,9]
    @test Util.split!(similar(a0), copy(a0), 1, 3, 4)[1:4] == [1,7,4,10]
    @test Util.split!(similar(a0), copy(a0), 1, 1, 8)[1:8] ==  Util.split!(similar(a0), copy(a0), 8)[1:8]

    @test Util.merge!(similar(a0), ia, inca, copy(a0), 2)[ia:inca:ia+(2-1)*inca] == [1,2]
    @test Util.merge!(similar(a0), ia, inca, copy(a0), 4)[ia:inca:ia+(4-1)*inca] == [1,3,2,4]
    ia = 1
    inca = 3
    @test Util.merge!(similar(a0), ia, inca, copy(a0), 4)[ia:inca:ia+(4-1)*inca] == [1,3,2,4]
    @test Util.merge!(similar(a0), 1, 1, copy(a0), 8)[1:8] ==  Util.merge!(similar(a0), copy(a0), 8)[1:8]

    n = 128
    x = randn(n)
    for L = 0:ndyadicscales(n)
        for st in (:full, :dwt)
            @test isvalidtree(x, maketree(n, L, st))
        end
    end
    tree = maketree(n, 4)
    tree[5] = false
    @test !isvalidtree(x, tree)
    tree = maketree(n, 4)
    tree[18] = true
    @test isvalidtree(x, tree)
    tree[7] = false
    @test !isvalidtree(x, tree)
    tree[7] = true
    tree[9] = false
    @test !isvalidtree(x, tree)

    EE = Exception
    @test_throws EE maketree(n, 4, :foo)

    makewavelet(wavelet(WT.db2, WT.Filter))
    for tf in ("Blocks", "HeaviSine")
        testfunction(8, tf)
    end
    EE = Exception
    @test_throws EE testfunction(8, "sksksks")
end


text:
@testset "_merge - AbstractDict" begin
    @testset "Simple" begin
        expected = Dict("a" => 1, "b" => 2)

        a = Dict("a" => 1)
        b = Dict("b" => 2)

        @test AWS._merge(a, b) == expected
    end

    @testset "Complex Different Nested Dict Keys" begin
        expected = Dict("common" => Dict("b" => 2))

        a = Dict("common" => Dict("a" => 1))
        b = Dict("common" => Dict("b" => 2))

        @test AWS._merge(a, b) == expected
    end

    @testset "Complex Same Nested Dict Keys" begin
        expected = Dict("common" => Dict("a" => 2))

        a1 = Dict("common" => Dict("a" => 1))
        a2 = Dict("common" => Dict("a" => 2))

        @test AWS._merge(a1, a2) == expected
    end
end

@testset "mergewith(_merge) - AbstractDict" begin
    @testset "Simple" begin
        expected = Dict("a" => 1, "b" => 2)

        a = Dict("a" => 1)
        b = Dict("b" => 2)

        @test mergewith(AWS._merge, a, b) == expected
    end

    @testset "Complex Differed Nested Dict Keys" begin
        expected = Dict("common" => Dict("a" => 1, "b" => 2))

        a = Dict("common" => Dict("a" => 1))
        b = Dict("common" => Dict("b" => 2))

        @test mergewith(AWS._merge, a, b) == expected
    end

    @testset "Complex Same Nested Dict Keys" begin
        expected = Dict("common" => Dict("a" => 2))

        a1 = Dict("common" => Dict("a" => 1))
        a2 = Dict("common" => Dict("a" => 2))

        @test mergewith(AWS._merge, a1, a2) == expected
    end
end

@testset "_merge - AbstractString" begin
    expected = "b"

    @test AWS._merge("a", "b") == expected
end

@testset "_assignment_to_kw!" begin
    @testset "non-expression" begin
        ex = :(true)
        @test_throws ArgumentError AWS._assignment_to_kw!(ex)
    end

    @testset "non-assignment" begin
        ex = :(a => true)
        @test_throws ArgumentError AWS._assignment_to_kw!(ex)
    end

    @testset "assignment" begin
        ex = :(a = true)
        @test AWS._assignment_to_kw!(ex) == Expr(:kw, :a, true)
        @test ex == Expr(:kw, :a, true)
    end
end

ext: jl


text:
## Loading packages.
using MicroHH
using PyPlot
using MicroHH.StencilBuilder
using LoopVectorization

## Loading settings.
include("drycbl_settings.jl")
settings[1]["timeloop"]["start_time"] = settings[1]["timeloop"]["end_time"]

## Initialize the model.
n_domains = 1
m = Model("drycbl", n_domains, settings, Float32)
load_model!(m)

## Compute the scalar dissipation
function calc_scalar_dissipation!(
    slngrad, s,
    dxi, dyi, dzi, dzhi,
    is, ie, js, je, ks, ke)

    @fast3d begin
        @fd (slngrad, s) slngrad = log(gradx(gradx(s))^2 + grady(grady(s))^2 + gradz(gradz(s))^2)
    end
end

f = m.fields[1]; g = m.grid[1]
slngrad = zeros(size(f.s))
calc_scalar_dissipation!(
    slngrad, f.s,
    g.dxi, g.dyi, g.dzi, g.dzhi,
    g.is, g.ie, g.js, g.je, g.ks, g.ke)

x = @view g.x[g.is:g.ie]
y = @view g.y[g.js:g.je]
z = @view g.z[g.ks:g.ke]
slngrad_xy = @view slngrad[g.is:g.ie, g.js:g.je, 6]
slngrad_xz = @view slngrad[g.is:g.ie, g.js, g.ks:g.ke]

smin = -25; smax = -11

figure()
#pcolormesh(x, y, slngrad_xy', vmin=smin, vmax=smax, shading="nearest", cmap=plt.cm.magma)
pcolormesh(x, y, slngrad_xy', shading="nearest", cmap=plt.cm.magma)
xlabel("x (m)")
ylabel("y (m)")
colorbar()
tight_layout()
display(gcf())

figure()
#pcolormesh(x, z, slngrad_xz', vmin=smin, vmax=smax, shading="nearest", cmap=plt.cm.magma)
pcolormesh(x, z, slngrad_xz', shading="nearest", cmap=plt.cm.magma)
xlabel("x (m)")
ylabel("z (m)")
colorbar()
tight_layout()
display(gcf())

show()


text:
# Boolean variables
locked = false
closed = true
let
    is_secure  = closed && locked
    println("secure:", is_secure)
    not_secure = !closed || !locked
    if not_secure
        print("not secure")
    end
end

max_stars_repo_name: elucian/julia

max_issues_repo_name: elucian/julia

max_forks_repo_name: elucian/julia


text:
import YAML
config = YAML.load(open("config.yaml"))

using DiskJockey.constants
using DiskJockey.model
using DiskJockey.visibilities
using DiskJockey.image
using DiskJockey.gridding
using HDF5

import PyPlot.plt
using LaTeXStrings

# Points at which to create images
parameters = readdlm("parameters_star.dat")

dv = DataVis(config["data_file"], true)[5]

# Conjugation is necessary for the SMA and ALMA
visibilities.conj!(dv)

qq = get_qq(dv)

# Find the indices that sort these in increasing order
ind = sortperm(qq)

qq = qq[ind]
uu = dv.uu[ind]
vv = dv.vv[ind]

fid = h5open("grid_VV.hdf5", "r")
# Load the sampled visibilities from HDF5
reals = read(fid, "real")
imags = read(fid, "imag")
VV = reals + imags .* im # Complex visibility

close(fid)

npoints = size(VV, 2)

mean_vis = mean(VV, 2)

# See how the real and imaginary values of the visibility change over the full range.

# Subtract the value

# Normalize the change in visibility from -1 to 1

# Write function that takes in a range, that shows the min to max of that value.
index = Float64[i for i=1:5]

lw = 0.3

function plot_range(range, fname)

  # index this subset of parameters
  pars = parameters[range, :]
  # print(pars)

  # select this subset of visibilities
  VV_sub = VV[:,range]

  println(size(VV_sub))

  fig, ax = plt[:subplots](nrows=3, figsize=(10, 9), sharex=true)

  # for i in rand(1:npoints, 1000)
  for i=1:npoints
    vis = squeeze(VV_sub[i, :], 1)

    ax[1][:plot](index, abs(vis), lw=lw)
    ax[2][:plot](index, real(vis), lw=lw)
    ax[3][:plot](index, imag(vis), lw=lw)
  end

  ax[3][:set_xlabel]("index")

  ax[1][:set_ylabel]("Mod")
  ax[2][:set_ylabel]("Real")
  ax[3][:set_ylabel]("Imag")

  plt[:savefig](fname, dpi=600)

end

plot_range(1:5, "visibilities_nu.png")
plot_range(6:10, "visibilities_mass.png")
plot_range(11:15, "visibilities_r_c.png")
plot_range(16:20, "visibilities_T_10.png")
plot_range(21:25, "visibilities_q.png")
plot_range(26:30, "visibilities_logM_gas.png")
plot_range(31:35, "visibilities_ksi.png")
plot_range(36:40, "visibilities_incl.png")
plot_range(41:45, "visibilities_PA.png")

quit()

max_stars_repo_name: iancze/DiskEmulator

max_issues_repo_name: iancze/DiskEmulator

max_forks_repo_name: iancze/DiskEmulator


text:
include("simulation.jl")

include("solution.jl")

include("plots.jl")


text:
module TestLiftMacro

using NullableUtils
using Base.Test

f(x::Int, y::Int) = x * y
x = Nullable(5)
y = Nullable(5)
z = Nullable{Int}()
u = Nullable('a')

@test isequal((@lift f(x, y) Int), Nullable(25))
a = @lift f(x, z) Int
@test a.isnull == true
@test typeof(a).parameters[1] == Int
@test_throws MethodError @lift f(x, Nullable(5)) Int
@test_throws MethodError @lift f(x, u) Int

e = NullableUtils._lift(:( f(x, y) ), Int)
@test e.args[2].head == :if
@test e.args[2].args[1].head == :||

end # module TestLiftMacro

max_stars_repo_name: davidagold/NullableUtils

max_issues_repo_name: davidagold/NullableUtils

max_forks_repo_name: davidagold/NullableUtils


text:
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/MortarContact2DAD.jl/blob/master/LICENSE

using MortarContact2DAD, Test

# Matching mesh, no gap between surfaces

X = Dict(
    1 => [0.0, 2.0],
    2 => [0.0, 0.0],
    3 => [0.0, 2.0],
    4 => [0.0, 0.0])

u = Dict(
    1 => [0.0, 0.0],
    2 => [0.0, 0.0],
    3 => [0.0, 0.0],
    4 => [0.0, 0.0])

slave = Element(Seg2, [1, 2])
master = Element(Seg2, [3, 4])
update!([slave, master], "geometry", X)
update!([slave, master], "displacement", u)
problem = Problem(Contact2DAD, "test problem", 2, "displacement")
problem.properties.rotate_normals = false
add_slave_elements!(problem, [slave])
add_master_elements!(problem, [master])
problem.assembly.u = zeros(8)
problem.assembly.la = zeros(8)
assemble!(problem, 0.0)

n = slave("normal", 0.0)
@test isapprox(n[1], n[2])
@test isapprox(n[1], [1.0, 0.0])

C1 = Matrix(sparse(problem.assembly.C1, 4, 8))
C2 = Matrix(sparse(problem.assembly.C2, 4, 8))
K = Matrix(sparse(problem.assembly.K, 4, 8))
D = Matrix(sparse(problem.assembly.D, 4, 8))
f = Vector(sparse(problem.assembly.f, 4, 1)[:])
g = Vector(sparse(problem.assembly.g, 4, 1)[:])

C1_expected = [
 1.0  0.0  0.0  0.0  -1.0   0.0   0.0   0.0
 0.0  1.0  0.0  0.0   0.0  -1.0   0.0   0.0
 0.0  0.0  1.0  0.0   0.0   0.0  -1.0   0.0
 0.0  0.0  0.0  1.0   0.0   0.0   0.0  -1.0]
C2_expected = [
 1.0  0.0  0.0  0.0  -1.0  0.0   0.0  0.0
 0.0  0.0  0.0  0.0   0.0  0.0   0.0  0.0
 0.0  0.0  1.0  0.0   0.0  0.0  -1.0  0.0
 0.0  0.0  0.0  0.0   0.0  0.0   0.0  0.0]
@test isapprox(C1, C1_expected)
@test isapprox(C2, C2_expected)
@test isapprox(K, zeros(4, 8))
@test isapprox(f, zeros(4))
@test isapprox(g, zeros(4))

empty!(problem.assembly)
problem.properties.iteration = 0
problem.properties.contact_state_in_first_iteration = :INACTIVE
problem.properties.print_summary = true
assemble!(problem, 0.0)
C2 = Matrix(sparse(problem.assembly.C2, 4, 8))
@test !isapprox(C2, C2_expected)

problem.assembly.u = zeros(0)
problem.assembly.la = zeros(0)
empty!(problem.assembly)
@test_throws Exception assemble!(problem, 0.0)

using MortarContact2DAD: get_slave_dofs, get_master_dofs
@test get_slave_dofs(problem) == [1, 2, 3, 4]
@test get_master_dofs(problem) == [5, 6, 7, 8]
@test_throws Exception add_elements!(problem, [slave])


text:
isdefined(Base, :__precompile__) && __precompile__()

module ConicIP

export Id, conicIP, pivot, preprocess_conicIP,
  ConicIPSolver, Block

import Base:+,*,-,\,^
using Base.LinAlg.BLAS:axpy!,scal!
using WoodburyMatrices

include("diag.jl")
include("blockmatrices.jl")
include("kktsolvers.jl")

ViewTypes   = Union{SubArray}
VectorTypes = Union{Matrix, Vector, ViewTypes}
MatrixTypes = Union{Matrix, Array{Real,2},
                    SparseMatrixCSC{Real,Integer}}

# returns 0 for matrices with dimension 0.
normsafe(x) = isempty(x) ? 0 : norm(x)

# ──────────────────────────────────────────────────────────────
#  3x1 block vector
# ──────────────────────────────────────────────────────────────

type v4x1; y::Matrix; w::Matrix; v::Matrix; s::Matrix; end

+(a::v4x1, b::v4x1) = v4x1(a.y + b.y, a.w + b.w, a.v + b.v, a.s + b.s)
-(a::v4x1, b::v4x1) = v4x1(a.y - b.y, a.w - b.w, a.v - b.v, a.s - b.s)
Base.norm(a::v4x1)  = norm(a.y) + normsafe(a.w) + normsafe(a.v) + normsafe(a.s)

function axpy4!(α::Number, x::v4x1, y::v4x1)
    axpy!(α, x.y, y.y); axpy!(α, x.w, y.w)
    axpy!(α, x.v, y.v); axpy!(α, x.s, y.s)
end

# ──────────────────────────────────────────────────────────────
#  Linear operator representing a congruence transform of a
#  matrix in vectorized form
# ──────────────────────────────────────────────────────────────

type VecCongurance; R :: Matrix; end

*(W::VecCongurance, x::VectorTypes)    = vecm(W.R'*mat(x)*W.R)
Base.ctranspose(W::VecCongurance)      = VecCongurance(W.R');
Base.inv(W::VecCongurance)             = VecCongurance(inv(W.R))
Base.size(W::VecCongurance, i)         = round(Int, size(W.R,1)*(size(W.R,1)+1)/2)
Base.Ac_mul_B(W1::VecCongurance, W2::VecCongurance) = VecCongurance(W2.R*W1.R')

function Base.full(W::VecCongurance)
  n = size(W,1)
  I = eye(n)
  Z = zeros(n,n)
  for i = 1:n
    Z[:,i] = W*I[:,i][:]
  end
  return Z
end

function Base.sparse(W::VecCongurance)
  return sparse(full(W))
end

ord(x) = begin; n = length(x); round(Int, (sqrt(1+8*n) - 1)/2); end

function mat(x)

  # inverse of vecm
  # > mat([1,2,3,4,5,6)]
  #  1    2/√2  3√2
  #  2    4     5√2
  #  3√2  5√2   6

  n = ord(x)
  Z = zeros(n,n)
  for i = 1:n
    k = round(Int, length(x) - (n-i+2)*(n-i+1)/2)
    for j = 1:n
      if i <= j
        if i == j
          Z[i,j] = x[k+j-i+1]
        else
          Z[i,j] = x[k+j-i+1]/√2
        end
      else
        Z[i,j] = Z[j,i];
      end
    end
  end
  return Z

end

function vecm(Z)

  # inverse of mat
  # > vecm([1 2 3; 2 4 5; 3 5 6])
  # [1 2√2 3√2 4 5√2 6]

  n = size(Z,1)
  x = zeros(round(Int, n*(n+1)/2),1)
  c = 1
  for i = 1:n
    for j = 1:n
      if i <= j
        if i == j
          x[c] = Z[i,j];
        else
          x[c] = Z[i,j]*√2;
        end
        c = c + 1;
      end
    end
  end
  return x

end

# ──────────────────────────────────────────────────────────────
#  Misc Helper Types/Functions
# ──────────────────────────────────────────────────────────────

# Example:  block_ranges([1,1,3]) = [1:1,2:2,3:5]
cum_range(x) = [i:(j-1) for (i,j) in
        zip(cumsum([1;x])[1:end-1], cumsum([1;x])[2:end])]
QF(r) = 2*r[1]*r[1] - dot(r,r)
Q(x::VectorTypes,y::VectorTypes) = 2*x[1]*y[1] - dot(x,y) # xᵀJy
fts(x₁, α₁, y₁, x₂, α₂, y₂)      = vecdot(x₁,x₂) - α₂*vecdot(x₁,y₂) -
          α₁*vecdot(y₁,x₂) + α₁*α₂*vecdot(y₁,y₂) # (x₁ - α₁*y₁)'(x₂ - α₂y₂)

function nestod_soc(z,s)

  # Nesterov-Todd Scaling Matrix for the second order cone
  # Matrix which satisfies the properties
  # W*z = inv(W)*s

  n = size(z,1)

  β = (QF(s)/QF(z))^(1/4)

  # Normalize z,s vectors
  z = z/sqrt(QF(z))
  s = s/sqrt(QF(s))

  γ = sqrt((1 + vecdot(z,s))/2)

  # Jz = J*z;
  scal!(length(z), -1., z, 1)
  z[1] = -z[1]

  w = (1./(2.*γ))*(s + z)
  w[1] = w[1] + 1
  scal!(length(w), (sqrt(2*β)/sqrt(2*w[1])), w, 1)

  J = Diag(Float64[β for i = 1:n])
  J.diag[1] = -β

  return SymWoodbury(J, w, 1.)

end

function nestod_sdc(z,s)

  # Nesterov-Todd Scaling Matrix for the Semidefinite Cone
  # Matrix which satisfies the properties
  # W*z = inv(W)*sb

  Ls  = chol(mat(s))';
  Lz  = chol(mat(z))';
  (U,Λ,V) = svd(Lz'*Ls)
  R = inv(Lz)'*U*spdiagm(sqrt(Λ))
  return VecCongurance(R)

end

# function nestod_sdc_sym(z,s)

#   # Symmetric Nesterov-Todd Scaling Matrix for the Semidefinite
#   # Cone.

#   Z  = mat(z); S  = mat(s); Sq = S^(0.5)
#   (U,S,V) = svd(Sq*Z*Sq)
#   E = U*spdiagm(1./sqrt(abs(S)))*U'
#   R = Sq'*(E)*Sq
#   (U,S,V) = svd(R)
#   R = U*spdiagm(sqrt(abs(S)))*U'
#   return VecCongurance(R)

#   # (Us,Ss,Vs) = svd(S)
#   # (Uz,Sz,Vz) = svd(Z)

#   # Ss = Us*spdiagm(Ss.^(0.125))*Vs'
#   # Zz = Uz*spdiagm(Sz.^(-0.25))*Vz'
#   # return VecCongurance(Ss*Zz*Ss)
# #  return VecCongurance(S^(0.125)*Z^(-0.25)*S^(0.125))

# end

function maxstep_rp(x,d)

  # Assume x in R+.
  # Returns maximum α such that x + α*d in R+.

  # assert(all(x .> 0));

  #I = d .>= 0;
  #if all(!I); return 1; end # all directions unbounded

  minVal = Inf
  for i = 1:length(x)
    if d[i] > 0
      minVal = min(minVal, x[i]/d[i])
    end
  end
  return minVal

end

function maxstep_rp(x, e::Void)

  # Let α = inf { α | -x + αe >= 0 }
  # Then this returns
  # 0       if α < 0   (point is STRICTLY feasible)
  # 1 + α   otherwise

  if all(x .> 0)
    return 0;
  else
    return -1 + minimum(x);
  end

end

function maxstep_soc(x,d)

  # Assume x in Q.
  # Returns maximum α such that x - α*d in Q.

  # assert((norm(x[2:end]) <= x[1]))

  # J = -speye(size(x,1)); J[1,1] = 1; # Hyperbolic Identiy

  d = -d;
  γ = Q(x,x)
  xbar = x/sqrt(γ)
  β = Q(xbar,d)

  ρ1 = β /sqrt(γ)
  μ  = ((β + d[1])/(xbar[1] + 1))[1]
  ρ2 = (d[2:end] - μ*xbar[2:end])
  alpha = norm(ρ2)/sqrt(γ) - ρ1
  if alpha < 0
    return Inf
  else
    return 1/alpha
  end

end

function maxstep_soc(x, e::Void)

  # Maximum step to cone
  α = norm(x[2:end]) - x[1];
  return α < 0 ? 0 : -1 - α;

end

function maxstep_sdc(x,d)

  # Maximum step to Semidefinite cone
  X     = mat(x)^(-1/2);
  D     = mat(d)
  XDX   = X*D*X
  XDX   = 0.5*(XDX + XDX')
  (Λ,_) = eig(XDX)
  Λn    = Λ .< 0
  if all(Λn)
    return Inf
  else
    return 1/maximum(Λ[!Λn])
  end

end

function maxstep_sdc(x,d::Void)

  # Maximum step to Semidefinite cone
  X = mat(x)
  (Λ,_) = eig(X)
  minΛ  = minimum(Λ)
  return all(minΛ .> 0) ? 0 : -1 + minΛ

end

function drp!(x, y, o)

    @inbounds @simd for i = 1:length(x); o[i] = x[i]/y[i]; end

end

function xrp!(x, y, o)

    @inbounds @simd for i = 1:length(x); o[i] = x[i]*y[i]; end

end

function dsoc!(y,x, o)

  # Inverse of arrow matrix
  #     ┌                         ┐ ┌    ┐
  # α⁻¹ │  y1  -yb                │ │ x1 │
  #     │ -yb   (αI + yb*yb')/y1  │ │ xb │
  #     └                         ┘ └    ┘

  @inbounds y1 = x[1];
  @inbounds yb = view(x,2:length(x))
  α = y1^2 - vecdot(yb,yb)

  @inbounds x1 = y[1];
  @inbounds xb = view(y,2:length(x))
  o[1] = (y1*x1 - vecdot(yb,xb) )/α
  β1 = ((-x1/α) + vecdot(yb,xb)/(y1*α))
  β2 = 1/y1
  @inbounds @simd for i = 2:length(o)
    o[i] = yb[i-1]*β1 + xb[i-1]*β2
  end

end

function xsoc!(x, y, o)

  o[1] = dot(x,y)
  @inbounds @simd for i = 2:length(x); o[i] = x[1]*y[i] + y[1]*x[i]; end

end

function dsdc!(x, y, o)

  n = round(Int, sqrt(size(x,1)))
  X = mat(x); Y = mat(y)
  o[:] = vecm(lyap(Y,-X))

end

function xsdc!(x, y, o)

  X = mat(x); Y = mat(y)
  o[:] = vecm(X*Y + Y*X)

end

# ──────────────────────────────────────────────────────────────
#  Interior Point
# ──────────────────────────────────────────────────────────────

type Solution

  y      :: Matrix  # primal
  w      :: Matrix  # dual (linear equality)
  v      :: Matrix  # dual (linear inequality)
  status :: Symbol  # :Optimal, :Infeasible
  Iter   :: Integer # number of iterations
  Mu     :: Real    # optimality conditions
  prFeas :: Real
  duFeas :: Real
  muFeas :: Real
  pobj   :: Real
  dobj   :: Real

end

"""
  conicIP(Q, c, A, b, cone_dims, G, d;
  solve3x3gen = solve3x3gen_sparse,
  optTol = 1e-5,
  DTB = 0.01,
  verbose = true,
  maxRefinementSteps = 3,
  maxIters = 100,
  cache_nestodd = false,
  refinementThreshold = optTol/1e7)

Interior point solver for the system

```
minimize    ½yᵀQy - cᵀy
s.t         Ay >= b
            Gy  = d
```

Q,c,A,b,G,d are matrices (c,b,d are NOT vectors)

cone_dims is an array of tuples (Cone Type, Dimension)

```
e.g. [("R",2),("Q",4)] means
(y₁, y₂)          in  R+
(y₃, y₄, y₅, y₆)  in  Q
```

SDP Cones are NOT supported and purely experimental at this
point.

The parameter solve3x3gen allows the passing of a custom solver
for the KKT System, as follows

```
julia> L = solve3x3gen(F,F⁻ᵀ,Q,A,G)

Then this

julia> (a,b,c) = L(y,w,v)

solves the system
┌             ┐ ┌   ┐   ┌   ┐
│ Q   G'  -A' │ │ a │ = │ y │
│ G           │ │ b │   │ w │
│ A       FᵀF │ │ c │   │ v │
└             ┘ └   ┘   └   ┘
```

We can also wrap a 2x2 solver using pivot3gen(solve2x2gen)
The 2x2 solves the system

```
julia> L = solve2x2gen(F,F⁻ᵀ,Q,A,G)

Then this

julia> (a,b) = L(y,w)

solves the system

┌                     ┐ ┌   ┐   ┌   ┐
│ Q + Aᵀinv(FᵀF)A  G' │ │ a │ = │ y │
│ G                   │ │ b │   │ w │
└                     ┘ └   ┘   └   ┘
```
"""
function conicIP(

  # ½xᵀQx - cᵀx
  Q, c::Matrix,

  # Ax ≧ b
  A, b::Matrix, cone_dims,

  # Gx = d
  G = spzeros(0,length(c)), d = zeros(0,1);

  # Solver Parameters

  # L = solve3x3gen(F,F⁻ᵀ,Q,A,G)
  # L(a,b,c) solves the system
  # ┌             ┐ ┌   ┐   ┌   ┐
  # │ Q   G'  -A' │ │ a │ = │ y │
  # │ G           │ │ b │   │ w │
  # │ A       FᵀF │ │ c │   │ v │
  # └             ┘ └   ┘   └   ┘
  #
  # We can also wrap a 2x2 solver using pivot3gen(solve2x2gen)
  # The 2x2 solves the system
  #
  # L = solve2x2gen(F,F⁻ᵀ,Q,A,G)
  # L(a,b) solves
  # ┌                ┐ ┌   ┐   ┌   ┐
  # │ Q + AᵀFᵀFA  G' │ │ a │ = │ y │
  # │ G              │ │ b │   │ w │
  # └                ┘ └   ┘   └   ┘
  kktsolver = kktsolver_qr,

  optTol = 1e-6,           # Optimal Tolerance
  DTB = 0.01,              # Distance to Boundary
  verbose = true,          # Verbose Output
  maxRefinementSteps = 3,  # Maximum number of IR Steps
  maxIters = 100,          # Maximum number of interior iterations
  cache_nestodd = false,   # Set to true if there are many small blocks
  infeasTol = optTol,      # Infeasibility threshold (this shouldn't need to be tweaked,
                           # but set it small if the program returns infeasible/unbounded when
                           # you are sure it isn't)
  refinementThreshold = optTol/1e7 # Accuracy of refinement steps
  )

  # Precomputed transposition matrices
  Aᵀ = A'; Gᵀ = G'

  n = length(c) # Number of variables
  m = size(A,1) # Number of inequality constraints
  p = size(G,1) # Number of equality constraints

  block_types  = [i[1] for i in cone_dims]
  block_sizes  = [i[2] for i in cone_dims]
  block_data   = zip(block_types, cum_range(block_sizes),
                     [i for i in 1:length(block_types)])

  normc = norm(c)
  normd = isempty(d) ? -Inf: norm(d)
  normb = isempty(b) ? -Inf: norm(b)

  # Sanity Checks
  ◂ = nothing
  #!(m == 0 && length(block_sizes) == 0) || m != sum(block_sizes) ? error("Inconsistency in inequalities") : ◂
  size(Q,1) != size(Q,2)? error("Q is not square") : ◂
  size(b,1) != m        ? error("Inconsistency in inequalities") : ◂
  size(c,1) != n        ? error("Inconsistency in inequalities/objective") : ◂
  size(d,1) != p        ? error("Inconsistency in equalities") : ◂
  size(G,2) != n        ? error("Inconsistency in equalities/objective") : ◂

  # Number to scale (z's) by
  # 1 for each R_+ dimension
  # 1 for each Q cone (regardless of dimension)
  conedim = 0
  for (btype, I, i) = block_data
    if btype == "R"; conedim += length(I);  end
    if btype == "Q"; conedim += 1;          end
    if btype == "S"; conedim += ord(I);     end
  end

  # e = conic group identity
  # Concatenate the vectors
  # [1, 1, … , 1] for R_+
  # [1, 0, … , 0] for Q
  # vecm(I)       for S
  e = zeros(m,1)
  for (btype, I, i) = block_data
    m_i = length(I)
    if btype == "R"; e[I] = ones(m_i,1);             end
    if btype == "Q"; e[I] = [1; zeros(m_i-1,1)];     end
    if btype == "S"; e[I] = vecm(eye(ord(I)));       end
  end

  # ──────────────────────────────────────────────────────────────
  #  Functions capturing cone_dims
  # ──────────────────────────────────────────────────────────────

  function maxstep(x, d)

    # Linesearch

    min_α = Inf;
    @inbounds for (btype, I, i) = block_data
      xI = view(x,I)
      dI = ( d == nothing ? nothing : view(d,I) )
      if btype == "R"; α = maxstep_rp(xI,dI);  end
      if btype == "Q"; α = maxstep_soc(xI,dI); end
      if btype == "S"; α = maxstep_sdc(xI,dI); end
      min_α = min(α, min_α)
    end

    return min_α;

  end

  function nt_scaling(x, y)

    # Compute Nesterov-Todd scaling matrix, F s.t.
    # λ = F*x = F\y

    B = Block(size(block_sizes,1));

    @inbounds for (btype, I, i) = block_data
      xI = view(x,I); yI = view(y,I);
      if btype == "R"; B[i] = Diag(sqrt(yI./xI)); end
      if btype == "Q"; B[i] = nestod_soc(xI, yI); end
      if btype == "S"; B[i] = nestod_sdc(xI, yI); end
    end

    return B;

  end

  function ÷(x,y)

    # Group division x ○\ y

    o = zeros(length(x),1)
    @inbounds for (btype, I, i) = block_data
      xI = view(x,I); yI = view(y,I); oI = view(o,I)
      if btype == "R"; drp!(xI, yI, oI);  end
      if btype == "Q"; dsoc!(xI, yI, oI); end
      if btype == "S"; dsdc!(xI, yI, oI); end
    end
    return o;

  end

  function ∘(x,y)

    # Group product x ○ y

    o = zeros(length(x),1)
    @inbounds for (btype, I, i) = block_data
      xI = view(x,I); yI = view(y,I); oI = view(o,I)
      if btype == "R"; xrp!(xI, yI, oI);  end
      if btype == "Q"; xsoc!(xI, yI, oI); end
      if btype == "S"; xsdc!(xI, yI, oI); end
    end
    return o;

  end

  solve3x3gen = kktsolver(Q,A,G,cone_dims)

  function solve4x4gen(λ, F, F⁻ᵀ, solve3x3gen = solve3x3gen)

    #
    # solve4x4gen(λ, F)(r) solves the 4x4 KKT System
    # ┌                  ┐ ┌    ┐   ┌     ┐
    # │ Q   G'  -A'      │ │ Δy │ = │ r.y │
    # │ G                │ │ Δw │   │ r.w │ S = block(λ)*F
    # │ A             -I │ │ Δv │   │ r.v │ V = block(λ)*F⁻ᵀ
    # │          S     V │ │ Δs │   │ r.s │
    # └                  ┘ └    ┘   └     ┘
    # F = Nesterov-Todd scaling matrix
    #

    solve3x3 = solve3x3gen(F, F⁻ᵀ)

    function solve4x4(r)

      t1 = F'*(r.s ÷ λ)
      (Δy, Δw, Δv)  = solve3x3(r.y, r.w, r.v + t1)
      axpy!(-1, F'*(F*Δv), t1) # > Δs = t1 - F*(F*Δv)
      return v4x1(Δy,Δw,Δv,t1)

    end

  end

  if verbose
      print("\n > INTERIOR POINT SOLVER v0.7 (July 2016)\n\n")
  end

  # ────────────────────────────────────────────────────────────
  #  Initial Point
  # ────────────────────────────────────────────────────────────

  I  = Block([Diag(ones(i)) for i = block_sizes])
  r0 = v4x1(c, d, b, zeros(m,1))
  z  = solve4x4gen(e,I,I)(r0)

  α_v = maxstep(z.v, nothing)
  α_s = maxstep(z.s, nothing)

  # Change to +
  z.v = z.v - α_v*e
  z.s = z.s - α_s*e

  if verbose
      println("            Optimality                      Objective              Infeasibility       ")
      println()
      ξ1()=@printf("\x1b[1m %-6s  │  %-8s  %-8s  %-8s │  %-8s  %-8s  │  %-8s  %-8s │  %-8s \x1b[0m\n",
                  "  Iter","prFeas","duFeas","muFeas","pobj","dobj","icertp","icertd","refine");ξ1()
  end

  # ────────────────────────────────────────────────────────────
  #  Iterate Loop
  # ────────────────────────────────────────────────────────────

  sol     = Solution(z.y, z.w, z.v, :None, 0, 0, Inf, Inf, Inf, Inf, -Inf)
  optBest = Inf
  rStep   = 0
  rnorm   = 0
  for Iter = 1:maxIters

    F    = nt_scaling(z.v, z.s)   # Nesterov-Todd Scaling Matrix
    F⁻ᵀ  = inv(F)'
    λ    = F*z.v;                 # This is also F⁻ᵀ*z.s.

    solve = solve4x4gen(λ,F,F⁻ᵀ)   # Caches 4x4 solver
                                   # (used a few times, at least 2)

    #         ┌                   ┐ ┌     ┐
    # rleft = │ Q   G'   -A'      │ │ z.y │
    #         │ G                 │ │ z.w │  V = block(λ)*F⁻ᵀ
    #         │ A              -I │ │ z.v │    = block(λ)*λ
    #         │           S     V │ │ z.s │
    #         └                   ┘ └     ┘
    rleft = v4x1( Q*z.y + Gᵀ*z.w - Aᵀ*z.v ,
                  G*z.y                   ,
                  A*z.y - z.s             ,
                  λ ∘ λ                   )

    # True Residual of nonlinear KKT System
    r0 = v4x1(rleft.y - c, rleft.w - d, rleft.v - b, rleft.s);

    # Gap
    μbar = vecdot(z.v,z.s)
    μ    = μbar/conedim

    # ────────────────────────────────────────────────────────────
    #  Print iterate status, save best iterate
    # ────────────────────────────────────────────────────────────

    cᵀy = vecdot(c,z.y)
    rDu = norm(r0.y)/(1+norm(c))
    rPr = normsafe(r0.v)/(1+normsafe(b))
    rCp = normsafe(r0.s)/(1+abs(cᵀy));

    if max(rDu, rPr, rCp) < optBest
      sol.y[:] = z.y; sol.w[:] = z.w; sol.v[:] = z.v
      sol.Iter = Iter; sol.Mu = μ;
      sol.duFeas = rDu; sol.prFeas = rPr; sol.muFeas = rCp
      optBest = max(rDu, rPr, rCp)
    end

    pobj = 0.5*vecdot(z.y, Q*z.y) - vecdot(c, z.y)
    dobj = pobj + vecdot(z.w, r0.w) + vecdot(z.v, r0.v) - vecdot(z.v, z.s)

    sol.pobj = pobj
    sol.dobj = dobj

    # ────────────────────────────────────────────────────────────
    # Convergence Checks (on previous iterate)
    # ────────────────────────────────────────────────────────────

    # Optimality
    if max(rDu, rPr, rCp) < optTol
      sol.status = :Optimal
    end

    if !(p == 0 && m == 0)

      # Primal Infeasibility
      #
      # Certificate: ∃w,v such that
      #   Gᵀw + Aᵀv = 0
      #   bᵀv + dᵀw < 0
      #   w ≧ 0
      #
      # The program returns a certificate w,v that satisfies
      #
      #  CVXOPT style         ECOS Style
      #  -------------------------------------------
      #   Gᵀw + Aᵀv = 0        Gᵀw + Aᵀv = 0
      #   bᵀv + dᵀw = -1       bᵀv + dᵀw < 0
      #   w ≧ 0                w ≧ 0
      #                        norm(v) + norm(w) = 1
      #
      dᵀy_bᵀv  = vecdot(d,z.w) - vecdot(b,z.v)

      p_infeas_unscaled = norm(Gᵀ*z.w - Aᵀ*z.v)
      p_infeas_cvx = dᵀy_bᵀv < 0 ? p_infeas_unscaled/(normsafe(z.y) + normsafe(z.v)) : NaN
      p_infeas_ecos = dᵀy_bᵀv < 0 ? p_infeas_unscaled/(max(1,normc)*abs(dᵀy_bᵀv)) : NaN
      p_infeas = max(p_infeas_cvx, p_infeas_ecos)

      if p_infeas < infeasTol
        sol.y[:] = 0*sol.y[:]/0; sol.w[:] = z.w/-dᵀy_bᵀv; sol.v[:] = z.v/-dᵀy_bᵀv;
        sol.status = :Infeasible
      end

      # Dual Infeasiblity
      #
      # Certificate: ∃y,s such that
      #   Ay - s = 0   (d_infeas1)
      #   Gy = 0       (d_infeas2)
      #   Qy = 0       (d_infeas3)
      #   cᵀy > 0
      #   s ≧ 0
      #
      # The program returns a certificate y that satisfies
      #
      #  CVXOPT style    ECOS style
      #  ------------------------------
      #   Ay ≧ 0          Ay ≧ 0
      #   Gy = 0          Gy = 0
      #   Qy = 0          Qy = 0
      #   cᵀy = 1         cᵀy > 0
      #                   norm(y) = 1
      #
      d_infeas1 = isempty(A) ? -Inf : norm(A*z.y - z.s)
      d_infeas2 = isempty(G) ? -Inf : norm(G*z.y)
      d_infeas3 = all(isfinite(z.y)) ? norm(Q*z.y) : NaN

      d_infeas_cvx = cᵀy > 0 ? max(d_infeas1/max(1,normb), d_infeas2/max(1,normd), d_infeas3/max(1,normc))/abs(cᵀy) : NaN
      d_infeas_ecos = cᵀy > 0 ? max(d_infeas1, d_infeas2, d_infeas3)/norm(z.y) : NaN
      d_infeas = abs(max(d_infeas_cvx, d_infeas_ecos))

      if d_infeas < infeasTol
        sol.y[:] = z.y/abs(cᵀy); sol.v[:] = 0*sol.v[:]/0; sol.w[:] = 0*sol.w[:]/0
        sol.status = :Unbounded
      end

    end

    if verbose
      if rnorm > 0.001; print("\x1b[1m\x1b[31m"); end
      ξ2()=@printf(" %6i  │  %-8.1e  %-8.1e  %-8.1e │  % -8.1e  % -8.1e  │  %-8.1e  %-8.1e │  %i\n",
                  Iter, rDu, rPr, rCp, pobj, dobj, p_infeas, d_infeas, rStep);ξ2()
      if rnorm > 0.001; print("\x1b[0m"); end
    end

    if verbose
      if sol.status == :Infeasible; print("\n > EXIT -- Certificate of Infeasiblity Found!\n\n"); end
      if sol.status == :Unbounded;  print("\n > EXIT -- Certificate of Dual Infeasibility Found!\n\n"); end
      if sol.status == :Optimal;    print("\n > EXIT -- Below Tolerance!\n\n"); end
    end

    if sol.status != :None; return sol; end

    # Cause of Divergence Unknown
    if !all(isfinite([μ, rDu, rPr, rCp]))
      if verbose; print("\n > EXIT -- Error!\n\n"); end
      sol.status = :Error; return sol
    end

    # ────────────────────────────────────────────────────────────
    #  Predictor
    # ────────────────────────────────────────────────────────────

    d_aff   = solve(r0)

    α_aff_v = min( maxstep( z.v, d_aff.v ) , 1 )
    α_aff_s = min( maxstep( z.s, d_aff.s ) , 1 )
    α_aff   = min( α_aff_v , α_aff_s )

    # >> ρ  = (z.v - α_aff*d_aff.v)'*(z.s - α_aff*d_aff.s)/μbar
    ρ  = fts(z.v, α_aff, d_aff.v, z.s, α_aff,d_aff.s)/μbar
    σ  = max(0,min(1,ρ))^3

    # ────────────────────────────────────────────────────────────
    #  Corrector
    # ────────────────────────────────────────────────────────────

    F⁻ᵀdfs = F⁻ᵀ*d_aff.s
    Fdfs   = F*d_aff.v

    # >> lc = -(F⁻ᵀdfs ∘ Fdfs) + (σ*μ)[1]*e;
    lc = (F⁻ᵀdfs ∘ Fdfs); axpy!(-(σ*μ)[1], e, lc);
    scal!(length(e), -1., lc, 1)

    r  =  v4x1(r0.y, r0.w, r0.v, rleft.s - lc)

    # ────────────────────────────────────────────────────────────
    #  Take newton step, with iterative refinement
    # ────────────────────────────────────────────────────────────

    Δz  = solve(r);
    rStep = 1;
    for rStep = 1:maxRefinementSteps
      rkkt  = v4x1( Q*Δz.y  + Gᵀ*Δz.w  - Aᵀ*Δz.v , # y
                    G*Δz.y                       , # w
                    A*Δz.y - Δz.s                , # v
                    λ∘(F*Δz.v) + λ∘(F⁻ᵀ*Δz.s) )    # s
      rIr = r - rkkt
      rnorm = norm(rIr)/(n + 2*m)
      # if rnorm > 0.1
      #   verbose ? warn("4x4 solve failed, residual norm $(rnorm)") : ◂
      # end
      if rnorm < refinementThreshold; break; end
      Δzr = solve(rIr)
      Δz  = Δz + Δzr
    end

    # ────────────────────────────────────────────────────────────
    # Make Step
    # ────────────────────────────────────────────────────────────

    α_v = min( maxstep(z.v, Δz.v/(1-DTB)), 1 )
    α_s = min( maxstep(z.s, Δz.s/(1-DTB)), 1 )
    α   = min( α_v, α_s )

    # >> z = z - α*Δz;
    axpy4!(-α, Δz, z)

  end

  sol.status = :Abandoned
  return sol

end

include("wrapper.jl")
include("preprocessor.jl")

end


text:
using Test
using SkillRating

@testset "glicko" begin
    @test round(glicko_rating(1500, 200, [1400, 1550, 1700], [30, 100, 300], [1, 0, 0])) == 1464
    @test round(glicko_RD(1500, 200, [1400, 1550, 1700], [30, 100, 300], [1, 0, 0]), digits = 1) == 151.4
    @test round(glicko_expected(1400, 80, 1500, 150), digits = 3) == 0.376
    @test round.(glicko2(0, 1.1513, 0.06, [-0.5756, 0.2878, 1.1513], [0.1727, 0.5756, 1.7269], [1, 0, 0]; τ = 0.5), digits=4) == (-0.2069, 0.8722, 0.06)

    @test round(elo_rating(1613, [1609, 1477, 1388, 1586, 1720], [0,0.5,1,1,0])) == 1601
    @test round(elo_expected(1613, 1609), digits=2) == 0.51
end

max_stars_repo_name: altre/SkillRating

max_issues_repo_name: altre/SkillRating

max_forks_repo_name: altre/SkillRating


text:
module Constraint

using MathOptInterface
const MOI = MathOptInterface
const MOIU = MOI.Utilities
const MOIB = MOI.Bridges

const CI = MOI.ConstraintIndex

# Definition of a constraint bridge
include("bridge.jl")

# Mapping between constraint indices and bridges
include("map.jl")

# Bridge optimizer bridging a specific constraint bridge
include("single_bridge_optimizer.jl")

# TODO(odow): the compiler in Julia <= 1.2 (and in later versions unless
# fixed) gets stuck compiling add_constrained_variable for some inputs. This
# method seemed necessary to fix it.
# See https://github.com/JuliaLang/julia/issues/32167 for more.
function MOI.Bridges.Constraint.bridge_constraint(BridgeType, b, f, s)
    throw(MOI.UnsupportedConstraint{typeof(f), typeof(s)}())
end

# Constraint bridges
# Function conversion bridges

"""
    abstract type AbstractFunctionConversionBridge <: AbstractBridge end

Bridge a constraint `F`-in-`S` into a constraint `G`-in-`S` where `F` and `G`
are equivalent representations of the same function. By convention, the
transformed function is stored in the `constraint` field.
"""
abstract type AbstractFunctionConversionBridge <: AbstractBridge end

function MOI.get(model::MOI.ModelLike, attr::MOI.AbstractConstraintAttribute,
                 bridge::AbstractFunctionConversionBridge)
    if invariant_under_function_conversion(attr)
        return MOI.get(model, attr, bridge.constraint)
    else
        throw(ArgumentError("Bridge of type `$(typeof(bridge))` does not support accessing the attribute `$attr` because `MOIB.Constraint.invariant_under_function_conversion($attr)` returns `false`."))
    end
end

function MOI.set(model::MOI.ModelLike, attr::MOI.AbstractConstraintAttribute,
                 bridge::AbstractFunctionConversionBridge, value)
    if invariant_under_function_conversion(attr)
        return MOI.set(model, attr, bridge.constraint, value)
    else
        throw(ArgumentError("Bridge of type `$(typeof(bridge))` does not support setting the attribute `$attr` because `MOIB.Constraint.invariant_under_function_conversion($attr)` returns `false`."))
    end
end

"""
    invariant_under_function_conversion(attr::MOI.AbstractConstraintAttribute)

Returns whether the value of the attribute does not change if the constraint
`F`-in-`S` is transformed into a constraint `G`-in-`S` where `F` and `G` are
equivalent representations of the same function. If it returns true, then
subtypes of [`Constraint.AbstractFunctionConversionBridge`](@ref) such as
[`Constraint.ScalarFunctionizeBridge`](@ref) and
[`Constraint.VectorFunctionizeBridge`](@ref) will automatically support
[`MOI.get`](@ref) and [`MOI.set`](@ref) for `attr`.
"""
invariant_under_function_conversion(::MOI.AbstractConstraintAttribute) = false

function invariant_under_function_conversion(::Union{
       MOI.ConstraintSet,
       MOI.ConstraintBasisStatus,
       MOI.ConstraintPrimal,
       MOI.ConstraintPrimalStart,
       MOI.ConstraintDual,
       MOI.ConstraintDualStart})
    return true
end

include("functionize.jl")
const ScalarFunctionize{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{ScalarFunctionizeBridge{T}, OT}
const VectorFunctionize{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{VectorFunctionizeBridge{T}, OT}
# TODO add affine -> quadratic conversion bridge

include("flip_sign.jl")
const GreaterToLess{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{GreaterToLessBridge{T}, OT}
const LessToGreater{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{LessToGreaterBridge{T}, OT}
const NonnegToNonpos{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NonnegToNonposBridge{T}, OT}
const NonposToNonneg{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NonposToNonnegBridge{T}, OT}
include("vectorize.jl")
const Vectorize{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{VectorizeBridge{T}, OT}
include("scalarize.jl")
const Scalarize{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{ScalarizeBridge{T}, OT}
include("slack.jl")
const ScalarSlack{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{ScalarSlackBridge{T}, OT}
const VectorSlack{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{VectorSlackBridge{T}, OT}
include("interval.jl")
const SplitInterval{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SplitIntervalBridge{T}, OT}
include("rsoc.jl")
const RSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCBridge{T}, OT}
include("socr.jl")
const SOCR{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCRBridge{T}, OT}
include("quad_to_soc.jl")
const QuadtoSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{QuadtoSOCBridge{T}, OT}
include("norm_to_lp.jl")
const NormInfinity{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormInfinityBridge{T}, OT}
const NormOne{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormOneBridge{T}, OT}
include("geomean.jl")
const GeoMean{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{GeoMeanBridge{T}, OT}
include("square.jl")
const Square{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SquareBridge{T}, OT}
include("det.jl")
const LogDet{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{LogDetBridge{T}, OT}
const RootDet{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RootDetBridge{T}, OT}
include("soc_to_psd.jl")
const SOCtoPSD{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCtoPSDBridge{T}, OT}
const RSOCtoPSD{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCtoPSDBridge{T}, OT}
include("indicator.jl")

"""
    add_all_bridges(bridged_model, T::Type)

Add all bridges defined in the `Bridges.Constraint` submodule to
`bridged_model`. The coefficient type used is `T`.
"""
function add_all_bridges(bridged_model, T::Type)
    MOIB.add_bridge(bridged_model, GreaterToLessBridge{T})
    MOIB.add_bridge(bridged_model, LessToGreaterBridge{T})
    MOIB.add_bridge(bridged_model, NonnegToNonposBridge{T})
    MOIB.add_bridge(bridged_model, NonposToNonnegBridge{T})
    MOIB.add_bridge(bridged_model, ScalarizeBridge{T})
    MOIB.add_bridge(bridged_model, VectorizeBridge{T})
    MOIB.add_bridge(bridged_model, ScalarSlackBridge{T})
    MOIB.add_bridge(bridged_model, VectorSlackBridge{T})
    MOIB.add_bridge(bridged_model, ScalarFunctionizeBridge{T})
    MOIB.add_bridge(bridged_model, VectorFunctionizeBridge{T})
    MOIB.add_bridge(bridged_model, SplitIntervalBridge{T})
    MOIB.add_bridge(bridged_model, QuadtoSOCBridge{T})
    MOIB.add_bridge(bridged_model, NormInfinityBridge{T})
    MOIB.add_bridge(bridged_model, NormOneBridge{T})
    MOIB.add_bridge(bridged_model, GeoMeanBridge{T})
    MOIB.add_bridge(bridged_model, SquareBridge{T})
    MOIB.add_bridge(bridged_model, LogDetBridge{T})
    MOIB.add_bridge(bridged_model, RootDetBridge{T})
    MOIB.add_bridge(bridged_model, RSOCBridge{T})
    MOIB.add_bridge(bridged_model, SOCRBridge{T})
    # We do not add `SOCtoPSDBridge` as transforming the `SOC` to `RSOC` and
    # then to `PSD` produces a smaller SDP constraint.
    MOIB.add_bridge(bridged_model, RSOCtoPSDBridge{T})
    MOIB.add_bridge(bridged_model, IndicatorActiveOnFalseBridge{T})
    return
end

end


text:
# Attributes

"""
    AbstractOptimizerAttribute

Abstract supertype for attribute objects that can be used to set or get attributes (properties) of the optimizer.

### Note

The difference between `AbstractOptimizerAttribute` and `AbstractModelAttribute`
lies in the behavior of `is_empty`, `empty!` and `copy_to`. Typically optimizer
attributes only affect how the model is solved.
"""
abstract type AbstractOptimizerAttribute end

"""
    AbstractModelAttribute

Abstract supertype for attribute objects that can be used to set or get attributes (properties) of the model.
"""
abstract type AbstractModelAttribute end

"""
    AbstractVariableAttribute

Abstract supertype for attribute objects that can be used to set or get attributes (properties) of variables in the model.
"""
abstract type AbstractVariableAttribute end

"""
    AbstractConstraintAttribute

Abstract supertype for attribute objects that can be used to set or get attributes (properties) of constraints in the model.
"""
abstract type AbstractConstraintAttribute end

# Attributes should not contain any `VariableIndex` or `ConstraintIndex` as the
# set is passed unmodifed during `copy_to`.
const AnyAttribute = Union{AbstractOptimizerAttribute, AbstractModelAttribute, AbstractVariableAttribute, AbstractConstraintAttribute}

# This allows to use attributes in broadcast calls without the need to
# embed it in a `Ref`
Base.broadcastable(attribute::AnyAttribute) = Ref(attribute)

"""
    struct UnsupportedAttribute{AttrType} <: UnsupportedError
        attr::AttrType
        message::String
    end

An error indicating that the attribute `attr` is not supported by the model,
i.e. that [`supports`](@ref) returns `false`.
"""
struct UnsupportedAttribute{AttrType<:AnyAttribute} <: UnsupportedError
    attr::AttrType
    message::String
end
UnsupportedAttribute(attr::AnyAttribute) = UnsupportedAttribute(attr, "")
element_name(err::UnsupportedAttribute) = "Attribute $(err.attr)"

"""
    struct SetAttributeNotAllowed{AttrType} <: NotAllowedError
        attr::AttrType
        message::String # Human-friendly explanation why the attribute cannot be set
    end

An error indicating that the attribute `attr` is supported (see
[`supports`](@ref)) but cannot be set for some reason (see the error string).
"""
struct SetAttributeNotAllowed{AttrType<:AnyAttribute} <: NotAllowedError
    attr::AttrType
	message::String # Human-friendly explanation why the attribute cannot be set
end
SetAttributeNotAllowed(attr::AnyAttribute) = SetAttributeNotAllowed(attr, "")

operation_name(err::SetAttributeNotAllowed) = "Setting attribute $(err.attr)"
message(err::SetAttributeNotAllowed) = err.message

"""
    AbstractSubmittable

Abstract supertype for objects that can be submitted to the model.
"""
abstract type AbstractSubmittable end

# This allows to use submittable in broadcast calls without the need to embed
# it in a `Ref`.
Base.broadcastable(sub::AbstractSubmittable) = Ref(sub)

"""
    struct UnsupportedSubmittable{SubmitType} <: UnsupportedError
        sub::SubmitType
        message::String
    end

An error indicating that the submittable `sub` is not supported by the model,
i.e. that [`supports`](@ref) returns `false`.
"""
struct UnsupportedSubmittable{SubmitType<:AbstractSubmittable} <: UnsupportedError
    sub::SubmitType
    message::String
end

"""
    struct SubmitNotAllowed{SubmitTyp<:AbstractSubmittable} <: NotAllowedError
        sub::SubmitType
        message::String # Human-friendly explanation why the attribute cannot be set
    end

An error indicating that the submittable `sub` is supported (see
[`supports`](@ref)) but cannot be added for some reason (see the error string).
"""
struct SubmitNotAllowed{SubmitType<:AbstractSubmittable} <: NotAllowedError
    sub::SubmitType
    message::String # Human-friendly explanation why the attribute cannot be set
end
SubmitNotAllowed(sub::AbstractSubmittable) = SubmitNotAllowed(sub, "")

operation_name(err::SubmitNotAllowed) = "Submitting $(err.sub)"
message(err::SubmitNotAllowed) = err.message

struct ResultIndexBoundsError{AttrType} <: Exception
    attr::AttrType
    result_count::Int
end
# TODO: rename the .N -> .result_index field in necessary attributes (e.g.,
# VariablePrimal, ConstraintPrimal, ConstraintDual), and remove this helper
# function.
_result_index_field(attr) = attr.result_index
function check_result_index_bounds(model::ModelLike, attr)
    result_count = get(model, ResultCount())
    if !(1 <= _result_index_field(attr) <= result_count)
        throw(ResultIndexBoundsError(attr, result_count))
    end
end
function Base.showerror(io::IO, err::ResultIndexBoundsError)
    print(io,
        "Result index of attribute $(err.attr) out of bounds. There are " *
        "currently $(err.result_count) solution(s) in the model.")
end

"""
    supports(model::ModelLike, sub::AbstractSubmittable)::Bool

Return a `Bool` indicating whether `model` supports the submittable `sub`.

    supports(model::ModelLike, attr::AbstractOptimizerAttribute)::Bool

Return a `Bool` indicating whether `model` supports the optimizer attribute
`attr`. That is, it returns `false` if `copy_to(model, src)` shows a warning in
case `attr` is in the [`ListOfOptimizerAttributesSet`](@ref) of `src`; see
[`copy_to`](@ref) for more details on how unsupported optimizer attributes are
handled in copy.

    supports(model::ModelLike, attr::AbstractModelAttribute)::Bool

Return a `Bool` indicating whether `model` supports the model attribute `attr`.
That is, it returns `false` if `copy_to(model, src)` cannot be performed in case
`attr` is in the [`ListOfModelAttributesSet`](@ref) of `src`.

    supports(model::ModelLike, attr::AbstractVariableAttribute, ::Type{VariableIndex})::Bool

Return a `Bool` indicating whether `model` supports the variable attribute
`attr`. That is, it returns `false` if `copy_to(model, src)` cannot be performed
in case `attr` is in the [`ListOfVariableAttributesSet`](@ref) of `src`.

    supports(model::ModelLike, attr::AbstractConstraintAttribute, ::Type{ConstraintIndex{F,S}})::Bool where {F,S}

Return a `Bool` indicating whether `model` supports the constraint attribute
`attr` applied to an `F`-in-`S` constraint. That is, it returns `false` if
`copy_to(model, src)` cannot be performed in case `attr` is in the
[`ListOfConstraintAttributesSet`](@ref) of `src`.

For all five methods, if the attribute is only not supported in specific
circumstances, it should still return `true`.

Note that `supports` is only defined for attributes for which
[`is_copyable`](@ref) returns `true` as other attributes do not appear in the
list of attributes set obtained by `ListOf...AttributesSet`.
"""
function supports end
supports(::ModelLike, ::AbstractSubmittable) = false
function supports(::ModelLike, attr::Union{AbstractModelAttribute,
                                           AbstractOptimizerAttribute})
    if !is_copyable(attr)
        throw(ArgumentError("`supports` is not defined for $attr, it is only" *
                            " defined for attributes such that `is_copyable`" *
                            " returns `true`."))
    end
    return false
end
function supports(::ModelLike, attr::Union{AbstractVariableAttribute,
                                           AbstractConstraintAttribute},
                  ::Type{<:Index})
    if !is_copyable(attr)
        throw(ArgumentError("`supports` is not defined for $attr, it is only" *
                            " defined for attributes such that `is_copyable`" *
                            " returns `true`."))
    end
    return false
end

"""
    get(optimizer::AbstractOptimizer, attr::AbstractOptimizerAttribute)

Return an attribute `attr` of the optimizer `optimizer`.

    get(model::ModelLike, attr::AbstractModelAttribute)

Return an attribute `attr` of the model `model`.

    get(model::ModelLike, attr::AbstractVariableAttribute, v::VariableIndex)

If the attribute `attr` is set for the variable `v` in the model `model`, return
its value, return `nothing` otherwise. If the attribute `attr` is not supported
by `model` then an error should be thrown instead of returning `nothing`.

    get(model::ModelLike, attr::AbstractVariableAttribute, v::Vector{VariableIndex})

Return a vector of attributes corresponding to each variable in the collection `v` in the model `model`.

    get(model::ModelLike, attr::AbstractConstraintAttribute, c::ConstraintIndex)

If the attribute `attr` is set for the constraint `c` in the model `model`,
return its value, return `nothing` otherwise. If the attribute `attr` is not
supported by `model` then an error should be thrown instead of returning
`nothing`.

    get(model::ModelLike, attr::AbstractConstraintAttribute, c::Vector{ConstraintIndex{F,S}})

Return a vector of attributes corresponding to each constraint in the collection `c` in the model `model`.

    get(model::ModelLike, ::Type{VariableIndex}, name::String)

If a variable with name `name` exists in the model `model`, return the
corresponding index, otherwise return `nothing`. Errors if two variables
have the same name.

    get(model::ModelLike, ::Type{ConstraintIndex{F,S}}, name::String) where {F<:AbstractFunction,S<:AbstractSet}

If an `F`-in-`S` constraint with name `name` exists in the model `model`, return
the corresponding index, otherwise return `nothing`. Errors if two constraints
have the same name.

    get(model::ModelLike, ::Type{ConstraintIndex}, name::String)

If *any* constraint with name `name` exists in the model `model`, return the
corresponding index, otherwise return `nothing`. This version is available for
convenience but may incur a performance penalty because it is not type stable.
Errors if two constraints have the same name.

### Examples

```julia
get(model, ObjectiveValue())
get(model, VariablePrimal(), ref)
get(model, VariablePrimal(5), [ref1, ref2])
get(model, OtherAttribute("something specific to cplex"))
get(model, VariableIndex, "var1")
get(model, ConstraintIndex{ScalarAffineFunction{Float64},LessThan{Float64}}, "con1")
get(model, ConstraintIndex, "con1")
```
"""
function get end
# We want to avoid being too specific in the type arguments to avoid method ambiguity.
# For model, get(::ModelLike, ::AbstractVariableAttribute, ::Vector{VariableIndex}) would not allow
# to define get(::SomeModel, ::AnyAttribute, ::Vector)
get(model::ModelLike, attr::AnyAttribute, idxs::Vector) = get.(model, attr, idxs)

function get(model::ModelLike, attr::AnyAttribute, args...)
    throw(ArgumentError("ModelLike of type $(typeof(model)) does not support accessing the attribute $attr"))
end

"""
    get!(output, model::ModelLike, args...)

An in-place version of `get`.
The signature matches that of `get` except that the the result is placed in the vector `output`.
"""
function get! end
function get!(output, model::ModelLike, attr::AnyAttribute, args...)
    throw(ArgumentError("ModelLike of type $(typeof(model)) does not support accessing the attribute $attr"))
end

"""
    set(optimizer::AbstractOptimizer, attr::AbstractOptimizerAttribute, value)

Assign `value` to the attribute `attr` of the optimizer `optimizer`.

    set(model::ModelLike, attr::AbstractModelAttribute, value)

Assign `value` to the attribute `attr` of the model `model`.

    set(model::ModelLike, attr::AbstractVariableAttribute, v::VariableIndex, value)

Assign `value` to the attribute `attr` of variable `v` in model `model`.

    set(model::ModelLike, attr::AbstractVariableAttribute, v::Vector{VariableIndex}, vector_of_values)

Assign a value respectively to the attribute `attr` of each variable in the collection `v` in model `model`.

    set(model::ModelLike, attr::AbstractConstraintAttribute, c::ConstraintIndex, value)

Assign a value to the attribute `attr` of constraint `c` in model `model`.

    set(model::ModelLike, attr::AbstractConstraintAttribute, c::Vector{ConstraintIndex{F,S}}, vector_of_values)

Assign a value respectively to the attribute `attr` of each constraint in the collection `c` in model `model`.

An [`UnsupportedAttribute`](@ref) error is thrown if `model` does not support
the attribute `attr` (see [`supports`](@ref)) and a [`SetAttributeNotAllowed`](@ref)
error is thrown if it supports the attribute `attr` but it cannot be set.

### Replace set in a constraint

    set(model::ModelLike, ::ConstraintSet, c::ConstraintIndex{F,S}, set::S)

Change the set of constraint `c` to the new set `set` which should be of the
same type as the original set.

#### Examples

If `c` is a `ConstraintIndex{F,Interval}`

```julia
set(model, ConstraintSet(), c, Interval(0, 5))
set(model, ConstraintSet(), c, GreaterThan(0.0))  # Error
```

### Replace function in a constraint

    set(model::ModelLike, ::ConstraintFunction, c::ConstraintIndex{F,S}, func::F)

Replace the function in constraint `c` with `func`. `F` must match the original
function type used to define the constraint.

#### Note

Setting the constraint function is not allowed if `F` is
[`SingleVariable`](@ref), it throws a
[`SettingSingleVariableFunctionNotAllowed`](@ref) error. Indeed, it would
require changing the index `c` as the index of `SingleVariable` constraints
should be the same as the index of the variable.

#### Examples

If `c` is a `ConstraintIndex{ScalarAffineFunction,S}` and `v1` and `v2` are
`VariableIndex` objects,

```julia
set(model, ConstraintFunction(), c,
    ScalarAffineFunction(ScalarAffineTerm.([1.0, 2.0], [v1, v2]), 5.0))
set(model, ConstraintFunction(), c, SingleVariable(v1)) # Error
```
"""
function set end
# See note with get
function set(model::ModelLike,
             attr::Union{AbstractVariableAttribute,
                         AbstractConstraintAttribute},
             idxs::Vector, vector_of_values::Vector)
    if length(idxs) != length(vector_of_values)
        throw(DimensionMismatch("Number of indices ($(length(idxs))) does " *
                                "not match the number of values " *
                                "($(length(vector_of_values))) set to `$attr`."))
    end
    return set.(model, attr, idxs, vector_of_values)
end

function set(model::ModelLike, attr::AnyAttribute, args...)
    throw_set_error_fallback(model, attr, args...)
end
# throw_set_error_fallback is included so that we can return type-specific error
# messages without needing to overload set and cause ambiguity errors. For
# examples, see ConstraintSet and ConstraintFunction. throw_set_error_fallback should
# not be overloaded by users of MOI.
function throw_set_error_fallback(model::ModelLike,
                                  attr::Union{AbstractModelAttribute,
                                              AbstractOptimizerAttribute},
                                  value;
                                  error_if_supported = SetAttributeNotAllowed(attr))
    if supports(model, attr)
        throw(error_if_supported)
    else
        throw(UnsupportedAttribute(attr))
    end
end
function throw_set_error_fallback(model::ModelLike,
                                  attr::Union{AbstractVariableAttribute,
                                              AbstractConstraintAttribute},
                                  index::Index, value;
                                  error_if_supported = SetAttributeNotAllowed(attr))
    if supports(model, attr, typeof(index))
        throw(error_if_supported)
    else
        throw(UnsupportedAttribute(attr))
    end
end

"""
    SettingSingleVariableFunctionNotAllowed()

Error type that should be thrown when the user calls [`set`](@ref) to change
the [`ConstraintFunction`](@ref) of a [`SingleVariable`](@ref) constraint.
"""
struct SettingSingleVariableFunctionNotAllowed <: Exception end

"""
    submit(optimizer::AbstractOptimizer, sub::AbstractSubmittable,
           values...)::Nothing

Submit `values` to the submittable `sub` of the optimizer `optimizer`.

An [`UnsupportedSubmittable`](@ref) error is thrown if `model` does not support
the attribute `attr` (see [`supports`](@ref)) and a [`SubmitNotAllowed`](@ref)
error is thrown if it supports the submittable `sub` but it cannot be submitted.
""" # TODO add an example once we have an attribute which can be submitted, e.g. Lazy constraint
function submit end
function submit(model::ModelLike, sub::AbstractSubmittable, args...)
    if supports(model, sub)
        throw(ArgumentError(
            "Submitting $(typeof.(args)) for `$(typeof(sub))` is not valid."))
    else
        throw(UnsupportedSubmittable(sub))
    end
end

## Submittables

"""
    LazyConstraint(callback_data)

Lazy constraint `func`-in-`set` submitted as `func, set`. The optimal
solution returned by [`VariablePrimal`](@ref) will satisfy all lazy
constraints that have been submitted.

This can be submitted only from the [`LazyConstraintCallback`](@ref). The
field `callback_data` is a solver-specific callback type that is passed as the
argument to the feasible solution callback.

## Examples

Suppose `fx = MOI.SingleVariable(x)` and `fx = MOI.SingleVariable(y)`
where `x` and `y` are [`VariableIndex`](@ref)s of `optimizer`. To add a
`LazyConstraint` for `2x + 3y <= 1`, write
```julia
func = 2.0fx + 3.0fy
set = MOI.LessThan(1.0)
MOI.submit(optimizer, MOI.LazyConstraint(callback_data), func, set)
```
inside a [`LazyConstraintCallback`](@ref) of data `callback_data`.
"""
struct LazyConstraint{CallbackDataType} <: AbstractSubmittable
    callback_data::CallbackDataType
end

"""
    HeuristicSolutionStatus

An Enum of possible return values for [`submit`](@ref) with
[`HeuristicSolution`](@ref).
This informs whether the heuristic solution was accepted or rejected.
Possible values are:
* `HEURISTIC_SOLUTION_ACCEPTED`: The heuristic solution was accepted.
* `HEURISTIC_SOLUTION_REJECTED`: The heuristic solution was rejected.
* `HEURISTIC_SOLUTION_UNKNOWN`: No information available on the acceptance.
"""
@enum(HeuristicSolutionStatus,
      HEURISTIC_SOLUTION_ACCEPTED,
      HEURISTIC_SOLUTION_REJECTED,
      HEURISTIC_SOLUTION_UNKNOWN)

"""
    HeuristicSolution(callback_data)

Heuristically obtained feasible solution. The solution is submitted as
`variables, values` where `values[i]` gives the value of `variables[i]`,
similarly to [`set`](@ref). The [`submit`](@ref) call returns a
[`HeuristicSolutionStatus`](@ref) indicating whether the provided solution
was accepted or rejected.

This can be submitted only from the [`HeuristicCallback`](@ref). The
field `callback_data` is a solver-specific callback type that is passed as the
argument to the heuristic callback.

Some solvers require a complete solution, others only partial solutions.
"""
struct HeuristicSolution{CallbackDataType} <: AbstractSubmittable
    callback_data::CallbackDataType
end

"""
    UserCut(callback_data)

Constraint `func`-to-`set` suggested to help the solver detect the solution
given by [`CallbackVariablePrimal`](@ref) as infeasible. The cut is submitted
as `func, set`.
Typically [`CallbackVariablePrimal`](@ref) will violate integrality constraints,
and a cut would be of the form [`ScalarAffineFunction`](@ref)-in-[`LessThan`](@ref)
or [`ScalarAffineFunction`](@ref)-in-[`GreaterThan`](@ref). Note that, as
opposed to [`LazyConstraint`](@ref), the provided constraint cannot modify the
feasible set, the constraint should be redundant, e.g., it may be a consequence
of affine and integrality constraints.

This can be submitted only from the [`UserCutCallback`](@ref). The
field `callback_data` is a solver-specific callback type that is passed as the
argument to the infeasible solution callback.

Note that the solver may silently ignore the provided constraint.
"""
struct UserCut{CallbackDataType} <: AbstractSubmittable
    callback_data::CallbackDataType
end

"""
    struct InvalidCallbackUsage{C, S} <: Exception
        callback::C
        submittable::S
    end

An error indicating that `submittable` cannot be submitted inside `callback`.

For example, [`UserCut`](@ref) cannot be submitted inside
[`LazyConstraintCallback`](@ref).
"""
struct InvalidCallbackUsage{C, S} <: Exception
    callback::C
    submittable::S
end

function Base.showerror(io::IO, err::InvalidCallbackUsage)
    print(io, "InvalidCallbackUsage: Cannot submit $(err.submittable) inside a $(err.callback).")
end

## Optimizer attributes

"""
    ListOfOptimizerAttributesSet()

An optimizer attribute for the `Vector{AbstractOptimizerAttribute}` of all optimizer attributes that were set.
"""
struct ListOfOptimizerAttributesSet <: AbstractOptimizerAttribute end

"""
    SolverName()

An optimizer attribute for the string identifying the solver/optimizer.
"""
struct SolverName <: AbstractOptimizerAttribute end

"""
    Silent()

An optimizer attribute for silencing the output of an optimizer. When `set`
to `true`, it takes precedence over any other attribute controlling verbosity
and requires the solver to produce no output. The default value is `false`
which has no effect. In this case the verbosity is controlled by other
attributes.

## Note

Every optimizer should have verbosity on by default. For instance, if a solver
has a solver-specific log level attribute, the MOI implementation should set it
to `1` by default. If the user sets `Silent` to `true`, then the log level
should be set to `0`, even if the user specifically sets a value of log level.
If the value of `Silent` is `false` then the log level set to the solver is the
value given by the user for this solver-specific parameter or `1` if none is
given.
"""
struct Silent <: AbstractOptimizerAttribute end

"""
    TimeLimitSec()

An optimizer attribute for setting a time limit for an optimization. When `set`
to `nothing`, it deactivates the solver time limit. The default value is
`nothing`. The time limit is in seconds.
""" # TODO add a test checking if the solver returns TIME_LIMIT status when the time limit is hit
struct TimeLimitSec <: AbstractOptimizerAttribute end

"""
    RawParameter(name)

An optimizer attribute for the solver-specific parameter identified by `name`
which is typically an `Enum` or a `String`.
"""
struct RawParameter <: AbstractOptimizerAttribute
    name::Any
end

"""
    NumberOfThreads()

An optimizer attribute for setting the number of threads used for an
optimization. When set to `nothing` uses solver default. Values are positive
integers. The default value is `nothing`.
"""
struct NumberOfThreads <: AbstractOptimizerAttribute end

### Callbacks

"""
    struct OptimizeInProgress{AttrType<:AnyAttribute} <: Exception
        attr::AttrType
    end

Error thrown from optimizer when `MOI.get(optimizer, attr)` is called inside an
[`AbstractCallback`](@ref) while it is only defined once [`optimize!`](@ref) has
completed. This can only happen when `is_set_by_optimize(attr)` is `true`.
"""
struct OptimizeInProgress{AttrType<:AnyAttribute} <: Exception
    attr::AttrType
end

function Base.showerror(io::IO, err::OptimizeInProgress)
    print(io, typeof(err), ": Cannot get result as the `MOI.optimize!` has not",
          " finished.")
end

"""
    abstract type AbstractCallback <: AbstractOptimizerAttribute end

Abstract type for optimizer attribute representing a callback function. The
value set to subtypes of `AbstractCallback` is a function that may be called
during [`optimize!`](@ref). As [`optimize!`](@ref) is in progress, the result
attributes (i.e, the attributes `attr` such that `is_set_by_optimize(attr)`)
may not be accessible from the callback, hence trying to get result attributes
might throw a [`OptimizeInProgress`](@ref) error.

At most one callback of each type can be registered. If an optimizer already
has a function for a callback type, and the user registers a new function,
then the old one is replaced.

The value of the attribute should be a function taking only one argument,
commonly called `callback_data`, that can be used for instance in
[`LazyConstraintCallback`](@ref), [`HeuristicCallback`](@ref) and
[`UserCutCallback`](@ref).
"""
abstract type AbstractCallback <: AbstractOptimizerAttribute end

"""
    LazyConstraintCallback() <: AbstractCallback

The callback can be used to reduce the feasible set given the current primal
solution by submitting a [`LazyConstraint`](@ref). For instance, it may be
called at an incumbent of a mixed-integer problem. Note that there is no
guarantee that the callback is called at *every* feasible primal solution.

The feasible primal solution is accessed through
[`CallbackVariablePrimal`](@ref). Trying to access other result
attributes will throw [`OptimizeInProgress`](@ref) as discussed in
[`AbstractCallback`](@ref).

## Examples

```julia
x = MOI.add_variables(optimizer, 8)
MOI.set(optimizer, MOI.LazyConstraintCallback(), callback_data -> begin
    sol = MOI.get(optimizer, MOI.CallbackVariablePrimal(callback_data), x)
    if # should add a lazy constraint
        func = # computes function
        set = # computes set
        MOI.submit(optimizer, MOI.LazyConstraint(callback_data), func, set)
    end
end
```
"""
struct LazyConstraintCallback <: AbstractCallback end

"""
    HeuristicCallback() <: AbstractCallback

The callback can be used to submit [`HeuristicSolution`](@ref) given the
current primal solution.
For instance, it may be called at fractional (i.e., non-integer) nodes in the
branch and bound tree of a mixed-integer problem. Note that there is not
guarantee that the callback is called *everytime* the solver has an infeasible
solution.

The current primal solution is accessed through
[`CallbackVariablePrimal`](@ref). Trying to access other result
attributes will throw [`OptimizeInProgress`](@ref) as discussed in
[`AbstractCallback`](@ref).

## Examples

```julia
x = MOI.add_variables(optimizer, 8)
MOI.set(optimizer, MOI.HeuristicCallback(), callback_data -> begin
    sol = MOI.get(optimizer, MOI.CallbackVariablePrimal(callback_data), x)
    if # can find a heuristic solution
        values = # computes heuristic solution
        MOI.submit(optimizer, MOI.HeuristicSolution(callback_data), x,
                   values)
    end
end
```
"""
struct HeuristicCallback <: AbstractCallback end

"""
    UserCutCallback() <: AbstractCallback

The callback can be used to submit [`UserCut`](@ref) given the current primal
solution. For instance, it may be called at fractional (i.e., non-integer) nodes
in the branch and bound tree of a mixed-integer problem. Note that there is not
guarantee that the callback is called *everytime* the solver has an infeasible
solution.

The infeasible solution is accessed through
[`CallbackVariablePrimal`](@ref). Trying to access other result
attributes will throw [`OptimizeInProgress`](@ref) as discussed in
[`AbstractCallback`](@ref).

## Examples

```julia
x = MOI.add_variables(optimizer, 8)
MOI.set(optimizer, MOI.UserCutCallback(), callback_data -> begin
    sol = MOI.get(optimizer, MOI.CallbackVariablePrimal(callback_data), x)
    if # can find a user cut
        func = # computes function
        set = # computes set
        MOI.submit(optimizer, MOI.UserCut(callback_data), func, set)
    end
end
```
"""
struct UserCutCallback <: AbstractCallback end

## Model attributes

"""
    ListOfModelAttributesSet()

A model attribute for the `Vector{AbstractModelAttribute}` of all model
attributes `attr` such that 1) `is_copyable(attr)` returns `true` and 2) the
attribute was set to the model.
"""
struct ListOfModelAttributesSet <: AbstractModelAttribute end

"""
    Name()

A model attribute for the string identifying the model. It has a default value
of `""` if not set`.
"""
struct Name <: AbstractModelAttribute end

"""
    ObjectiveSense()

A model attribute for the objective sense of the objective function, which
must be an `OptimizationSense`: `MIN_SENSE`, `MAX_SENSE`, or
`FEASIBILITY_SENSE`. The default is `FEASIBILITY_SENSE`.
"""
struct ObjectiveSense <: AbstractModelAttribute end

@enum OptimizationSense MIN_SENSE MAX_SENSE FEASIBILITY_SENSE

"""
    NumberOfVariables()

A model attribute for the number of variables in the model.
"""
struct NumberOfVariables <: AbstractModelAttribute end

"""
    ListOfVariableIndices()

A model attribute for the `Vector{VariableIndex}` of all variable indices present in the model
(i.e., of length equal to the value of `NumberOfVariables()`) in the order in
which they were added.
"""
struct ListOfVariableIndices <: AbstractModelAttribute end

"""
    ListOfConstraintIndices{F,S}()

A model attribute for the `Vector{ConstraintIndex{F,S}}` of all constraint indices of type
`F`-in-`S` in the model (i.e., of length equal to the value of
`NumberOfConstraints{F,S}()`) in the order in which they were added.
"""
struct ListOfConstraintIndices{F,S} <: AbstractModelAttribute end

"""
    NumberOfConstraints{F,S}()

A model attribute for the number of constraints of the type `F`-in-`S` present in the model.
"""
struct NumberOfConstraints{F,S} <: AbstractModelAttribute end

"""
    ListOfConstraints()

A model attribute for the list of tuples of the form `(F,S)`, where `F` is a function type
and `S` is a set type indicating that the attribute `NumberOfConstraints{F,S}()`
has value greater than zero.
"""
struct ListOfConstraints <: AbstractModelAttribute end

"""
    ObjectiveFunction{F<:AbstractScalarFunction}()

A model attribute for the objective function which has a type `F<:AbstractScalarFunction`.
`F` should be guaranteed to be equivalent but not necessarily identical to the function type provided by the user.
Throws an `InexactError` if the objective function cannot be converted to `F`,
e.g. the objective function is quadratic and `F` is `ScalarAffineFunction{Float64}` or
it has non-integer coefficient and `F` is `ScalarAffineFunction{Int}`.
"""
struct ObjectiveFunction{F<:AbstractScalarFunction} <: AbstractModelAttribute end

"""
    ObjectiveFunctionType()

A model attribute for the type `F` of the objective function set using the
`ObjectiveFunction{F}` attribute.

## Examples

In the following code, `attr` should be equal to `MOI.SingleVariable`:
```julia
x = MOI.add_variable(model)
MOI.set(model, MOI.ObjectiveFunction{MOI.SingleVariable}(),
         MOI.SingleVariable(x))
attr = MOI.get(model, MOI.ObjectiveFunctionType())
```
"""
struct ObjectiveFunctionType <: AbstractModelAttribute end

## Optimizer attributes

"""
    ObjectiveValue(resultidx::Int=1)

A model attribute for the objective value of the `result_index`th primal result.
"""
struct ObjectiveValue <: AbstractModelAttribute
    result_index::Int
    (::Type{ObjectiveValue})(result_index=1) = new(result_index)
end

"""
    DualObjectiveValue(result_index::Int=1)

A model attribute for the value of the objective function of the dual problem
for the `result_index`th dual result.
"""
struct DualObjectiveValue <: AbstractModelAttribute
    result_index::Int
    (::Type{DualObjectiveValue})(result_index=1) = new(result_index)
end

"""
    ObjectiveBound()

A model attribute for the best known bound on the optimal objective value.
"""
struct ObjectiveBound <: AbstractModelAttribute end

"""
    RelativeGap()

A model attribute for the final relative optimality gap, defined as ``\\frac{|b-f|}{|f|}``, where ``b`` is the best bound and ``f`` is the best feasible objective value.
"""
struct RelativeGap <: AbstractModelAttribute  end

"""
    SolveTime()

A model attribute for the total elapsed solution time (in seconds) as reported by the optimizer.
"""
struct SolveTime <: AbstractModelAttribute end

"""
    SimplexIterations()

A model attribute for the cumulative number of simplex iterations during the optimization process.
In particular, for a mixed-integer program (MIP), the total simplex iterations for all nodes.
"""
struct SimplexIterations <: AbstractModelAttribute end

"""
    BarrierIterations()

A model attribute for the cumulative number of barrier iterations while solving a problem.
"""
struct BarrierIterations <: AbstractModelAttribute end

"""
    NodeCount()

A model attribute for the total number of branch-and-bound nodes explored while solving a mixed-integer program (MIP).
"""
struct NodeCount <: AbstractModelAttribute end

"""
    RawSolver()

A model attribute for the object that may be used to access a solver-specific API for this optimizer.
"""
struct RawSolver <: AbstractModelAttribute end

"""
    ResultCount()

A model attribute for the number of results available.
"""
struct ResultCount <: AbstractModelAttribute end

## Variable attributes

"""
    ListOfVariableAttributesSet()

A model attribute for the `Vector{AbstractVariableAttribute}` of all variable
attributes `attr` such that 1) `is_copyable(attr)` returns `true` and 2) the
attribute was set to variables.
"""
struct ListOfVariableAttributesSet <: AbstractModelAttribute end

"""
    VariableName()

A variable attribute for a string identifying the variable. It is *valid* for
two variables to have the same name; however, variables with duplicate names
cannot be looked up using [`get`](@ref). It has a default value of `""` if not
set`.
"""
struct VariableName <: AbstractVariableAttribute end

"""
    VariablePrimalStart()

A variable attribute for the initial assignment to some primal variable's value that the optimizer may use to warm-start the solve.
"""
struct VariablePrimalStart <: AbstractVariableAttribute end

"""
    VariablePrimal(N)
    VariablePrimal()

A variable attribute for the assignment to some primal variable's value in result `N`.
If `N` is omitted, it is 1 by default.
"""
struct VariablePrimal <: AbstractVariableAttribute
    N::Int
end
VariablePrimal() = VariablePrimal(1)
_result_index_field(attr::VariablePrimal) = attr.N

"""
    CallbackVariablePrimal(callback_data)

A variable attribute for the assignment to some primal variable's value during
the callback identified by `callback_data`.
"""
struct CallbackVariablePrimal{CallbackDataType} <: AbstractVariableAttribute
    callback_data::CallbackDataType
end
is_set_by_optimize(::CallbackVariablePrimal) = true

"""
    BasisStatusCode

An Enum of possible values for the `ConstraintBasisStatus` attribute.
This explains the status of a given element with respect to an optimal solution basis.
Possible values are:
* `BASIC`: element is in the basis
* `NONBASIC`: element is not in the basis
* `NONBASIC_AT_LOWER`: element is not in the basis and is at its lower bound
* `NONBASIC_AT_UPPER`: element is not in the basis and is at its upper bound
* `SUPER_BASIC`: element is not in the basis but is also not at one of its
  bounds

Note: `NONBASIC_AT_LOWER` and `NONBASIC_AT_UPPER` should be used only for
constraints with the `Interval`. In this case cases they are necessary to
distinguish which side of the constraint. One-sided constraints (e.g.,
`LessThan` and `GreaterThan`) should use `NONBASIC` instead of the
`NONBASIC_AT_*` values.
"""
@enum(BasisStatusCode, BASIC, NONBASIC, NONBASIC_AT_LOWER, NONBASIC_AT_UPPER,
      SUPER_BASIC)

## Constraint attributes

"""
    ListOfConstraintAttributesSet{F, S}()

A model attribute for the `Vector{AbstractConstraintAttribute}` of all
constraint attributes `attr` such that 1) `is_copyable(attr)` returns `true` and
2) the attribute was set to `F`-in-`S` constraints.

## Note

The attributes [`ConstraintFunction`](@ref) and [`ConstraintSet`](@ref) should
not be included in the list even if then have been set with [`set`](@ref).
"""
struct ListOfConstraintAttributesSet{F,S} <: AbstractModelAttribute end

"""
    ConstraintName()

A constraint attribute for a string identifying the constraint. It is *valid*
for constraints variables to have the same name; however, constraints with
duplicate names cannot be looked up using [`get`](@ref) regardless of if they
have the same `F`-in-`S` type. It has a default value of `""` if not set.
"""
struct ConstraintName <: AbstractConstraintAttribute end

"""
    ConstraintPrimalStart()

A constraint attribute for the initial assignment to some constraint's primal value(s) that the optimizer may use to warm-start the solve.
"""
struct ConstraintPrimalStart <: AbstractConstraintAttribute end

"""
    ConstraintDualStart()

A constraint attribute for the initial assignment to some constraint's dual value(s) that the optimizer may use to warm-start the solve.
"""
struct ConstraintDualStart <: AbstractConstraintAttribute end

"""
    ConstraintPrimal(N)
    ConstraintPrimal()

A constraint attribute for the assignment to some constraint's primal value(s) in result `N`.
If `N` is omitted, it is 1 by default.

Given a constraint `function-in-set`, the `ConstraintPrimal` is the value of the
function evaluated at the primal solution of the variables. For example, given
the constraint `ScalarAffineFunction([x,y], [1, 2], 3)`-in-`Interval(0, 20)` and
a primal solution of `(x,y) = (4,5)`, the `ConstraintPrimal` solution of the
constraint is `1 * 4 + 2 * 5 + 3 = 17`.
"""
struct ConstraintPrimal <: AbstractConstraintAttribute
    N::Int
end
ConstraintPrimal() = ConstraintPrimal(1)
_result_index_field(attr::ConstraintPrimal) = attr.N

"""
    ConstraintDual(N)
    ConstraintDual()

A constraint attribute for the assignment to some constraint's dual value(s) in result `N`.
If `N` is omitted, it is 1 by default.
"""
struct ConstraintDual <: AbstractConstraintAttribute
    N::Int
end
ConstraintDual() = ConstraintDual(1)
_result_index_field(attr::ConstraintDual) = attr.N

"""
    ConstraintBasisStatus(result_index)
    ConstraintBasisStatus()

A constraint attribute for the `BasisStatusCode` of some constraint in result
`result_index`, with respect to an available optimal solution basis. If
`result_index` is omitted, it is 1 by default.

**For the basis status of a variable, query the corresponding `SingleVariable`
constraint that enforces the variable's bounds.**
"""
struct ConstraintBasisStatus <: AbstractConstraintAttribute
    result_index::Int
end
ConstraintBasisStatus() = ConstraintBasisStatus(1)

"""
    ConstraintFunction()

A constraint attribute for the `AbstractFunction` object used to define the constraint.
It is guaranteed to be equivalent but not necessarily identical to the function provided by the user.
"""
struct ConstraintFunction <: AbstractConstraintAttribute end

function throw_set_error_fallback(::ModelLike, attr::ConstraintFunction,
                                  ::ConstraintIndex{F, S}, ::F;
                                  error_if_supported = SetAttributeNotAllowed(attr)) where {F <: AbstractFunction, S}
    throw(error_if_supported)
end
func_type(c::ConstraintIndex{F, S}) where {F, S} = F
function throw_set_error_fallback(::ModelLike, ::ConstraintFunction,
                                  constraint_index::ConstraintIndex,
                                  func::AbstractFunction;
                                  kwargs...)
    throw(ArgumentError("""Cannot modify functions of different types.
    Constraint type is $(func_type(constraint_index)) while the replacement
    function is of type $(typeof(func))."""))
end

"""
    ConstraintSet()

A constraint attribute for the `AbstractSet` object used to define the constraint.
"""
struct ConstraintSet <: AbstractConstraintAttribute end

function throw_set_error_fallback(::ModelLike, attr::ConstraintSet,
                                  ::ConstraintIndex{F, S}, ::S;
                                  error_if_supported = SetAttributeNotAllowed(attr)) where {F, S <: AbstractSet}
    throw(error_if_supported)
end
set_type(::ConstraintIndex{F, S}) where {F, S} = S
function throw_set_error_fallback(::ModelLike, ::ConstraintSet,
                                  constraint_index::ConstraintIndex,
                                  set::AbstractSet;
                                  kwargs...)
    throw(ArgumentError("""Cannot modify sets of different types. Constraint
    type is $(set_type(constraint_index)) while the replacement set is of
    type $(typeof(set)). Use `transform` instead."""))
end

## Termination status
"""
    TerminationStatus()

A model attribute for the `TerminationStatusCode` explaining why the optimizer stopped.
"""
struct TerminationStatus <: AbstractModelAttribute end

"""
    TerminationStatusCode

An Enum of possible values for the `TerminationStatus` attribute. This attribute
is meant to explain the reason why the optimizer stopped executing in the most
recent call to [`optimize!`](@ref).

If no call has been made to [`optimize!`](@ref), then the `TerminationStatus`
is:

* `OPTIMIZE_NOT_CALLED`: The algorithm has not started.

## OK

These are generally OK statuses, i.e., the algorithm ran to completion normally.

* `OPTIMAL`: The algorithm found a globally optimal solution.
* `INFEASIBLE`: The algorithm concluded that no feasible solution exists.
* `DUAL_INFEASIBLE`: The algorithm concluded that no dual bound exists for the
  problem. If, additionally, a feasible (primal) solution is known to
  exist, this status typically implies that the problem is unbounded, with some
  technical exceptions.
* `LOCALLY_SOLVED`: The algorithm converged to a stationary point, local
  optimal solution, could not find directions for improvement, or otherwise
  completed its search without global guarantees.
* `LOCALLY_INFEASIBLE`: The algorithm converged to an infeasible point or
  otherwise completed its search without finding a feasible solution, without
  guarantees that no feasible solution exists.
* `INFEASIBLE_OR_UNBOUNDED`: The algorithm stopped because it decided that the
  problem is infeasible or unbounded; this occasionally happens during MIP
  presolve.

## Solved to relaxed tolerances

* `ALMOST_OPTIMAL`: The algorithm found a globally optimal solution to relaxed
  tolerances.
* `ALMOST_INFEASIBLE`: The algorithm concluded that no feasible solution exists
  within relaxed tolerances.
* `ALMOST_DUAL_INFEASIBLE`: The algorithm concluded that no dual bound exists for
  the problem within relaxed tolerances.
* `ALMOST_LOCALLY_SOLVED`: The algorithm converged to a stationary point, local
  optimal solution, or could not find directions for improvement within relaxed
  tolerances.

## Limits

The optimizer stopped because of some user-defined limit.

* `ITERATION_LIMIT`: An iterative algorithm stopped after conducting the maximum
  number of iterations.
* `TIME_LIMIT`: The algorithm stopped after a user-specified computation time.
* `NODE_LIMIT`: A branch-and-bound algorithm stopped because it explored a
  maximum number of nodes in the branch-and-bound tree.
* `SOLUTION_LIMIT`: The algorithm stopped because it found the required number of
  solutions. This is often used in MIPs to get the solver to return the first
  feasible solution it encounters.
* `MEMORY_LIMIT`: The algorithm stopped because it ran out of memory.
* `OBJECTIVE_LIMIT`: The algorthm stopped because it found a solution better than
  a minimum limit set by the user.
* `NORM_LIMIT`: The algorithm stopped because the norm of an iterate became too
  large.
* `OTHER_LIMIT`: The algorithm stopped due to a limit not covered by one of the
  above.

## Problematic

This group of statuses means that something unexpected or problematic happened.

* `SLOW_PROGRESS`: The algorithm stopped because it was unable to continue making
  progress towards the solution.
* `NUMERICAL_ERROR`: The algorithm stopped because it encountered unrecoverable
  numerical error.
* `INVALID_MODEL`: The algorithm stopped because the model is invalid.
* `INVALID_OPTION`: The algorithm stopped because it was provided an invalid
  option.
* `INTERRUPTED`: The algorithm stopped because of an interrupt signal.
* `OTHER_ERROR`: The algorithm stopped because of an error not covered by one of
  the statuses defined above.
"""
@enum(TerminationStatusCode,
    OPTIMIZE_NOT_CALLED,
    # OK
    OPTIMAL, INFEASIBLE, DUAL_INFEASIBLE, LOCALLY_SOLVED, LOCALLY_INFEASIBLE,
    INFEASIBLE_OR_UNBOUNDED,
    # Solved to relaxed tolerances
    ALMOST_OPTIMAL, ALMOST_INFEASIBLE, ALMOST_DUAL_INFEASIBLE,
    ALMOST_LOCALLY_SOLVED,
    # Limits
    ITERATION_LIMIT, TIME_LIMIT,  NODE_LIMIT, SOLUTION_LIMIT, MEMORY_LIMIT,
    OBJECTIVE_LIMIT, NORM_LIMIT, OTHER_LIMIT,
    # Problematic
    SLOW_PROGRESS, NUMERICAL_ERROR, INVALID_MODEL, INVALID_OPTION, INTERRUPTED,
    OTHER_ERROR
)

"""
    RawStatusString()

A model attribute for a solver specific string explaining why the optimizer
stopped.
"""
struct RawStatusString <: AbstractModelAttribute end

## Result status

"""
    ResultStatusCode

An Enum of possible values for the `PrimalStatus` and `DualStatus` attributes.
The values indicate how to interpret the result vector.

* `NO_SOLUTION`: the result vector is empty.
* `FEASIBLE_POINT`: the result vector is a feasible point.
* `NEARLY_FEASIBLE_POINT`: the result vector is feasible if some constraint
  tolerances are relaxed.
* `INFEASIBLE_POINT`: the result vector is an infeasible point.
* `INFEASIBILITY_CERTIFICATE`: the result vector is an infeasibility certificate.
  If the `PrimalStatus` is `INFEASIBILITY_CERTIFICATE`, then the primal result
  vector is a certificate of dual infeasibility. If the `DualStatus` is
  `INFEASIBILITY_CERTIFICATE`, then the dual result vector is a proof of primal
  infeasibility.
* `NEARLY_INFEASIBILITY_CERTIFICATE`: the result satisfies a relaxed criterion for
  a certificate of infeasibility.
* `REDUCTION_CERTIFICATE`: the result vector is an ill-posed certificate; see
  [this article](https://arxiv.org/abs/1408.4685) for details. If the
  `PrimalStatus` is `REDUCTION_CERTIFICATE`, then the primal result vector is a
  proof that the dual problem is ill-posed. If the `DualStatus` is
  `REDUCTION_CERTIFICATE`, then the dual result vector is a proof that the primal
  is ill-posed.
* `NEARLY_REDUCTION_CERTIFICATE`: the result satisfies a relaxed criterion for
  an ill-posed certificate.
* `UNKNOWN_RESULT_STATUS`: the result vector contains a solution with an unknown
  interpretation.
* `OTHER_RESULT_STATUS`: the result vector contains a solution with an
  interpretation not covered by one of the statuses defined above.
"""
@enum(ResultStatusCode, NO_SOLUTION,
      FEASIBLE_POINT, NEARLY_FEASIBLE_POINT, INFEASIBLE_POINT,
      INFEASIBILITY_CERTIFICATE, NEARLY_INFEASIBILITY_CERTIFICATE,
      REDUCTION_CERTIFICATE, NEARLY_REDUCTION_CERTIFICATE,
      UNKNOWN_RESULT_STATUS, OTHER_RESULT_STATUS)

"""
    PrimalStatus(N)
    PrimalStatus()

A model attribute for the `ResultStatusCode` of the primal result `N`.
If `N` is omitted, it defaults to 1. If `N` is larger than the value of
[`ResultCount`](@ref) then `NO_SOLUTION` is returned.
"""
struct PrimalStatus <: AbstractModelAttribute
    N::Int
end
PrimalStatus() = PrimalStatus(1)
_result_index_field(attr::PrimalStatus) = attr.N

"""
    DualStatus(N)
    DualStatus()

A model attribute for the `ResultStatusCode` of the dual result `N`.
If `N` is omitted, it defaults to 1. If `N` is larger than the value of
[`ResultCount`](@ref) then `NO_SOLUTION` is returned.
"""
struct DualStatus <: AbstractModelAttribute
    N::Int
end
DualStatus() = DualStatus(1)
_result_index_field(attr::DualStatus) = attr.N

"""
    is_set_by_optimize(::AnyAttribute)

Return a `Bool` indicating whether the value of the attribute is modified
during an [`optimize!`](@ref) call, that is, the attribute is used to query
the result of the optimization.

## Important note when defining new attributes

This function returns `false` by default so it should be implemented for
attributes that are modified by [`optimize!`](@ref).
"""
is_set_by_optimize(::AnyAttribute) = false
function is_set_by_optimize(::Union{ObjectiveValue,
                                    DualObjectiveValue,
                                    ObjectiveBound,
                                    RelativeGap,
                                    SolveTime,
                                    SimplexIterations,
                                    BarrierIterations,
                                    NodeCount,
                                    RawSolver,
                                    ResultCount,
                                    TerminationStatus,
                                    RawStatusString,
                                    PrimalStatus,
                                    DualStatus,
                                    VariablePrimal,
                                    ConstraintPrimal,
                                    ConstraintDual,
                                    ConstraintBasisStatus})
    return true
end

"""
    is_copyable(::AnyAttribute)

Return a `Bool` indicating whether the value of the attribute may be copied
during [`copy_to`](@ref) using [`set`](@ref).

## Important note when defining new attributes

By default `is_copyable(attr)` returns `!is_set_by_optimize(attr)`. A specific
method should be defined for attributes which are copied indirectly during
[`copy_to`](@ref). For instance, both `is_copyable` and
[`is_set_by_optimize`](@ref) return `false` for the following attributes:

* [`ListOfOptimizerAttributesSet`](@ref), [`ListOfModelAttributesSet`](@ref),
  [`ListOfConstraintAttributesSet`](@ref) and
  [`ListOfVariableAttributesSet`](@ref).
* [`SolverName`](@ref) and [`RawSolver`](@ref): these attributes cannot be set.
* [`NumberOfVariables`](@ref) and [`ListOfVariableIndices`](@ref): these
  attributes are set indirectly by [`add_variable`](@ref) and
  [`add_variables`](@ref).
* [`ObjectiveFunctionType`](@ref): this attribute is set indirectly when setting
  the [`ObjectiveFunction`](@ref) attribute.
* [`NumberOfConstraints`](@ref), [`ListOfConstraintIndices`](@ref),
  [`ListOfConstraints`](@ref), [`ConstraintFunction`](@ref) and
  [`ConstraintSet`](@ref): these attributes are set indirectly by
  [`add_constraint`](@ref) and [`add_constraints`](@ref).
"""
function is_copyable(attr::AnyAttribute)
    return !is_set_by_optimize(attr)
end
function is_copyable(::Union{ListOfOptimizerAttributesSet,
                             ListOfModelAttributesSet,
                             ListOfConstraintAttributesSet,
                             ListOfVariableAttributesSet,
                             SolverName,
                             RawSolver,
                             NumberOfVariables,
                             ListOfVariableIndices,
                             NumberOfConstraints,
                             ObjectiveFunctionType,
                             ListOfConstraintIndices,
                             ListOfConstraints,
                             ConstraintFunction,
                             ConstraintSet})
    return false
end


text:
# BigBed File Format
# ==================
#
# The BigWig/BigBed format is documented in Kent, W. James, et al. "BigWig and BigBed: enabling browsing of large distributed datasets." Bioinformatics 26.17 (2010): 2204-2207.
#
# This file is a part of BioJulia.
# License is MIT: https://github.com/BioJulia/Bio.jl/blob/master/LICENSE.md

module BigBed

using BED
using BBI
using BioCore

import Automa
import Automa.RegExp: @re_str
import BufferedStreams
import ColorTypes
import FixedPointNumbers: N0f8
import GenomicFeatures: GenomicFeatures, Interval, seqname, leftposition, rightposition
import Libz

include("reader.jl")
include("writer.jl")
include("record.jl")
include("overlap.jl")

end # module