text:
function __init__()
	register(
		DataDep(
			"annthyroid",
			"""
			Dataset: ANNThyroid
			Authors: Ross Quinlan
			Website: https://archive.ics.uci.edu/ml/datasets/Thyroid+Disease

			[Quinlan, 1987]
				Quinlan, John Ross, et al.
				"Inductive knowledge acquisition: a case study."
				Proceedings of the Second Australian Conference on Applications of expert systems. 1987.

			The original thyroid disease (ann-thyroid) dataset from UCI machine learning repository is
			a classification dataset, which is suited for training ANNs. It has 3772 training instances
			and 3428 testing instances. It has 15 categorical and 6 real attributes. The problem is to
			determine whether a patient referred to the clinic is hypothyroid. Therefore three classes
			are built: normal (not hypothyroid), hyperfunction and subnormal functioning.
			""",
			[
				"https://archive.ics.uci.edu/ml/machine-learning-databases/thyroid-disease/ann-train.data",
				"https://archive.ics.uci.edu/ml/machine-learning-databases/thyroid-disease/ann-test.data"
			],
			"fbecd00c2ab44e0c95fb135e9c2f39d597fb8074dfaa8581ac2f889c927d40ad"
		))
	# this ensures eager loading (upon first usage of the package)
	# datadep"annthyroid"
	# we dont do that no more

	register(
		DataDep(
			"arrhythmia",
			"""
			Dataset: Arrhythmia
			Authors: H. Altay Guvenir
			Website: https://archive.ics.uci.edu/ml/datasets/Arrhythmia

			[Guvenir, 1997]
				Guvenir, H. Altay, et al.
				"A supervised machine learning algorithm for arrhythmia analysis."
				Computers in Cardiology 1997. IEEE, 1997.

			The aim is to distinguish between the presence and absence of cardiac arrhythmia and to classify
			it in one of the 16 groups. Class 01 refers to 'normal' ECG classes 02 to 15 refers to different
			classes of arrhythmia and class 16 refers to the rest of unclassified ones. For the time being,
			there exists a computer program that makes such a classification. However there are differences
			between the cardiolog's and the programs classification. Taking the cardiolog's as a gold standard
			we aim to minimise this difference by means of machine learning tools.
			""",
			"https://archive.ics.uci.edu/ml/machine-learning-databases/arrhythmia/arrhythmia.data",
			"a7f0f4ca289a4c58b5ed85a9adb793189acd38425828ce3dfbb70adb45f30169"
		))

	register(
		DataDep(
			"htru2",
			"""
			Dataset: HTRU2
			Authors: Dr. Robert Lyon
			Website: https://archive.ics.uci.edu/ml/datasets/HTRU2

			[Keith, 2010]
				M. J. Keith et al.
				"The High Time Resolution Universe Pulsar Survey - I. System Configuration and Initial Discoveries."
				onthly Notices of the Royal Astronomical Society, 2010.

			HTRU2 is a data set which describes a sample of pulsar candidates collected during
			the High Time Resolution Universe Survey (South).
			""",
			"https://archive.ics.uci.edu/ml/machine-learning-databases/00372/HTRU2.zip",
			"ba442c076dd22a8952700f26e38499fc1806037dcf7bea0e125e6bfba393f379",
			post_fetch_method = unpack
		))

	register(
		DataDep(
			"kdd99",
			"""
			Dataset: KDD Cup 1999
			Authors: Stolfo et al.
			Website: http://kdd.ics.uci.edu/databases/kddcup99/kddcup99.html

			[Stolfo, 2000]
				 S. J. Stolfo, W. Fan, W. Lee, A. Prodromidis, and P. K. Chan.
				"Costbased modeling for fraud and intrusion detection: Results from the jam project."
				discex, 2000.

			This is the data set used for The Third International Knowledge Discovery and Data Mining Tools
			Competition, which was held in conjunction with KDD-99 The Fifth International Conference on
			Knowledge Discovery and Data Mining. The competition task was to build a network intrusion
			detector, a predictive model capable of distinguishing between ``bad'' connections, called
			intrusions or attacks, and ``good'' normal connections. This database contains a standard set
			of data to be audited, which includes a wide variety of intrusions simulated in a military
			network environment.

			!!!!!!!!!!!!!!!!!!!!

			WARNING --- for some users, the automatic unpacking may fail. Run this instead:

			mkdir ~/.julia/datadeps/kdd99
			cd ~/.julia/datadeps/kdd99
			wget http://kdd.ics.uci.edu/databases/kddcup99/kddcup.data.gz
			wget http://kdd.ics.uci.edu/databases/kddcup99/kddcup.data_10_percent.gz
			gzip -d kddcup.data.gz
			gzip -d kddcup.data_10_percent.gz

			!!!!!!!!!!!!!!!!!!!!
			""",
			["http://kdd.ics.uci.edu/databases/kddcup99/kddcup.data.gz",
			"http://kdd.ics.uci.edu/databases/kddcup99/kddcup.data_10_percent.gz"],
			"bb29388a787b1cea818a6fd427d14ad45b556176b5fbf13257ca33c1d2dad7f3",
			post_fetch_method = unpack
		))

	register(
		DataDep(
			"spambase",
			"""
			Dataset: Spambase
			Authors: Mark Hopkins, Erik Reeber, George Forman, Jaap Suermondt
			Website: https://archive.ics.uci.edu/ml/datasets/spambase

			Our collection of spam e-mails came from our postmaster and individuals who had filed spam.
			Our collection of non-spam e-mails came from filed work and personal e-mails, and hence
			the word 'george' and the area code '650' are indicators of non-spam. These are useful when
			constructing a personalized spam filter. One would either have to blind such non-spam indicators
			or get a very wide collection of non-spam to generate a general purpose spam filter.
			""",
			"https://archive.ics.uci.edu/ml/machine-learning-databases/spambase/spambase.data",
			"b1ef93de71f97714d3d7d4f58fc9f718da7bbc8ac8a150eff2778616a8097b12"
		))

	register(
		DataDep(
			"mammography",
			"""
			Dataset: Mammography
			Authors: Tobias Kuehn
			Website: https://www.openml.org/d/310

			Mammography dataset.
			""",
			"https://www.openml.org/data/get_csv/52214/phpn1jVwe",
			"d39362668aa89b2b48aa0e6c2d12277850ace4c390e15b89862b7717f1525f0c"
			))

	register(
		DataDep(
			"har",
			"""
			Dataset: Human Activity Recognition
			Authors: Jorge L. Reyes
			Website: https://archive.ics.uci.edu/ml/datasets/human+activity+recognition+using+smartphones

			Human Activity Recognition dataset. Normal class = "WALKING", anomalous class = other.
			""",
			"https://archive.ics.uci.edu/ml/machine-learning-databases/00240/UCI%20HAR%20Dataset.zip",
			"2045e435c955214b38145fb5fa00776c72814f01b203fec405152dac7d5bfeb0",
			post_fetch_method = unpack
			))

	register(
		DataDep(
			"seismic",
			"""
			Dataset: Seismic activity
			Authors: Marek Sikora
			Website: https://archive.ics.uci.edu/ml/datasets/seismic-bumps

			Seismic bumps from mining activity.
			""",
			"https://archive.ics.uci.edu/ml/machine-learning-databases/00266/seismic-bumps.arff",
			"aabe512fab65b36d1dfb462650b75cfd8d99d8cc2723e8ecb4e6f5e1caccd5a7",
			))

	register(
		DataDep(
			"MNIST-C",
			"""
			Dataset: MNIST-C
			Authors: Norman Mu, Justin Gilmer
			Website: https://github.com/google-research/mnist-c

			Corrupted MNIST dataset.
			""",
			"https://zenodo.org/record/3239543/files/mnist_c.zip?download=1",
			"af9ee8c6a815870c7fdde5af84c7bf8db0bcfa1f41056db83871037fba70e493",
			post_fetch_method = unpack
			))

	register(
		DataDep(
			"MVTec-AD",
			"""
			Dataset: MVTec-AD
			Authors: Paul Bergmann, Michael Fauser, David Sattlegger, Carsten Steger
			Website: https://www.mvtec.com/company/research/datasets/mvtec-ad

			MVTec AD is a dataset for benchmarking anomaly detection methods with a focus on industrial inspection. It contains over 5000 high-resolution images divided into fifteen different object and texture categories. Each category comprises a set of defect-free training images and a test set of images with various kinds of defects as well as images without defects.
			""",
			"ftp://guest:GU%2E205dldo@ftp.softronics.ch/mvtec_anomaly_detection/mvtec_anomaly_detection.tar.xz",
			"cf4313b13603bec67abb49ca959488f7eedce2a9f7795ec54446c649ac98cd3d",
			post_fetch_method = unpack
			))
end


text:
import Base: isempty

export ResetMap,
       get_A,
       get_b

"""
    ResetMap{N<:Real, S<:LazySet{N}} <: LazySet{N}

Type that represents a lazy reset map.
A reset map is a special case of an affine map ``A x + b, x ∈ X`` where the
linear map ``A`` is the identity matrix with zero entries in all reset
dimensions, and the translation vector ``b`` is zero in all other dimensions.

### Fields

- `X`      -- convex set
- `resets` -- resets (a mapping from an index to a new value)

### Example

```jldoctest resetmap
julia> X = BallInf([2.0, 2.0, 2.0], 1.0);

julia> r = Dict(1 => 4.0, 3 => 0.0);

julia> rm = ResetMap(X, r);

```

Here `rm` modifies the set `X` such that `x1` is reset to 4 and `x3` is reset to
0, while `x2` is not modified.
Hence `rm` is equivalent to the set
`Hyperrectangle([4.0, 2.0, 0.0], [0.0, 1.0, 0.0])`, i.e., an axis-aligned line
segment embedded in 3D.

The corresponding affine map ``A x + b`` would be:

```math
    \begin{pmatrix} 0 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 0 \end{pmatrix} x +
    \begin{pmatrix} 4 & 0 & 0 \end{pmatrix}
```

Use the function `get_A` (resp. `get_b`) to create the matrix `A` (resp. vector
`b`) corresponding to a given reset map.

The (in this case unique) support vector of `rm` in direction `ones(3)` is:

```jldoctest resetmap
julia> σ(ones(3), rm)
3-element Array{Float64,1}:
 4.0
 3.0
 0.0
```
"""
struct ResetMap{N<:Real, S<:LazySet{N}} <: LazySet{N}
    X::S
    resets::Dict{Int, N}
end

"""
    get_A(rm::ResetMap{N}) where {N<:Real}

Return the ``A`` matrix of the affine map ``A x + b, x ∈ X`` represented by a
reset map.

### Input

- `rm` -- reset map

### Output

The (sparse) square matrix for the affine map ``A x + b, x ∈ X`` represented by
the reset map.

### Algorithm

We construct the identity matrix and set all entries in the reset dimensions to
zero.
"""
function get_A(rm::ResetMap{N}) where {N<:Real}
    n = dim(rm)
    A = sparse(N(1)*I, n, n)
    for i in keys(rm.resets)
        A[i, i] = zero(N)
    end
    return A
end

"""
    get_b(rm::ResetMap{N}) where {N<:Real}

Return the ``b`` vector of the affine map ``A x + b, x ∈ X`` represented by a
reset map.

### Input

- `rm` -- reset map

### Output

The (sparse) vector for the affine map ``A x + b, x ∈ X`` represented by the
reset map.
The vector contains the reset value for all reset dimensions, and is zero for
all other dimensions.
"""
function get_b(rm::ResetMap{N}) where {N<:Real}
    n = dim(rm)
    b = sparsevec(Int[], N[], n)
    for (i, val) in rm.resets
        b[i] = val
    end
    return b
end

# --- LazySet interface functions ---

"""
    dim(rm::ResetMap)

Return the dimension of a reset map.

### Input

- `rm` -- reset map

### Output

The dimension of a reset map.
"""
function dim(rm::ResetMap)::Int
    return dim(rm.X)
end

"""
    σ(d::AbstractVector{N}, rm::ResetMap{N}) where {N<:Real}

Return the support vector of a reset map.

### Input

- `d`  -- direction
- `rm` -- reset map

### Output

The support vector in the given direction.
If the direction has norm zero, the result depends on the wrapped set.
"""
function σ(d::AbstractVector{N}, rm::ResetMap{N}) where {N<:Real}
    d_reset = copy(d)
    for var in keys(rm.resets)
        d_reset[var] = zero(N)
    end
    return substitute(rm.resets, σ(d_reset, rm.X))
end

"""
    ρ(d::AbstractVector{N}, rm::ResetMap{N}) where {N<:Real}

Return the support function of a reset map.

### Input

- `d`  -- direction
- `rm` -- reset map

### Output

The support function in the given direction.

### Notes

We use the usual dot-product definition, but for unbounded sets we redefine the
product between ``0`` and ``±∞`` as ``0``; Julia returns `NaN` here.

```jldoctest
julia> Inf * 0.0
NaN
```

See the discussion
[here](https://math.stackexchange.com/questions/28940/why-is-infty-cdot-0-not-clearly-equal-to-0).
"""
function ρ(d::AbstractVector{N}, rm::ResetMap{N}) where {N<:Real}
    return dot_zero(d, σ(d, rm))
end

"""
    an_element(rm::ResetMap)

Return some element of a reset map.

### Input

- `rm` -- reset map

### Output

An element in the reset map.
It relies on the `an_element` function of the wrapped set.
"""
function an_element(rm::ResetMap)
    return substitute(rm.resets, an_element(rm.X))
end

"""
    isempty(rm::ResetMap)::Bool

Return if a reset map is empty or not.

### Input

- `rm` -- reset map

### Output

`true` iff the wrapped set is empty.
"""
function isempty(rm::ResetMap)::Bool
    return isempty(rm.X)
end

"""
    constraints_list(rm::ResetMap{N}) where {N<:Real}

Return the list of constraints of a polytopic reset map.

### Input

- `rm` -- reset map of a polytope

### Output

The list of constraints of the reset map.

### Notes

We assume that the underlying set `X` is a polytope, i.e., is bounded and offers
a method `constraints_list(X)`.

### Algorithm

We fall back to `constraints_list` of a `LinearMap` of the `A`-matrix in the
affine-map view of a reset map.
Each reset dimension ``i`` is projected to zero, expressed by two constraints
for each reset dimension.
Then it remains to shift these constraints to the new value.

For instance, if the dimension ``5`` was reset to ``4``, then there will be
constraints ``x₅ ≤ 0`` and ``-x₅ ≤ 0``.
We then modify the right-hand side of these constraints to ``x₅ ≤ 4`` and
``-x₅ ≤ -4``, respectively.
"""
function constraints_list(rm::ResetMap{N}) where {N<:Real}
    # if `vector` has exactly one non-zero entry, return its index
    # otherwise return 0
    function find_unique_nonzero_entry(vector::AbstractVector{N})
        res = 0
        for (i, v) in enumerate(vector)
            if v != zero(N)
                if res != 0
                    # at least two non-zero entries
                    return 0
                else
                    # first non-zero entry so far
                    res = i
                end
            end
        end
        return res
    end

    constraints = copy(constraints_list(LinearMap(get_A(rm), rm.X)))
    for (i, c) in enumerate(constraints)
        constrained_dim = find_unique_nonzero_entry(c.a)
        if constrained_dim > 0  # constraint in only one dimension
            if !haskey(rm.resets, constrained_dim)
                continue  # not a dimension we are interested in
            end
            new_value = rm.resets[constrained_dim]
            if new_value == zero(N)
                @assert c.b == zero(N)
                continue  # a reset to 0 needs not create a new constraint
            end
            if c.a[constrained_dim] < zero(N)
                # change sign for lower bound
                new_value = -new_value
            end
            constraints[i] = HalfSpace(c.a, new_value)
        end
    end
    return constraints
end

"""
    constraints_list(rm::ResetMap{N, S}) where
        {N<:Real, S<:AbstractHyperrectangle}

Return the list of constraints of a hyperrectangular reset map.

### Input

- `rm` -- reset map of a hyperrectangular set

### Output

The list of constraints of the reset map.

### Algorithm

We iterate through all dimensions.
If there is a reset, we construct the corresponding (flat) constraints.
Otherwise, we construct the corresponding constraints of the underlying set.
"""
function constraints_list(rm::ResetMap{N, S}
                         ) where {N<:Real, S<:AbstractHyperrectangle}
    H = rm.X
    n = dim(H)
    constraints = Vector{LinearConstraint{N}}(undef, 2*n)
    j = 1
    for i in 1:n
        ei = LazySets.Approximations.UnitVector(i, n, one(N))
        if haskey(rm.resets, i)
            # reset dimension => add flat constraints
            v = rm.resets[i]
            constraints[j] = HalfSpace(ei, v)
            constraints[j+1] = HalfSpace(-ei, -v)
        else
            # non-reset dimension => use the hyperrectangle's constraints
            constraints[j] = HalfSpace(ei, high(H, i))
            constraints[j+1] = HalfSpace(-ei, -low(H, i))
        end
        j += 2
    end
    return constraints
end

ext: jl


text:
module MyPoly

export MyPoint, MyPolyVar, area

include("point.jl")
include("poly.jl")

end # module


text:
module builtin

import io
import math
import stream

def load_module(path) {
  load_module__(path)  ; External C function
}

def str(input) {
  if ~input return 'None'
  if input is Object return input.to_s()
  stringify__(input)  ; External C function
}

def concat(args) {
  if ~args {
    return 'None'
  }
  if ~(args.len) {
    return str(args)
  }
  strc = ''
  for i=0, i<args.len, i=i+1 {
    strc.extend(str(args[i]))
  }
  strc
}
self.cat = self.concat

def hash(v) {
  if v is Object {
    return v.hash()
  }
  Int(v)
}

def cmp(o1, o2) {
  if (o1 is Object) and (o2 is Object) {
    return o1.cmp(o2)
  }
  o1 - o2
}
; DO NOT CHANGE
def eq(o1, o2) {
  if o1 ^ o2 return False
  if hash(o1) != hash(o2) return False
  if (o1 is Object) and (o2 is Object) {
    return o1.eq(o2)
  }
  if (o1 is Object) or (o2 is Object) {
    return False
  }
  return Int(o1) == Int(o2)
}

def neq(o1, o2) {
  ~eq(o1, o2)
}

class Range {
  new(field start, field inc, field end) {}
  method __in__(n) {
    (n <= end) and (n >= start) and (n % inc == start % inc)
  }
  method __index__(i) {
    if (i < 0) raise Error(cat('Range index out of bounds. was ', i))
    val = start + i * inc
    if (val > end) raise Error(cat('Range index out of bounds. was ', i))
    val
  }
  method iter() {
    IndexIterator(self, 0, (end - start) / inc)
  }
  method to_s() {
    ':'.join(str(start), str(inc), str(end))
  }
  method list() {
    result = []
    for i=start, i<end, i=i+inc {
      result.append(i)
    }
    result
  }
}

def range(params) {
  start = params[0]
  if params.len == 3 {
    inc = params[1]
    end = params[2]
  } else {
    inc = 1
    end = params[1]
  }
  Range(start, inc, end)
}

class Object {
  method to_s() $module.concat(self.class.name, '@', self.$adr)
  method hash() {
    return $adr
  }
  method cmp(other) {
    $adr - other.$adr
  }
  method eq(other) $adr == other.$adr
  method neq(other) ~eq(other)
}

class Class {
  method to_s() {
    $module.concat(name, '.class')
  }
}

class Function {
  method to_s() $module.concat(self.class.name, '(',
                    self.module.name, '.',
                    name, ')')
  method call(args) {
    self.module.$lookup(name)(args)
  }
}

class Method : Function {
  method to_s()
      $module.concat(self.class.name, '(', self.method_path(), ')')
  method method_path() $module.concat(self.parent_class.module.name, '.',
                           self.parent_class.name, '.',
                           self.Function.name)
  method call(obj, args) {
    obj.$lookup(self.Function.name)(args)
  }
}

class ExternalMethod {
  method to_s()
      $module.concat(self.class.name, '(', self.method_path(), ')')
  method method_path() $module.concat(self.parent_class.module.name, '.',
                           self.parent_class.name, '.',
                           self.Function.name)
  method call(obj, args) {
    obj.$lookup(self.Function.name)(args)
  }
}

class MethodInstance {
  new(obj, meth) {
    self.obj = obj
    self.$method = meth
  }
  method call(args) {
    self.$method.call(self.obj, args)
  }
  method to_s() {
    $module.concat('MethodInstance(obj=', self.obj,
           ',$method=', self.$method, ')')
  }
}

class Module {
  method to_s()
      $module.concat(
          'Module(name=', self.name,
          ', classes=', self.classes,
          ', functions=', self.functions, ')')
}

class Tuple {
  field len
  method to_s() {
    strng = '('
    if len > 0 {
      strng.extend(str(self[0]))
    }
    for i=1, i<len, i=i+1 {
      strng.extend(',').extend(str(self[i]))
    }
    strng.extend(')')
  }
  method eq(t) {
    if t.len != len {
      return False
    }
    for i=0, i<len, i=i+1 {
      if ~$module.eq(self[i],t[i]) return False
    }
    return True
  }
}

class Array {
  field len
  method copy() {
    cpy = []
    cpy[len = 1] = None
    for i=0, i<len, i=i+1 {
      cpy[i] = self[i]
    }
    cpy
  }
  method hash() {
    hashval = 5381
    for i=0, i < len, i=i+1 {
      c = $module.hash(self[i])
      hashval = ((hashval * 33) + hashval) + c
    }
    hashval
  }
  method append(elt) {
    self[len] = elt
    return self
  }
  method extend(arr) {
    if ~(arr is Array) raise Error('Cannot extend something not an array.')
    if (0 == arr.len) return self
    self_len = len
    self[self_len+arr.len-1] = None
    for i=0, i<arr.len, i=i+1 {
      self[self_len+i] = arr[i]
    }
    return self
  }
  method elt_to_str(elt) {
    if elt is String {
      return '\''.extend(elt).extend('\'')
    }
    return str(elt)
  }
  method to_s() {
    '['.extend(','.join(map(elt_to_str))).extend(']')
  }
  method map(f) {
    arr = []
    for i=len-1, i>=0, i=i-1 {
      arr[i] = f(self[i])
    }
    arr
  }
  method each(f) {
    for i=0, i<len, i=i+1 {
      f(self[i])
    }
  }
  method flatten() {
    arr = []
    for i=0, i<len, i=i+1 {
      if self[i] is Array {
        child = self[i]
        for j=0, j<child.len, j=j+1
           arr.append(child[j])
      } else arr.append(self[i])
    }
    arr
  }
  method collect(f, s=None) {
    if len == 0 {
      if s is Function {
        return s()
      } else {
        return s
      }
    }
    i = 0
    a = None
    if s {
      if s is Function {
        a = s()
      } else {
        a = s
      }
    } else {
      a = self[0]
      i = 1
    }
    for _, i<len, i=i+1 {
      a = f(a, self[i])
    }
    return a
  }
  method equals_range(array, start, end) {
    if (start < 0) return False
    if (end > array.len) return False
    if((array.len - start) > self.len) return False
    if(array.len < end) return False

    for i=start, i<end, i=i+1 {
      if ~$module.eq(array[i], self[i]) {
        return False
      }
    }
    True
  }
  method eq(o) {
    if ~o return False
    if o.len != len return False
    equals_range(o, 0, len)
  }
  method partition(c, l, h) {
    x = self[h]
    i = l - 1
    for j=l, j<h, j=j+1 {
      if c(self[j], x) < 0 {
        i = i+1
        ; TODO: Fix external methods since they cannot be
        ; called witout self. for some reason.
        self.swap(i,j)
      }
    }
    i = i+1
    self.swap(i,h)
    i-1
  }
  method introsort(maxdepth, c, l, h) {
    if l < h {
      ; If recursed too many times, switch to insertion sort.
      if maxdepth <= 0 {
        inssort(c,l,h)
      } else { ; Otherwise quicksort.
        p = partition(c, l, h)
        introsort(maxdepth-1, c, l, p)
        introsort(maxdepth-1, c, p+1, h)
      }
    }
    self
  }
  method inssort(c, l=0, h=len) {
    i = l + 1
    while i < h {
      x = self[i]
      j = i-1
      while j >= l  {
        if c(self[j],x) <= 0 {
          break
        }
        j = j - 1
      }
      self.shift(j+1, i-(j+1), 1)
      self[j+1] = x
      i = i + 1
    }
    self
  }
  method sort() {
    self.introsort(math.log(len) * 2, cmp, 0, len)
  }
  method iter() {
    IndexIterator(self)
  }
  method stream() {
    $module.stream.Stream(self)
  }
}

def equals_range(a1, a1_start, a2, a2_start, length) {
  if (a1_start + length) > a1.len return False
  if (a2_start + length) > a2.len return False
  for i=0, i < length, i=i+1 {
    if a1[a1_start + i] != a2[a2_start+i] return False
  }
  True
}

class String {
  method append(elt) {
    self[len] = elt
    self
  }
  method extend(arr, start=None, end=None) {
    if ~(arr is String) raise Error('Cannot extend something not a String.')
    if start {
      if (start < 0) or (start > arr.len) {
        raise Error('Cannot extend with range before the start.')
      }
      if ~end {
        end = arr.len
      }
      if (end < start) {
        raise Error(concat('end must be >= start. start=', start, ' end=', end, '.'))
      }
      self.extend_range__(arr, start, end)
    } else self.extend__(arr)
    self
  }
  method find(substr, index=0) {
    self.find__(substr, index)
  }
  method find_all(substr, index=0) {
    self.find_all__(substr, index)
  }
  method __in__(substr) {
    find(substr) != None
  }
  method starts_with(array) {
    if array.len > len {
      return False
    }
    return equals_range(array, 0, array.len)
  }
  method join(array) {
    if array.len == 0 {
      return ''
    }
    a = array[0].copy()
    for i=1, i<array.len, i=i+1 {
      a.extend(self).extend(array[i])
    }
    a
  }
  method to_s() {
    self
  }
  method iter() {
    IndexIterator(self)
  }
  method substr(start, end=len) {
    return self.substr__(start, end)
  }
}

class Iterator {
  new(field has_next, field next) {}
}

class IndexIterator : Iterator {
  field indexable, start, end, i, index
  new(args) {
    if (args is Array) or (args is String) {
      indexable = args
      start = 0
      end = args.len
    } else if args is Tuple {
      (indexable, start, end) = args
    } else {
      raise Error(concat('Strange input: ', args))
    }
    i = -1
    index = start - 1
    self.Iterator.new(
        () -> index < (end - 1),
        () {
          i = i + 1
          index = index + 1
          (i, indexable[index])
        })
  }
}

class KVIterator : Iterator {
  new(field key_iter, field container) {
    self.Iterator.new(
        key_iter.has_next,
        () {
          k = key_iter.next()[1]
          return (k, container[k]) ; return required.
        })
  }
}

class MemoizedFunction : Function {
  field result, has_result
  new(field target) {
    result = None
    has_result = False
  }
  method call(args) {
    if ~has_result {
      has_result = True
      result = target(args)
    }
    return result
  }
  method to_s() {
    cat(self.class.name, '(', target.to_s(), ')')
  }
}

def memoize(fn) {
  return MemoizedFunction(fn)
}


text:
function publisher_amqp_config!(chan, exchangename)
    vent_queue = exchangename * "-vent"
    declare_exchange!(chan, exchangename, EXCHANGE_TYPE_FANOUT)
    declare_queue!(chan, vent_queue)
    queue_bind(chan, vent_queue, exchangename, "*")
end

function dlq_amqp_config!(chan, name)
    declare_exchange!(chan, DLQ_EXCHANGE, EXCHANGE_TYPE_DIRECT)
    declare_queue!(chan, dlq)
    queue_bind(chan, dlq, DLQ_EXCHANGE, dlq)
end


text:
module Output

# supports output functionality for the results of the sandpile symulations
# outputs the final grid in CSV format, as well as an image file

using CSV
using DataFrames
using Images

function TrimZeros(A)
  # given an array A trims any zero rows/columns from its borders
  # returns a 4 tuple of integers, i1, i2, j1, j2, where the trimmed array corresponds to A[i1:i2, j1:j2]
  # A can be either numeric or a boolean array

    i1, j1 = 1, 1;
    i2, j2 = size(A);

    zz = typeof(A[1,1])(0);   # comparison of a value takes into account the type as well

    # i1 is the first row which has non zero element
    for i = 1:size(A,1)
      q = false;
      for k = 1:size(A,2)
        if A[i,k] != zz
          q = true;
          #print("found non-zero at i1, k  = ", i1, " ", k , " value = ", A[i1, k], " \n");
          i1 = i;
          break
        end
      end

      if q == true
        break
      end
    end

    # i2 is the first from below row with non zero element
    for i in size(A,1):-1:1
      q = false;
      for k = 1:size(A,2)
        if A[i,k] != zz
          q = true;
          i2 = i;
          break
        end
      end

      if q == true
        break
      end
    end

    # j1 is the first column with non zero element

    for j = 1:size(A,2)
      q = false;
      for k = 1:size(A,1)
        if A[k,j] != zz
          j1 = j;
          q = true;
          break
        end
      end

      if q == true
        break
      end
    end

    # j2 is the last column with non zero element

    for j in size(A,2):-1:1
      q=false;
      for k=1:size(A,1)
        if A[k,j] != zz
          j2 = j;
          q=true;
          break
        end
      end

      if q==true
        break
      end
    end

    return i1, i2, j1, j2;

end

function addLayerofZeros(A, extraLayer)
  # adds layer of zeros from all corners to the given array A

  if extraLayer <= 0
    return A;
  end

  N, M = size(A);

  Z = zeros( typeof(A[1,1]), N + 2*extraLayer, M + 2*extraLayer );
  Z[(extraLayer+1):(N + extraLayer ), (extraLayer+1):(M + extraLayer ) ] = A;

  return Z;

end

function printIntoFile(A, extraLayer, strFileName, TrimSmallValues = false)
  # exports a 2d matrix A into a csv file
  # @extraLayer is an integers adding layer of 0-s sorrounding the output matrix

  # trimming off very small values; tiny values affect the performance of CSV export
  if TrimSmallValues == true
    A = map( x -> if ( abs( x - floor(x)  ) < 0.01   )  floor(x) else x end, A ) ;
  end

  i1, i2, j1, j2  = TrimZeros( A );
  A = A[i1:i2, j1:j2];

  A = addLayerofZeros(A, extraLayer);

  CSV.write(string(strFileName,".csv"), DataFrame(A), writeheader = false);

  return A;

end

function Array_magnifier(A, cell_mag, border_mag)
  # A is the main array; @cell_mag is the magnifying size of the cell,
  # @border_mag is the magnifying size of the border between lattice cells

  # creates a new array where each cell of the original array A appears magnified by size = cell_mag

  total_factor = cell_mag + border_mag;

  A1 = zeros( typeof(A[1,1]), total_factor*size(A,1), total_factor*size(A,2) );

  for i = 1:size(A,1)
       for j = 1:size(A,2)
            for u = ((i-1)*total_factor +1):(i*total_factor)
            for v = ((j-1)*total_factor + 1):(j*total_factor)
               if ( ( u - (i-1)*total_factor <= cell_mag  ) && ( v - (j-1)*total_factor <= cell_mag ) )
                  A1[u, v] = A[i,j] ;
               end
            end
            end
      end
  end

  return A1;

end

function saveAsGrayImage(A, fileName, cell_mag, border_mag, TrimSmallValues = false)
    # given a 2d matrix A, we save it as a gray image after magnifying by the given factors
    A1 = Array_magnifier(A, cell_mag, border_mag);
    A1 = A1/maximum(maximum(A1));

    # trimming very small values from A1 to improve performance
    if TrimSmallValues == true
        A1 = map( x -> if ( x < 0.01 ) 0.0 else round(x, digits = 2) end, A1  ) ;
    end

    save( string(fileName, ".png") , colorview(Gray, A1)) ;
end

function saveAsRGBImage(A, fileName, color_codes, cell_mag, border_mag)
  # color_codes is a dictionary, where key is a value in A and value is an RGB triplet
  # given a 2d array A, and color codes (mapping from values in A to RGB triples), save A
  # into fileName as png image after applying the magnifying factors

  #R = zeros(size(A));
  #G = zeros(size(A));
  #B = zeros(size(A));
  A1 = Array_magnifier(A, cell_mag, border_mag);
  color_mat = zeros(UInt8, (3, size(A1,1), size(A1,2)) );

  for i = 1:size(A1,1)
    for j = 1:size(A1,2)
        color_mat[:, i,j]  = get(color_codes, A1[i,j] , [0,0,0] )
    end
  end

  #A1 = Array_magnifier(A, cell_mag, border_mag)
  save( string(fileName, ".png") , colorview(RGB, color_mat/255)) ;

end

end # end of the module


text:
#=
VecSE2: a 2d euclidean vector with an orientation
=#

struct VecSE2{R<:Real} <: VecSE{3,R}
    x::R
    y::R
    θ::R
end

VecSE2() = VecSE2(0.0,0.0,0.0)
VecSE2(x::Real, y::Real) = VecSE2(x,y,0.0)
VecSE2(a::VecE2, θ::Real=0.0) = VecSE2(a.x, a.y, θ)
VecSE2(t::Tuple) = VecSE2(promote(t...)...)

Base.convert(::Type{VecE2}, a::VecSE2) = VecE2(a.x, a.y)
VecE2(a::VecSE2) = VecE2(a.x, a.y)

polar(r::Real, ϕ::Real, θ::Real) = VecSE2(r*cos(ϕ), r*sin(ϕ), θ)

Base.show(io::IO, a::VecSE2) = @printf(io, "VecSE2({%.3f, %.3f}, %.3f)", a.x, a.y, a.θ)

Base.:+(a::VecSE2, b::VecE2) = VecSE2(a.x+b.x, a.y+b.y, a.θ)
Base.:+(a::VecE2, b::VecSE2) = VecSE2(a.x+b.x, a.y+b.y, b.θ)

Base.:-(a::VecSE2, b::VecE2) = VecSE2(a.x-b.x, a.y-b.y, a.θ)
Base.:-(a::VecE2,  b::VecSE2) = VecSE2(a.x-b.x, a.y-b.y, -b.θ)

op_overload_error = """
    Operator overloading for Real and VecSE2 has been removed.

    To add or subtract, use VecSE2(x,y,θ) + b*VecE2(1.0, 1.0).
    To multiply or divide, use scale_euclidean(VecSE2(x,y,θ), b)
"""
Base.:-(b::Real, a::VecSE2) = error(op_overload_error)
Base.:-(a::VecSE2, b::Real) = error(op_overload_error)
Base.:+(b::Real, a::VecSE2) = error(op_overload_error)
Base.:+(a::VecSE2, b::Real) = error(op_overload_error)
Base.:*(b::Real, a::VecSE2) = error(op_overload_error)
Base.:*(a::VecSE2, b::Real) = error(op_overload_error)
Base.:/(a::VecSE2, b::Real) = error(op_overload_error)
Base.:^(a::VecSE2, b::Integer) = error(op_overload_error)
Base.:^(a::VecSE2, b::AbstractFloat) = error(op_overload_error)

Base.:%(a::VecSE2, b::Real) = error(op_overload_error)

scale_euclidean(a::VecSE2, b::Real) = VecSE2(b*a.x, b*a.y, a.θ)
clamp_euclidean(a::VecSE2, lo::Real, hi::Real) = VecSE2(clamp(a.x, lo, hi), clamp(a.y, lo, hi), a.θ)

Base.norm(a::VecSE2, p::Real=2) = error("norm is not defined for VecSE2 - use norm(VecE2(v)) to get the norm of the Euclidean part")
normsquared(a::VecSE2) = norm(a)^2 # this will correctly throw an error
Base.normalize(a::VecSE2, p::Real=2) = error("normalize is not defined for VecSE2. Use normalize_euclidean(v) to normalize the euclidean part of the vector")

function normalize_euclidian(a::VecSE2, p::Real=2)
    n = norm(VecE2(a))
    return VecSE2(a.x/n, a.y/n, a.θ)
end

Base.atan2(a::VecSE2) = atan2(a.y, a.x)

function lerp(a::VecSE2, b::VecSE2, t::Real)
    x = a.x + (b.x-a.x)*t
    y = a.y + (b.y-a.y)*t
    θ = lerp_angle(a.θ, b.θ, t)
    VecSE2(x, y, θ)
end

Base.rot180(a::VecSE2) = VecSE2(a.x, a.y, a.θ+π)
Base.rotl90(a::VecSE2) = VecSE2(a.x, a.y, a.θ+0.5π)
Base.rotr90(a::VecSE2) = VecSE2(a.x, a.y, a.θ-0.5π)
rot(a::VecSE2, Δθ::Float64) = VecSE2(a.x, a.y, a.θ+Δθ)

Base.mod2pi(a::VecSE2) = VecSE2(a.x, a.y, mod2pi(a.θ))


text:
@add_problem constraints_benchmark function LT_constraints(handle_problem!, args...)
    p = satisfy()
    x = [Variable() for _ = 1:1000]
    for (i, xi) in enumerate(x)
        push!(p.constraints, xi <= 1.0 * i)
    end
    handle_problem!(p)
    nothing
end

@add_problem constraints_benchmark function LT_constraint(handle_problem!, args...)
    p = satisfy()
    x = Variable(1000)
    push!(p.constraints, x <= collect(1.0:1000.0))
    handle_problem!(p)
    nothing
end

@add_problem constraints_benchmark function GT_constraints(handle_problem!, args...)
    p = satisfy()
    x = [Variable() for _ = 1:1000]
    for (i, xi) in enumerate(x)
        push!(p.constraints, xi >= 1.0 * i)
    end
    handle_problem!(p)
    nothing
end

@add_problem constraints_benchmark function GT_constraint(handle_problem!, args...)
    p = satisfy()
    x = Variable(1000)
    push!(p.constraints, x >= collect(1.0:1000.0))
    handle_problem!(p)
    nothing
end

@add_problem constraints_benchmark function equality_constraints(handle_problem!, args...)
    p = satisfy()
    x = [Variable() for _ = 1:1000]
    for (i, xi) in enumerate(x)
        push!(p.constraints, xi == 1.0 * i)
    end
    handle_problem!(p)
    nothing
end

@add_problem constraints_benchmark function equality_constraint(handle_problem!, args...)
    p = satisfy()
    x = Variable(1000)
    push!(p.constraints, x == collect(1.0:1000.0))
    handle_problem!(p)
    nothing
end

@add_problem constraints_benchmark function sdp_constraint(handle_problem!, args...)
    p = satisfy()
    x = Variable(44, 44) # 990 vectorized entries
    push!(p.constraints, x ⪰ 0)
    handle_problem!(p)
    nothing
end

@add_problem constraints_benchmark function sdp_constraints(handle_problem!, args...)
    p = satisfy()
    x = [Variable(4, 4) for _ = 1:100] # 1000 total vectorized entries
    for v in x
        push!(p.constraints, v ⪰ 0)
    end
    handle_problem!(p)
    nothing
end


text:
using CSV
using DataFrames

main_fn = ARGS[1]
with_fn = ARGS[2]
ret_fn = ARGS[3]
main_csv = CSV.read(main_fn)
with_csv = CSV.read(with_fn)
ret_csv = DataFrames.join(main_csv, with_csv, on="震度観測点名称", kind=:left)
CSV.write(ret_fn, ret_csv)


text:
# License for this file: MIT (expat)
# Copyright 2021, DLR Institute of System Dynamics and Control (DLR-SR)
# Developer: Martin Otter, DLR-SR
#
# This file is part of module ModiaResult

"""
    @enum ModiaResult.SignalType

Defines the type of the signal. Supported values:

- `ModiaResult.Independent`: Independent variable (usually the time signal).

- `ModiaResult.Continuous`: Piece-wise continuous signal (typically linearly interpolated).

- `ModiaResult.Clocked`: Clocked signal
  (values are only defined at the corresponding `Time` signal time instants and have
   no value in between; the latter might be signaled by piece-wise constant
   dotted lines).
"""
@enum SignalType Independent=1 Continuous=2 Clocked=3

"""
    (timeSignal, signal, signalType) = ModiaResult.rawSignal(result, name)

Given the result data structure `result` and a variable `name::AbstractString`,
return the result values of the independent variable (= `timeSignal`), the
corresponding result values of the variable (= `signal`) and the type
of the signal `signalType::`[`SignalType`](@ref)). Note, an error shall be raised,
if `name` is not known.

The following figure sketches the returned `timeSignal` and `signal` data structures:

![SignalDefinition](https://modiasim.github.io/ModiaResult.jl/resources/images/signal-definition.png)

Other signal types might be mapped to this basic signal type by introducing views.

# Details of the return arguments

`timeSignal::Vector{Vector{T1}}`:
A result consists of one or more **segments**.
`timeSignal[i][j]` is the value of time instant `j` in segment `i`.
`timeSignal[i][:]` must have monotonically increasing values and
type `T1<:Real` must be a subtype of `Real` for which a conversion to `AbstractFloat`
is defined. For example, `T1::Rational` is fine, but `T1::Complex` is not allowed.

`signal::Vector{Vector{T2}}` or `signal::Vector{Vector{Array{T2,N}}}`:
`signal[i][j]` is the value of the variable at time instant `timeSignal[i][j]`.
This value can be a scalar or an array. Type `T2` can have one of the following values:

- `T2 <: Real`, must be a subtype of `Real` for which a conversion to `AbstractFloat`
   is defined, or
- `T2 <: Measurements.Measurement{T1}`, or
- `T2 <: MonteCarloMeasurements.StaticParticles{T1,N}`, or
- `T2 <: MonteCarloMeasurements.Particles{T1,N}`.

If the signal is a constant with value `value`, return
`([[t_min, t_max]], [[value, value]], ModiaResult.Continuous)`.

If the signal is the time signal, return
`(timeSignal, timeSignal, ModiaResult.independent)`.
The `timeSignal` might be a dummy vector consisting of the first and last time point
in the result (if different timeSignals are present for different signals or
if the signal is constant).

`signal` and `timeSignal` may have units from package `Unitful`.

The information `signalType::SignalType` defines how the signal can be interpolated
and/or plotted.
"""
function rawSignal end

"""
    ModiaResult.signalNames(result)

Return a string vector of the signal names that are present in result.
"""
function signalNames end

"""
    ModiaResult.timeSignalName(result)

Return the name of the independent variable (typically: "time").
"""
function timeSignalName end

"""
    ModiaResult.hasOneTimeSignal(result)

Return true if `result` has one time signal.
Return false, if `result` has two or more time signals.
"""
function hasOneTimeSignal end

"""
    (signal, timeSignal, timeSignalName, signalType, arrayName,
     arrayIndices, nScalarSignals) = getSignalDetails(result, name)

Return the signal defined by `name::AbstractString` as
`signal::Vector{Matrix{<:Real}}`.
`name` may include an array range, such as "a.b.c[2:3,5]".
In this case `arrayName` is the name without the array indices,
such as `"a.b.c"`, `arrayIndices` is a tuple with the array indices,
such as `(2:3, 5)` and `nScalarSignals` is the number of scalar
signals, such as `3` if arrayIndices = `(2:3, 5)`.
Otherwise `arrayName = name, arrayIndices=(), nScalarSignals=1`.

In case the signal is not known or `name` cannot be interpreted,
`(nothing, nothing, nothing, nothing, name, (), 0)` is returned.

It is required that the value of the signal at a time instant
has either `typeof(value) <: Real` or
`typeof(value) = AbstractArray{Real, N}`.
The following `Real` types are currently supported:

1. `convert(Float64, eltype(value)` is supported
   (for example Float32, Float64, DoubleFloat, Rational, Int32, Int64, Bool).

2. Measurements.Measurement{<Type of (1)>}.

3. MonteCarloMeasurements.StaticParticles{<Type of (1)>}.

4. MonteCarloMeasurements.Particles{<Type of (1)>}.
"""
function getSignalDetails end

"""
    ModiaResult.hasSignal(result, name)

Returns `true` if signal `name::AbstractString` is available in `result`.
"""
function hasSignal(result, name::AbstractString)::Bool
    hasName = true
    try
        sigInfo = rawSignal(result,name)
    catch
        hasName = false
    end
    return hasName
end

"""
    ModiaResult.defaultHeading(result)

Return default heading of result as a string
(can be used as default heading for a plot).
"""
defaultHeading(result) = ""

max_stars_repo_name: ModiaSim/ModiaResult

max_issues_repo_name: ModiaSim/ModiaResult

max_forks_repo_name: ModiaSim/ModiaResult


text:
import DrWatson: quickactivate
quickactivate(@__DIR__, "Chemostat_Heerden2013")

@time begin
    import CSV
    import MAT
    using DataFrames
    import SparseArrays
    using Serialization
    using ProgressMeter
    using UtilsJL
    const UJL = UtilsJL
    using Plots
    using Base.Threads

    # -------------------------------------------------------------------
    # run add "https://github.com/josePereiro/Chemostat" in the
    # julia Pkg REPL for installing the package
    import Chemostat
    const ChU = Chemostat.Utils
    const ChSS = Chemostat.SteadyState
    const ChLP = Chemostat.LP

    # -------------------------------------------------------------------
    # Run add https://github.com/josePereiro/Chemostat_Heerden2013.jl in the Julia Pkg REPL to install the
    # package, then you must activate the package enviroment (see README)
    import Chemostat_Heerden2013
    const ChH = Chemostat_Heerden2013
    const Hd  = ChH.HeerdenData;
    const Bd  = ChH.BegData
    const iJR = ChH.iJR904
end

## -------------------------------------------------------------------
# BASE MODEL
# Load Mat file
function load_src_model()
    src_file = iJR.rawdir("iJR904.mat")
    mat_model = MAT.matread(src_file)["model"]
   return ChU.MetNet(mat_model; reshape = true);
end

get_comb(n::Int) = (digits(i; base = 2, pad = n) for i in ((2^n) - 1):-1:0)

## -------------------------------------------------------------------
# globals
inac_dat_cid = ("INACT_DAT")
inac_map = iJR.load_Hd_to_inactivate_map()
inac_rxns = vcat(values(inac_map)...)

## -------------------------------------------------------------------
# Collect data
let
    # UJL.delete_cache(inac_dat_cid) # Uncomment to recompute
    UJL.exist_cache(inac_dat_cid) && return

    inac_comb_pool = get_comb(length(inac_rxns))

    # init val
    # model0 = iJR.load_model("base_model")
    model0 = load_src_model()
    biom_ider = iJR.BIOMASS_IDER
    fbaout = ChLP.fba(model0, biom_ider)
    biom_val0 = ChU.av(model0, fbaout, biom_ider)
    inac_idxs = map(inac_rxns) do rxn
        ChU.rxnindex(model0, rxn)
    end

    model_pools = deepcopy(model0)
    prog = ProgressUnknown(;dt = 0.5)
    biom_vals = []
    for (ci, inac_comb) in enumerate(inac_comb_pool)

        to_inac = inac_rxns[isone.(inac_comb)]

        # inactivate
        for rxn in to_inac
            ChU.bounds!(model, rxn, 0.0, 0.0)
        end

        # Test FBA
        try
            fbaout = ChLP.fba(model, biom_ider)
            biom_val = ChU.av(model, fbaout, biom_ider)
            next!(prog; showvalues = [ ("biom_val", biom_val)])
            push!(biom_vals, biom_val)
        catch err;
            next!(prog; showvalues = [("biom_val", "Fails")])
            push!(biom_vals, 0.0)
        end

        # restart model
        model.lb[inac_idxs] .= model0.lb[inac_idxs]
        model.ub[inac_idxs] .= model0.ub[inac_idxs]

    end

    UJL.save_cache(inac_dat_cid, biom_vals)
end

## -------------------------------------------------------------------
# Plots
# growth vs n_inac
let
    biom_vals = UJL.load_cache(inac_dat_cid; verbose = false);
    inac_comb_pool = get_comb(length(inac_rxns))

    growth_hist = Dict()
    for (comb, biom_val) in zip(inac_comb_pool, biom_vals)
        ninac = sum(comb)
        get!(growth_hist, ninac, 0.0)
        growth_hist[ninac] = max(growth_hist[ninac], biom_val)
    end

    # plot(sum.(inac_comb_pool), biom_vals; label = "")
    bar(growth_hist; xlabel = "ninac", ylabel = "max growth", label = "")

end

## -------------------------------------------------------------------
# Plots
# growth vs inac rxn
let
    biom_vals = UJL.load_cache(inac_dat_cid; verbose = false);
    inac_comb_pool = get_comb(length(inac_rxns))

    growth_hist = Dict()
    for (comb, biom_val) in zip(inac_comb_pool, biom_vals)
        comb_inac_rxns = inac_rxns[isone.(comb)]
        for rxn in comb_inac_rxns
            get!(growth_hist, rxn, 0.0)
            growth_hist[rxn] += biom_val
        end
    end

    # plot(sum.(inac_comb_pool), biom_vals; label = "")
    bar(growth_hist;
        xlabel = "inac rxn", ylabel = "accum. growth",
        label = "",
        xrotation = 35
    )

end


text:
module SymmetricBitArrays

import Base: linearindexing, size, getindex, setindex!
export linearindexind, size, getindex, setindex!
export SymBitArray

type SymBitArray <: AbstractArray{Bool, 2}
    data::BitArray{1}
    SymBitArray(n::Int) = new(BitArray{1}(Int(n*(n+1)/2)))
end

linearindexing(a::SymBitArray) = Base.LinearSlow()

function size(a::SymBitArray)
    l = length(a.data)
    n = Int(sqrt(2l+1//4)-1//2)
    return (n, n)
end

#getindex(a::SymBitArray, i::Int) = getindex(a.data, i)

function getindex(a::SymBitArray, i::Int, j::Int)
    ncol = size(a)[1]
    row = max(i, j)
    col = min(i, j)
    c = ncol-col+1
    ind = Int((ncol*(ncol+1)/2) - (c*(c+1)/2)) + row - col + 1

    return a.data[ind]
end

#setindex!(a::SymBitArray, v::Bool, i::Int) = setindex!(a.data, v, i)

function setindex!(a::SymBitArray, v::Bool, i::Int, j::Int)
    ncol = size(a)[1]
    row = max(i, j)
    col = min(i, j)
    c = ncol-col+1
    ind = Int((ncol*(ncol+1)/2) - (c*(c+1)/2)) + row - col + 1

    a.data[ind] = v
end

end # module


text:
#   Problem :
#   *********
#
#   Source: Implementation found here
#   https://www.sfu.ca/~ssurjano/powell.html
#
#   N is the number of free variables, and should be a multiple of 4
#
# Daniel Henderson, 08/2021

f = (x) -> begin
    return error("LANGER.jl not implemented")
end

g! = (g, x) -> begin
    return error("LANGER.jl not implemented")
end

fg! = (g, x) -> begin
    return error("LANGER.jl not implemented")
end

@warn "TODO: Figure out why LANGER doesn't have an SIF but in Princeton and Georgian repo"

init = (n::Int=3000) -> begin
    x0 = zeros(n)
    return n, x0
end

# TestSet["LANGER"] = UncProgram("LANGER", f, g!, fg!, 25, zeros(25))


text:
using Pkg

pkgs = [
    "BenchmarkTools",
    "CSV",
    "DataFrames",
    "DistributedArrays",
    "Distributions",
    "Documenter",
    "FFTW",
    "Flux",
    "HDF5",
    "IJulia",
    "ImageIO",
    "ImageMagick",
    "Images",
    "JLD",
    "JuliaFormatter",
    "LaTeXStrings",
    "LsqFit",
    "MKL",
    "NPZ",
    "OhMyREPL",
    "PackageCompiler",
    "Plots",
    "ProfileView",
    "PyCall",
    "PyPlot",
    "Revise",
    "Zygote",
]

Pkg.add(pkgs)

ENV["JUPYTER"] = "/opt/miniconda3/envs/jupyter/bin/jupyter"
ENV["PYTHON"]  = "/opt/miniconda3/envs/jupyter/bin/python"
Pkg.build("IJulia")
Pkg.build("PyCall")


text:
"""
JuMPModels.jl gère les modèles JuMP et leur résolution
"""

function MCP_model_Lucas(stations::Array{Station,1},townsites::Array,p::Int, verbose::Bool = true)
    m = Model(GLPK.Optimizer)
    nbStations = length(stations)
    nbTownsites = length(townsites)
    h::Vector{Float64} = getDensity.(townsites)

    @variable(m, x[stations], Bin) # xi = 1 si la station i est construite, 0 sinon
    @variable(m, s[townsites], Bin) # si = 1 si la demande du lotissement i est satisfaite, 0 sinon

    @constraint(m, activation[townsite in townsites], sum(x[station] for station in townsite.nearStations) - s[townsite] >= 0)
    @constraint(m, limit, sum(x[station] for station in stations) == p)

    @objective(m, Max, dot(h,s))

    optimize!(m)

    activateStation = Vector{Station}(undef, nbStations)
    indFinActivateStation = 0

    verbose && println("Nombre d'habitant déservi par cette solution : $(JuMP.objective_value(m))")
    verbose && print("Les stations suivantes sont activées : ")
    for iter in stations
        val = JuMP.value(x[iter])
        if val != 0
            indFinActivateStation += 1
            activateStation[indFinActivateStation] = iter
            verbose && print(iter, " ")
        end
    end
    verbose && println()

    return activateStation[1:indFinActivateStation], JuMP.objective_value(m)
end

# PESP JuMP Model
function PESP_model(T::Int, E::Array{Node,1}, A_run::Array{Tuple{Int,Int},1}, A_dwell::Array{Tuple{Int,Int},1}, A_thr::Array{Tuple{Int,Int},1}, A_head::Array{Tuple{Int,Int},1}, A_reg::Array{Tuple{Int,Int},1}, L::Array{Int,2}, U::Array{Int,2})
    A = merge([A_run,A_dwell,A_head,A_thr,A_reg]) # regroupe toutes les arrêtes dans un seul vecteur de tuples

    nbNodes = length(E) # nombre de sommets
    nbArcs = length(A) # nombre d'arcs

    # déclaration du modèle
    m = Model(GLPK.Optimizer)

    # déclaration des variables
    @variable(m, 0 <= x[1:nbNodes] <= T) # temps d'arrivée à chaque sommet du graph
    @variable(m, z[1:nbNodes,1:nbNodes], Bin) # modulo

    # On fixe la premiere Navette à 0
    @constraint(m, init, x[1] == 0)
    # déclaration des contraintes
    @constraint(m, valid[arc in A], L[arc[1],arc[2]] <= x[arc[2]] - x[arc[1]] + z[arc[1],arc[2]]*T <= U[arc[1],arc[2]])

    # déclaration de l'objectif
    @objective(m, Min, sum(x[arc[2]] - x[arc[1]] +z[arc[1], arc[2]] * T for arc in A_dwell) + sum(x[arc[2]] - x[arc[1]] +z[arc[1], arc[2]] * T for arc in A_run))

    optimize!(m)

    # Ce qu'il faut renvoyer : pour chaque Navette un Tableau pour chaque station (horaireDépart, horaireArrivé)
    return m
end

# retourne le tableau de fusion résultante entre les cinq tableaux donnés de Tuple{Int,Int}
function merge(T::Array{Array{Tuple{Int,Int},1}})
    # calcul du nombre total de tuples
    nbTuples = 0
    for array in T
        nbTuples += length(array)
    end
    # allocation du vecteur à retourner
    result = Vector{Tuple{Int,Int}}(undef,nbTuples)
    indexResult = 1 # tête d'écriture
    # remplissage du vecteur résultat
    for array in T
        for tuple in array
            result[indexResult] = tuple
            indexResult += 1
        end
    end
    return result
end

# retourne un objet à passer au modèle JuMP PESP, avec nbStations > 0
function parserPESP(T::Int,nbStations::Int,nbShuttles::Int,stations::Array{Station,1}, distStations::Array{Float64, 2})

    dwellMax = 60 # en seconde
    dwellMin = 30 # en seconde
    vehicle_speed = 20 / 3.6 # en metre/seconde

    nbNodes = 4*(nbStations-1)*nbShuttles # nombre de sommets du graph
    nbNodesPerShuttle = 4*(nbStations-1)*nbShuttles # nombre de sommets du graph
    nbArcsRun = 2*nbShuttles*(nbStations-1) # nombre d'arcs run
    nbArcsDwell = 2*nbShuttles*(nbStations-1) # nombre d'arcs Dwell
    nbArcsReg = 2*nbShuttles*(nbStations-1)*(nbShuttles-1) # nombre d'arcs Reg
    # sommets du graph
    E = Array{Node,1}(undef,nbNodes)
    # arrêtes du graph
    A_run = Array{Tuple{Int,Int},1}(undef,nbArcsRun)
    A_dwell = Array{Tuple{Int,Int},1}(undef,nbArcsDwell)
    A_reg = Array{Tuple{Int,Int},1}(undef,nbArcsReg)
    # borne inférieure pour chaque couple de sommets du graph
    L = Array{Int,2}(undef,nbNodes,nbNodes)
    # borne supérieure pour chaque couple de sommets du graph
    U = Array{Int,2}(undef,nbNodes,nbNodes)

    iterEps = 0
    iterARun = 0
    iterADwell = 0
    iterAReg = 0
    for shuttle = 1:nbShuttles
        # L'aller de la navette
        iterEps += 1
        E[iterEps] = Node(getNameNode(iterEps), shuttle, 1, departure, forth)
        for iterStation = 2:nbStations-1
            iterEps += 1
            E[iterEps] = Node(getNameNode(iterEps), shuttle, iterStation, arrival, forth)

            # Trajet entre une station et une autre
            iterARun += 1
            A_run[iterARun] = (iterEps-1, iterEps)
            L[iterEps-1, iterEps] = Int(floor(0.95*distStations[ E[iterEps-1].indStation, E[iterEps].indStation] / vehicle_speed))
            U[iterEps-1, iterEps] = Int(ceil(1.05*distStations[ E[iterEps-1].indStation, E[iterEps].indStation] / vehicle_speed))

            iterEps += 1
            E[iterEps] = Node(getNameNode(iterEps), shuttle, iterStation, departure, forth)

            # Attente sur une station du trajet
            iterADwell += 1
            A_dwell[iterADwell] = (iterEps -1, iterEps)
            L[iterEps-1, iterEps] = dwellMin
            U[iterEps-1, iterEps] = dwellMax
        end
        iterEps += 1
        E[iterEps] = Node(getNameNode(iterEps), shuttle, nbStations, arrival, forth)

        iterARun += 1
        A_run[iterARun] = (iterEps - 1, iterEps)
        L[iterEps-1, iterEps] = Int(floor(0.95*distStations[ E[iterEps-1].indStation, E[iterEps].indStation] / vehicle_speed))
        U[iterEps-1, iterEps] = Int(ceil(1.05*distStations[ E[iterEps-1].indStation, E[iterEps].indStation] / vehicle_speed))

        # Le retour de la navette
        iterEps += 1
        E[iterEps] = Node(getNameNode(iterEps), shuttle, nbStations, departure, back)

        iterADwell += 1
        A_dwell[iterADwell] = (iterEps - 1, iterEps)
        L[iterEps-1, iterEps] = dwellMin + 10
        U[iterEps-1, iterEps] = dwellMax + 10

        for iterStation = 1:nbStations-2
            iterEps += 1
            E[iterEps] = Node(getNameNode(iterEps), shuttle, nbStations - iterStation, arrival, back)

            iterARun += 1
            A_run[iterARun] = (iterEps - 1, iterEps)
            L[iterEps-1, iterEps] = Int(floor(0.95*distStations[ E[iterEps-1].indStation, E[iterEps].indStation] / vehicle_speed))
            U[iterEps-1, iterEps] = Int(ceil(1.05*distStations[ E[iterEps-1].indStation, E[iterEps].indStation] / vehicle_speed))

            iterEps += 1
            E[iterEps] = Node(getNameNode(iterEps), shuttle, nbStations - iterStation, departure, back)

            iterADwell += 1
            A_dwell[iterADwell] = (iterEps - 1, iterEps)
            L[iterEps-1, iterEps] = dwellMin
            U[iterEps-1, iterEps] = dwellMax
        end
        iterEps += 1
        E[iterEps] = Node(getNameNode(iterEps), shuttle, 1, arrival, back)

        iterARun += 1
        A_run[iterARun] = (iterEps -1, iterEps)
        L[iterEps-1, iterEps] = Int(floor(0.95*distStations[ E[iterEps-1].indStation, E[iterEps].indStation] / vehicle_speed))
        U[iterEps-1, iterEps] = Int(ceil(1.05*distStations[ E[iterEps-1].indStation, E[iterEps].indStation] / vehicle_speed))

        iterADwell += 1
        A_dwell[iterADwell] = (iterEps, iterEps - 4*(nbStations - 1) +1)
        L[iterEps , (iterEps - 4*(nbStations - 1) +1)] = dwellMin + 10
        U[iterEps , (iterEps - 4*(nbStations - 1) +1)] = T - (dwellMax + 10)
    end

    for node = 1:(2*(nbStations-1))
        for shuttle = 1:nbShuttles
            for nextShuttle = (shuttle+1):nbShuttles
                iterAReg += 1
                A_reg[iterAReg] = (node + (shuttle - 1)*(4*(nbStations - 1)) , node + (nextShuttle - 1)*(4*(nbStations - 1)) )
                L[node + (shuttle - 1)*(4*(nbStations - 1)), (node + (nextShuttle - 1)*(4*(nbStations - 1)))] = Int(ceil(T/nbStations))
                U[node + (shuttle - 1)*(4*(nbStations - 1)), (node + (nextShuttle - 1)*(4*(nbStations - 1)))] = T - Int(ceil(T/nbStations))

                iterAReg += 1
                A_reg[iterAReg] = (node + (2*(nbStations-1)) + (shuttle - 1)*(4*(nbStations - 1)) , node + 2*(nbStations-1) + (nextShuttle - 1)*(4*(nbStations - 1)) )
                L[node + (2*(nbStations-1)) + (shuttle - 1)*(4*(nbStations - 1)), (node + 2*(nbStations-1) + (nextShuttle - 1)*(4*(nbStations - 1)))] = Int(ceil(T/nbShuttles))
                U[node + (2*(nbStations-1)) + (shuttle - 1)*(4*(nbStations - 1)), (node + 2*(nbStations-1) + (nextShuttle - 1)*(4*(nbStations - 1)))] = T - Int(ceil(T/nbShuttles))
            end
        end
    end

    return T, E, A_run, A_dwell, Vector{Tuple{Int, Int}}(), Vector{Tuple{Int, Int}}(), A_reg, L, U
end

function stationActivateGraph(listStations::Vector{Station}, listTownsites::Vector{Townsite})
    listP = collect(1:length(listStations))
    valuesP = Vector{Float64}(undef, length(listStations))

    for p = 1:length(listStations)
        etc, z = MCP_model_Lucas(listStations, listTownsites, p, false)
        valuesP[p] = z
    end

    plot(listP, valuesP, "b", marker = "o")
    grid()
    xlabel("Nombre de stations ouverte")
    ylabel("Densité maximale couvrable")
    title("Nombre d'habitants touchés par nombre de stations ouverte sur la ligne")
end

# # retourne un objet à passer au modèle JuMP PESP
# function parserPESP(T::Int,nbStations::Int,nbShuttles::Int,stations::Array{Station,1})
#     nbNodes = 4*(nbStations-1)*nbShuttles # nombre de sommets du graph
#     nbNodesPerShuttle = 4*(nbStations-1)*nbShuttles # nombre de sommets du graph
#     nbArcs = 2*nbShuttles*(nbStation-1)*(nbShuttles+1) # nombre d'arcs
#     # sommets du graph
#     E = Array{Node,1}(undef,nbNodes)
#     # arrêtes du graph
#     A = Array{Tuple{Int,Int},1}(undef,nbArcs)
#     # borne inférieure pour chaque couple de sommets du graph
#     L = Array{Int,2}(undef,nbNodes,nbNodes)
#     # borne supérieure pour chaque couple de sommets du graph
#     U = Array{Int,2}(undef,nbNodes,nbNodes)
#
#     # Calcul des sommets du graph
#     for indexNode in 1:nbNodes/nbShuttles
#         E[indexNode] = Node(getNameNode(indexNode),)
#     end
# end

max_stars_repo_name: LucasBaussay/ProjetODC

max_issues_repo_name: LucasBaussay/ProjetODC

max_forks_repo_name: LucasBaussay/ProjetODC


text:
#############################################################################
# ECOS.jl
# Wrapper around the ECOS solver https://github.com/ifa-ethz/ecos
# See http://github.com/JuliaOpt/ECOS.jl
#############################################################################
# types.jl
# Julia implementations of the types defined in ecos.h
# Types are mapped as follows
# pfloat -> Cdouble
# idxint -> SuiteSparse_long -> Clong
#############################################################################

# Solve status flags
# From: https://github.com/ifa-ethz/ecos/blob/master/include/ecos.h
const ECOS_OPTIMAL      =  0  # Problem solved to optimality
const ECOS_PINF         =  1  # Found certificate of primal infeasibility
const ECOS_DINF         =  2  # Found certificate of dual infeasibility
const ECOS_INACC_OFFSET = 10  # Offset exitflag at inaccurate results
const ECOS_MAXIT        = -1  # Maximum number of iterations reached
const ECOS_NUMERICS     = -2  # Search direction unreliable
const ECOS_OUTCONE      = -3  # s or z got outside the cone, numerics?
const ECOS_FATAL        = -7  # Unknown problem in solver

immutable Clpcone
    p::Clong
    w::Ptr{Cdouble}
    v::Ptr{Cdouble}
    kkt_idx::Ptr{Clong}
end

immutable Csocone
    p::Clong
    skbar::Ptr{Cdouble}
    zkbar::Ptr{Cdouble}
    a::Clong
    d1::Clong
    w::Cdouble
    eta::Cdouble
    eta_square::Cdouble
    q::Ptr{Cdouble}
    Didx::Ptr{Clong}
    u0::Cdouble
    u1::Cdouble
    v1::Cdouble
end

immutable Ccone
    lpcone::Ptr{Clpcone}
    socone::Ptr{Csocone}
    nsoc::Clong
end

immutable Cspmat
    jc::Ptr{Clong}
    ir::Ptr{Clong}
    pr::Ptr{Cdouble}
    n::Clong
    m::Clong
    nnz::Clong
end

immutable Ckkt
    PKPt::Ptr{Cspmat}
    L::Ptr{Cspmat}
    D::Ptr{Cdouble}
    work1::Ptr{Cdouble}
    work2::Ptr{Cdouble}
    work3::Ptr{Cdouble}
    work4::Ptr{Cdouble}
    work5::Ptr{Cdouble}
    work6::Ptr{Cdouble}
    RHS1::Ptr{Cdouble}
    RHS2::Ptr{Cdouble}
    dx1::Ptr{Cdouble}
    dx2::Ptr{Cdouble}
    dy1::Ptr{Cdouble}
    dy2::Ptr{Cdouble}
    dz1::Ptr{Cdouble}
    dz2::Ptr{Cdouble}
    P::Ptr{Clong}
    Pinv::Ptr{Clong}
    PK::Ptr{Clong}
    Parent::Ptr{Clong}
    Sign::Ptr{Clong}
    Pattern::Ptr{Clong}
    Flag::Ptr{Clong}
    Lnz::Ptr{Clong}
    delta::Cdouble
end

immutable Cstats
    pcost::Cdouble
    dcost::Cdouble
    pres::Cdouble
    dres::Cdouble
    pinf::Cdouble
    dinf::Cdouble
    pinfres::Cdouble
    dinfres::Cdouble
    gap::Cdouble
    relgap::Cdouble
    sigma::Cdouble
    mu::Cdouble
    step::Cdouble
    step_aff::Cdouble
    kapovert::Cdouble
    iter::Clong
    nitref1::Clong
    nitref2::Clong
    nitref3::Clong
    tsetup::Cdouble
    tsolve::Cdouble
    tfactor::Cdouble
    tkktsolve::Cdouble
    torder::Cdouble
    tkktcreate::Cdouble
    ttranspose::Cdouble
    tperm::Cdouble
    tfactor_t1::Cdouble
    tfactor_t2::Cdouble
end

immutable Csettings
    gamma::Cdouble
    delta::Cdouble
    eps::Cdouble
    feastol::Cdouble
    abstol::Cdouble
    reltol::Cdouble
    feastol_inacc::Cdouble
    abstol_inacc::Cdouble
    reltol_inacc::Cdouble
    nitref::Clong
    maxit::Clong
    verbose::Clong
end

immutable Cpwork
    # Dimensions
    n::Clong
    m::Clong
    p::Clong
    D::Clong

    # Variables
    x::Ptr{Cdouble}
    y::Ptr{Cdouble}
    z::Ptr{Cdouble}
    s::Ptr{Cdouble}
    lambda::Ptr{Cdouble}
    kap::Cdouble
    tau::Cdouble

    # Best iterates seen so far
    best_x::Ptr{Cdouble}
    best_y::Ptr{Cdouble}
    best_z::Ptr{Cdouble}
    best_s::Ptr{Cdouble}
    best_kap::Cdouble
    best_tau::Cdouble
    best_cx::Cdouble
    best_by::Cdouble
    best_hz::Cdouble
    best_info::Ptr{Cstats}

    # Temporary variables
    dsaff::Ptr{Cdouble}
    dzaff::Ptr{Cdouble}
    W_times_dzaff::Ptr{Cdouble}
    dsaff_by_W::Ptr{Cdouble}
    saff::Ptr{Cdouble}
    zaff::Ptr{Cdouble}

    # Cone
    C::Ptr{Ccone}
    A::Ptr{Cspmat}
    G::Ptr{Cspmat}
    c::Ptr{Cdouble}
    b::Ptr{Cdouble}
    h::Ptr{Cdouble}

    # equilibration vector
    xequil::Ptr{Cdouble}
    Aequil::Ptr{Cdouble}
    Gequil::Ptr{Cdouble}

    # scalings of problem data
    resx0::Cdouble
    resy0::Cdouble
    resz0::Cdouble

    # residuals
    rx::Ptr{Cdouble}
    ry::Ptr{Cdouble}
    rz::Ptr{Cdouble}
    rt::Cdouble
    hresx::Cdouble
    hresy::Cdouble
    hresz::Cdouble

    # temporary storage
    cx::Cdouble
    by::Cdouble
    hz::Cdouble
    sz::Cdouble

    # KKT System
    KKT::Ptr{Ckkt}

    # info struct
    info::Ptr{Cstats}

    # settings struct
    stgs::Ptr{Csettings}
end


text:
module C

using libsixel_jll
export libsixel_jll

const SIXELSTATUS = Cint

@enum characterSize::UInt32 begin
    CSIZE_7BIT = 0
    CSIZE_8BIT = 1
end

@enum methodForLargest::UInt32 begin
    LARGE_AUTO = 0
    LARGE_NORM = 1
    LARGE_LUM = 2
end

@enum methodForRep::UInt32 begin
    REP_AUTO = 0
    REP_CENTER_BOX = 1
    REP_AVERAGE_COLORS = 2
    REP_AVERAGE_PIXELS = 3
end

@enum methodForDiffuse::UInt32 begin
    DIFFUSE_AUTO = 0
    DIFFUSE_NONE = 1
    DIFFUSE_ATKINSON = 2
    DIFFUSE_FS = 3
    DIFFUSE_JAJUNI = 4
    DIFFUSE_STUCKI = 5
    DIFFUSE_BURKES = 6
    DIFFUSE_A_DITHER = 7
    DIFFUSE_X_DITHER = 8
end

@enum qualityMode::UInt32 begin
    QUALITY_AUTO = 0
    QUALITY_HIGH = 1
    QUALITY_LOW = 2
    QUALITY_FULL = 3
    QUALITY_HIGHCOLOR = 4
end

@enum builtinDither::UInt32 begin
    BUILTIN_MONO_DARK = 0
    BUILTIN_MONO_LIGHT = 1
    BUILTIN_XTERM16 = 2
    BUILTIN_XTERM256 = 3
    BUILTIN_VT340_MONO = 4
    BUILTIN_VT340_COLOR = 5
end

@enum formatType::UInt32 begin
    FORMATTYPE_COLOR = 0
    FORMATTYPE_GRAYSCALE = 64
    FORMATTYPE_PALETTE = 128
end

@enum pixelFormat::UInt32 begin
    PIXELFORMAT_RGB555 = 1
    PIXELFORMAT_RGB565 = 2
    PIXELFORMAT_RGB888 = 3
    PIXELFORMAT_BGR555 = 4
    PIXELFORMAT_BGR565 = 5
    PIXELFORMAT_BGR888 = 6
    PIXELFORMAT_ARGB8888 = 16
    PIXELFORMAT_RGBA8888 = 17
    PIXELFORMAT_G1 = 64
    PIXELFORMAT_G2 = 65
    PIXELFORMAT_G4 = 66
    PIXELFORMAT_G8 = 67
    PIXELFORMAT_AG88 = 83
    PIXELFORMAT_GA88 = 99
    PIXELFORMAT_PAL1 = 128
    PIXELFORMAT_PAL2 = 129
    PIXELFORMAT_PAL4 = 130
    PIXELFORMAT_PAL8 = 131
end

@enum paletteType::UInt32 begin
    PALETTETYPE_AUTO = 0
    PALETTETYPE_HLS = 1
    PALETTETYPE_RGB = 2
end

@enum encodePolicy::UInt32 begin
    ENCODEPOLICY_AUTO = 0
    ENCODEPOLICY_FAST = 1
    ENCODEPOLICY_SIZE = 2
end

@enum methodForResampling::UInt32 begin
    RES_NEAREST = 0
    RES_GAUSSIAN = 1
    RES_HANNING = 2
    RES_HAMMING = 3
    RES_BILINEAR = 4
    RES_WELSH = 5
    RES_BICUBIC = 6
    RES_LANCZOS2 = 7
    RES_LANCZOS3 = 8
    RES_LANCZOS4 = 9
end

# typedef void * ( * sixel_malloc_t ) ( size_t )
const sixel_malloc_t = Ptr{Cvoid}

# typedef void * ( * sixel_calloc_t ) ( size_t , size_t )
const sixel_calloc_t = Ptr{Cvoid}

# typedef void * ( * sixel_realloc_t ) ( void * , size_t )
const sixel_realloc_t = Ptr{Cvoid}

# typedef void ( * sixel_free_t ) ( void * )
const sixel_free_t = Ptr{Cvoid}

mutable struct sixel_allocator end

const sixel_allocator_t = sixel_allocator

function sixel_allocator_new(ppallocator, fn_malloc, fn_calloc, fn_realloc, fn_free)
    ccall((:sixel_allocator_new, libsixel), SIXELSTATUS, (Ptr{Ptr{sixel_allocator_t}}, sixel_malloc_t, sixel_calloc_t, sixel_realloc_t, sixel_free_t), ppallocator, fn_malloc, fn_calloc, fn_realloc, fn_free)
end

function sixel_allocator_ref(allocator)
    ccall((:sixel_allocator_ref, libsixel), Cvoid, (Ptr{sixel_allocator_t},), allocator)
end

function sixel_allocator_unref(allocator)
    ccall((:sixel_allocator_unref, libsixel), Cvoid, (Ptr{sixel_allocator_t},), allocator)
end

function sixel_allocator_malloc(allocator, n)
    ccall((:sixel_allocator_malloc, libsixel), Ptr{Cvoid}, (Ptr{sixel_allocator_t}, Csize_t), allocator, n)
end

function sixel_allocator_calloc(allocator, nelm, elsize)
    ccall((:sixel_allocator_calloc, libsixel), Ptr{Cvoid}, (Ptr{sixel_allocator_t}, Csize_t, Csize_t), allocator, nelm, elsize)
end

function sixel_allocator_realloc(allocator, p, n)
    ccall((:sixel_allocator_realloc, libsixel), Ptr{Cvoid}, (Ptr{sixel_allocator_t}, Ptr{Cvoid}, Csize_t), allocator, p, n)
end

function sixel_allocator_free(allocator, p)
    ccall((:sixel_allocator_free, libsixel), Cvoid, (Ptr{sixel_allocator_t}, Ptr{Cvoid}), allocator, p)
end

mutable struct sixel_output end

const sixel_output_t = sixel_output

# typedef int ( * sixel_write_function ) ( char * data , int size , void * priv )
const sixel_write_function = Ptr{Cvoid}

function sixel_output_new(output, fn_write, priv, allocator)
    ccall((:sixel_output_new, libsixel), SIXELSTATUS, (Ptr{Ptr{sixel_output_t}}, sixel_write_function, Any, Ptr{sixel_allocator_t}), output, fn_write, priv, allocator)
end

function sixel_output_create(fn_write, priv)
    ccall((:sixel_output_create, libsixel), Ptr{sixel_output_t}, (sixel_write_function, Ptr{Cvoid}), fn_write, priv)
end

function sixel_output_destroy(output)
    ccall((:sixel_output_destroy, libsixel), Cvoid, (Ptr{sixel_output_t},), output)
end

function sixel_output_ref(output)
    ccall((:sixel_output_ref, libsixel), Cvoid, (Ptr{sixel_output_t},), output)
end

function sixel_output_unref(output)
    ccall((:sixel_output_unref, libsixel), Cvoid, (Ptr{sixel_output_t},), output)
end

function sixel_output_get_8bit_availability(output)
    ccall((:sixel_output_get_8bit_availability, libsixel), Cint, (Ptr{sixel_output_t},), output)
end

function sixel_output_set_8bit_availability(output, availability)
    ccall((:sixel_output_set_8bit_availability, libsixel), Cvoid, (Ptr{sixel_output_t}, Cint), output, availability)
end

function sixel_output_set_gri_arg_limit(output, value)
    ccall((:sixel_output_set_gri_arg_limit, libsixel), Cvoid, (Ptr{sixel_output_t}, Cint), output, value)
end

function sixel_output_set_penetrate_multiplexer(output, penetrate)
    ccall((:sixel_output_set_penetrate_multiplexer, libsixel), Cvoid, (Ptr{sixel_output_t}, Cint), output, penetrate)
end

function sixel_output_set_skip_dcs_envelope(output, skip)
    ccall((:sixel_output_set_skip_dcs_envelope, libsixel), Cvoid, (Ptr{sixel_output_t}, Cint), output, skip)
end

function sixel_output_set_palette_type(output, palettetype)
    ccall((:sixel_output_set_palette_type, libsixel), Cvoid, (Ptr{sixel_output_t}, Cint), output, palettetype)
end

function sixel_output_set_encode_policy(output, encode_policy)
    ccall((:sixel_output_set_encode_policy, libsixel), Cvoid, (Ptr{sixel_output_t}, Cint), output, encode_policy)
end

mutable struct sixel_dither end

const sixel_dither_t = sixel_dither

function sixel_dither_new(ppdither, ncolors, allocator)
    ccall((:sixel_dither_new, libsixel), SIXELSTATUS, (Ptr{Ptr{sixel_dither_t}}, Cint, Ptr{sixel_allocator_t}), ppdither, ncolors, allocator)
end

function sixel_dither_create(ncolors)
    ccall((:sixel_dither_create, libsixel), Ptr{sixel_dither_t}, (Cint,), ncolors)
end

function sixel_dither_get(builtin_dither)
    ccall((:sixel_dither_get, libsixel), Ptr{sixel_dither_t}, (Cint,), builtin_dither)
end

function sixel_dither_destroy(dither)
    ccall((:sixel_dither_destroy, libsixel), Cvoid, (Ptr{sixel_dither_t},), dither)
end

function sixel_dither_ref(dither)
    ccall((:sixel_dither_ref, libsixel), Cvoid, (Ptr{sixel_dither_t},), dither)
end

function sixel_dither_unref(dither)
    ccall((:sixel_dither_unref, libsixel), Cvoid, (Ptr{sixel_dither_t},), dither)
end

function sixel_dither_initialize(dither, data, width, height, pixelformat, method_for_largest, method_for_rep, quality_mode)
    ccall((:sixel_dither_initialize, libsixel), SIXELSTATUS, (Ptr{sixel_dither_t}, Ptr{Cuchar}, Cint, Cint, Cint, Cint, Cint, Cint), dither, data, width, height, pixelformat, method_for_largest, method_for_rep, quality_mode)
end

function sixel_dither_set_diffusion_type(dither, method_for_diffuse)
    ccall((:sixel_dither_set_diffusion_type, libsixel), Cvoid, (Ptr{sixel_dither_t}, Cint), dither, method_for_diffuse)
end

function sixel_dither_get_num_of_palette_colors(dither)
    ccall((:sixel_dither_get_num_of_palette_colors, libsixel), Cint, (Ptr{sixel_dither_t},), dither)
end

function sixel_dither_get_num_of_histogram_colors(dither)
    ccall((:sixel_dither_get_num_of_histogram_colors, libsixel), Cint, (Ptr{sixel_dither_t},), dither)
end

function sixel_dither_get_num_of_histgram_colors(dither)
    ccall((:sixel_dither_get_num_of_histgram_colors, libsixel), Cint, (Ptr{sixel_dither_t},), dither)
end

function sixel_dither_get_palette(dither)
    ccall((:sixel_dither_get_palette, libsixel), Ptr{Cuchar}, (Ptr{sixel_dither_t},), dither)
end

function sixel_dither_set_palette(dither, palette)
    ccall((:sixel_dither_set_palette, libsixel), Cvoid, (Ptr{sixel_dither_t}, Ptr{Cuchar}), dither, palette)
end

function sixel_dither_set_complexion_score(dither, score)
    ccall((:sixel_dither_set_complexion_score, libsixel), Cvoid, (Ptr{sixel_dither_t}, Cint), dither, score)
end

function sixel_dither_set_body_only(dither, bodyonly)
    ccall((:sixel_dither_set_body_only, libsixel), Cvoid, (Ptr{sixel_dither_t}, Cint), dither, bodyonly)
end

function sixel_dither_set_optimize_palette(dither, do_opt)
    ccall((:sixel_dither_set_optimize_palette, libsixel), Cvoid, (Ptr{sixel_dither_t}, Cint), dither, do_opt)
end

function sixel_dither_set_pixelformat(dither, pixelformat)
    ccall((:sixel_dither_set_pixelformat, libsixel), Cvoid, (Ptr{sixel_dither_t}, Cint), dither, pixelformat)
end

function sixel_dither_set_transparent(dither, transparent)
    ccall((:sixel_dither_set_transparent, libsixel), Cvoid, (Ptr{sixel_dither_t}, Cint), dither, transparent)
end

# typedef void * ( * sixel_allocator_function ) ( size_t size )
const sixel_allocator_function = Ptr{Cvoid}

function sixel_encode(pixels, width, height, depth, dither, context)
    ccall((:sixel_encode, libsixel), SIXELSTATUS, (Ptr{Cuchar}, Cint, Cint, Cint, Ptr{sixel_dither_t}, Ptr{sixel_output_t}), pixels, width, height, depth, dither, context)
end

function sixel_decode_raw(p, len, pixels, pwidth, pheight, palette, ncolors, allocator)
    ccall((:sixel_decode_raw, libsixel), SIXELSTATUS, (Ptr{Cuchar}, Cint, Ptr{Ptr{Cuchar}}, Ptr{Cint}, Ptr{Cint}, Ptr{Ptr{Cuchar}}, Ptr{Cint}, Ptr{sixel_allocator_t}), p, len, pixels, pwidth, pheight, palette, ncolors, allocator)
end

function sixel_decode(sixels, size, pixels, pwidth, pheight, palette, ncolors, fn_malloc)
    ccall((:sixel_decode, libsixel), SIXELSTATUS, (Ptr{Cuchar}, Cint, Ptr{Ptr{Cuchar}}, Ptr{Cint}, Ptr{Cint}, Ptr{Ptr{Cuchar}}, Ptr{Cint}, sixel_allocator_function), sixels, size, pixels, pwidth, pheight, palette, ncolors, fn_malloc)
end

function sixel_helper_set_additional_message(message)
    ccall((:sixel_helper_set_additional_message, libsixel), Cvoid, (Ptr{Cchar},), message)
end

function sixel_helper_get_additional_message()
    ccall((:sixel_helper_get_additional_message, libsixel), Ptr{Cchar}, ())
end

function sixel_helper_format_error(status)
    ccall((:sixel_helper_format_error, libsixel), Ptr{Cchar}, (SIXELSTATUS,), status)
end

function sixel_helper_compute_depth(pixelformat)
    ccall((:sixel_helper_compute_depth, libsixel), Cint, (Cint,), pixelformat)
end

function sixel_helper_normalize_pixelformat(dst, dst_pixelformat, src, src_pixelformat, width, height)
    ccall((:sixel_helper_normalize_pixelformat, libsixel), SIXELSTATUS, (Ptr{Cuchar}, Ptr{Cint}, Ptr{Cuchar}, Cint, Cint, Cint), dst, dst_pixelformat, src, src_pixelformat, width, height)
end

function sixel_helper_scale_image(dst, src, srcw, srch, pixelformat, dstw, dsth, method_for_resampling, allocator)
    ccall((:sixel_helper_scale_image, libsixel), SIXELSTATUS, (Ptr{Cuchar}, Ptr{Cuchar}, Cint, Cint, Cint, Cint, Cint, Cint, Ptr{sixel_allocator_t}), dst, src, srcw, srch, pixelformat, dstw, dsth, method_for_resampling, allocator)
end

@enum imageFormat::UInt32 begin
    FORMAT_GIF = 0
    FORMAT_PNG = 1
    FORMAT_BMP = 2
    FORMAT_JPG = 3
    FORMAT_TGA = 4
    FORMAT_WBMP = 5
    FORMAT_TIFF = 6
    FORMAT_SIXEL = 7
    FORMAT_PNM = 8
    FORMAT_GD2 = 9
    FORMAT_PSD = 10
    FORMAT_HDR = 11
end

@enum loopControl::UInt32 begin
    LOOP_AUTO = 0
    LOOP_FORCE = 1
    LOOP_DISABLE = 2
end

mutable struct sixel_frame end

const sixel_frame_t = sixel_frame

function sixel_frame_new(ppframe, allocator)
    ccall((:sixel_frame_new, libsixel), SIXELSTATUS, (Ptr{Ptr{sixel_frame_t}}, Ptr{sixel_allocator_t}), ppframe, allocator)
end

function sixel_frame_create()
    ccall((:sixel_frame_create, libsixel), Ptr{sixel_frame_t}, ())
end

function sixel_frame_ref(frame)
    ccall((:sixel_frame_ref, libsixel), Cvoid, (Ptr{sixel_frame_t},), frame)
end

function sixel_frame_unref(frame)
    ccall((:sixel_frame_unref, libsixel), Cvoid, (Ptr{sixel_frame_t},), frame)
end

function sixel_frame_init(frame, pixels, width, height, pixelformat, palette, ncolors)
    ccall((:sixel_frame_init, libsixel), SIXELSTATUS, (Ptr{sixel_frame_t}, Ptr{Cuchar}, Cint, Cint, Cint, Ptr{Cuchar}, Cint), frame, pixels, width, height, pixelformat, palette, ncolors)
end

function sixel_frame_get_pixels(frame)
    ccall((:sixel_frame_get_pixels, libsixel), Ptr{Cuchar}, (Ptr{sixel_frame_t},), frame)
end

function sixel_frame_get_palette(frame)
    ccall((:sixel_frame_get_palette, libsixel), Ptr{Cuchar}, (Ptr{sixel_frame_t},), frame)
end

function sixel_frame_get_width(frame)
    ccall((:sixel_frame_get_width, libsixel), Cint, (Ptr{sixel_frame_t},), frame)
end

function sixel_frame_get_height(frame)
    ccall((:sixel_frame_get_height, libsixel), Cint, (Ptr{sixel_frame_t},), frame)
end

function sixel_frame_get_ncolors(frame)
    ccall((:sixel_frame_get_ncolors, libsixel), Cint, (Ptr{sixel_frame_t},), frame)
end

function sixel_frame_get_pixelformat(frame)
    ccall((:sixel_frame_get_pixelformat, libsixel), Cint, (Ptr{sixel_frame_t},), frame)
end

function sixel_frame_get_transparent(frame)
    ccall((:sixel_frame_get_transparent, libsixel), Cint, (Ptr{sixel_frame_t},), frame)
end

function sixel_frame_get_multiframe(frame)
    ccall((:sixel_frame_get_multiframe, libsixel), Cint, (Ptr{sixel_frame_t},), frame)
end

function sixel_frame_get_delay(frame)
    ccall((:sixel_frame_get_delay, libsixel), Cint, (Ptr{sixel_frame_t},), frame)
end

function sixel_frame_get_frame_no(frame)
    ccall((:sixel_frame_get_frame_no, libsixel), Cint, (Ptr{sixel_frame_t},), frame)
end

function sixel_frame_get_loop_no(frame)
    ccall((:sixel_frame_get_loop_no, libsixel), Cint, (Ptr{sixel_frame_t},), frame)
end

function sixel_frame_strip_alpha(frame, bgcolor)
    ccall((:sixel_frame_strip_alpha, libsixel), Cint, (Ptr{sixel_frame_t}, Ptr{Cuchar}), frame, bgcolor)
end

function sixel_frame_resize(frame, width, height, method_for_resampling)
    ccall((:sixel_frame_resize, libsixel), SIXELSTATUS, (Ptr{sixel_frame_t}, Cint, Cint, Cint), frame, width, height, method_for_resampling)
end

function sixel_frame_clip(frame, x, y, width, height)
    ccall((:sixel_frame_clip, libsixel), SIXELSTATUS, (Ptr{sixel_frame_t}, Cint, Cint, Cint, Cint), frame, x, y, width, height)
end

# typedef SIXELSTATUS ( * sixel_load_image_function ) ( sixel_frame_t /* in */ * frame , void /* in/out */ * context )
const sixel_load_image_function = Ptr{Cvoid}

function sixel_helper_load_image_file(filename, fstatic, fuse_palette, reqcolors, bgcolor, loop_control, fn_load, finsecure, cancel_flag, context, allocator)
    ccall((:sixel_helper_load_image_file, libsixel), SIXELSTATUS, (Ptr{Cchar}, Cint, Cint, Cint, Ptr{Cuchar}, Cint, sixel_load_image_function, Cint, Ptr{Cint}, Ptr{Cvoid}, Ptr{sixel_allocator_t}), filename, fstatic, fuse_palette, reqcolors, bgcolor, loop_control, fn_load, finsecure, cancel_flag, context, allocator)
end

function sixel_helper_write_image_file(data, width, height, palette, pixelformat, filename, imageformat, allocator)
    ccall((:sixel_helper_write_image_file, libsixel), SIXELSTATUS, (Ptr{Cuchar}, Cint, Cint, Ptr{Cuchar}, Cint, Ptr{Cchar}, Cint, Ptr{sixel_allocator_t}), data, width, height, palette, pixelformat, filename, imageformat, allocator)
end

mutable struct sixel_encoder end

const sixel_encoder_t = sixel_encoder

function sixel_encoder_new(ppencoder, allocator)
    ccall((:sixel_encoder_new, libsixel), SIXELSTATUS, (Ptr{Ptr{sixel_encoder_t}}, Ptr{sixel_allocator_t}), ppencoder, allocator)
end

function sixel_encoder_create()
    ccall((:sixel_encoder_create, libsixel), Ptr{sixel_encoder_t}, ())
end

function sixel_encoder_ref(encoder)
    ccall((:sixel_encoder_ref, libsixel), Cvoid, (Ptr{sixel_encoder_t},), encoder)
end

function sixel_encoder_unref(encoder)
    ccall((:sixel_encoder_unref, libsixel), Cvoid, (Ptr{sixel_encoder_t},), encoder)
end

function sixel_encoder_set_cancel_flag(encoder, cancel_flag)
    ccall((:sixel_encoder_set_cancel_flag, libsixel), SIXELSTATUS, (Ptr{sixel_encoder_t}, Ptr{Cint}), encoder, cancel_flag)
end

function sixel_encoder_setopt(encoder, arg, optarg)
    ccall((:sixel_encoder_setopt, libsixel), SIXELSTATUS, (Ptr{sixel_encoder_t}, Cint, Ptr{Cchar}), encoder, arg, optarg)
end

function sixel_encoder_encode(encoder, filename)
    ccall((:sixel_encoder_encode, libsixel), SIXELSTATUS, (Ptr{sixel_encoder_t}, Ptr{Cchar}), encoder, filename)
end

function sixel_encoder_encode_bytes(encoder, bytes, width, height, pixelformat, palette, ncolors)
    ccall((:sixel_encoder_encode_bytes, libsixel), SIXELSTATUS, (Ptr{sixel_encoder_t}, Ptr{Cuchar}, Cint, Cint, Cint, Ptr{Cuchar}, Cint), encoder, bytes, width, height, pixelformat, palette, ncolors)
end

mutable struct sixel_decoder end

const sixel_decoder_t = sixel_decoder

function sixel_decoder_new(ppdecoder, allocator)
    ccall((:sixel_decoder_new, libsixel), SIXELSTATUS, (Ptr{Ptr{sixel_decoder_t}}, Ptr{sixel_allocator_t}), ppdecoder, allocator)
end

function sixel_decoder_create()
    ccall((:sixel_decoder_create, libsixel), Ptr{sixel_decoder_t}, ())
end

function sixel_decoder_ref(decoder)
    ccall((:sixel_decoder_ref, libsixel), Cvoid, (Ptr{sixel_decoder_t},), decoder)
end

function sixel_decoder_unref(decoder)
    ccall((:sixel_decoder_unref, libsixel), Cvoid, (Ptr{sixel_decoder_t},), decoder)
end

function sixel_decoder_setopt(decoder, arg, optarg)
    ccall((:sixel_decoder_setopt, libsixel), SIXELSTATUS, (Ptr{sixel_decoder_t}, Cint, Ptr{Cchar}), decoder, arg, optarg)
end

function sixel_decoder_decode(decoder)
    ccall((:sixel_decoder_decode, libsixel), SIXELSTATUS, (Ptr{sixel_decoder_t},), decoder)
end

const LIBSIXEL_VERSION = "1.8.1"

const LIBSIXEL_ABI_VERSION = "1:6:0"

const SIXEL_OUTPUT_PACKET_SIZE = 16384

const SIXEL_PALETTE_MIN = 2

const SIXEL_PALETTE_MAX = 256

const SIXEL_USE_DEPRECATED_SYMBOLS = 1

const SIXEL_OK = 0x0000

const SIXEL_FALSE = 0x1000

const SIXEL_RUNTIME_ERROR = SIXEL_FALSE | 0x0100

const SIXEL_LOGIC_ERROR = SIXEL_FALSE | 0x0200

const SIXEL_FEATURE_ERROR = SIXEL_FALSE | 0x0300

const SIXEL_LIBC_ERROR = SIXEL_FALSE | 0x0400

const SIXEL_CURL_ERROR = SIXEL_FALSE | 0x0500

const SIXEL_JPEG_ERROR = SIXEL_FALSE | 0x0600

const SIXEL_PNG_ERROR = SIXEL_FALSE | 0x0700

const SIXEL_GDK_ERROR = SIXEL_FALSE | 0x0800

const SIXEL_GD_ERROR = SIXEL_FALSE | 0x0900

const SIXEL_STBI_ERROR = SIXEL_FALSE | 0x0a00

const SIXEL_STBIW_ERROR = SIXEL_FALSE | 0x0b00

const SIXEL_INTERRUPTED = SIXEL_OK | 0x0001

const SIXEL_BAD_ALLOCATION = SIXEL_RUNTIME_ERROR | 0x0001

const SIXEL_BAD_ARGUMENT = SIXEL_RUNTIME_ERROR | 0x0002

const SIXEL_BAD_INPUT = SIXEL_RUNTIME_ERROR | 0x0003

const SIXEL_NOT_IMPLEMENTED = SIXEL_FEATURE_ERROR | 0x0001

const SIXEL_LARGE_AUTO = 0x00

const SIXEL_LARGE_NORM = 0x01

const SIXEL_LARGE_LUM = 0x02

const SIXEL_REP_AUTO = 0x00

const SIXEL_REP_CENTER_BOX = 0x01

const SIXEL_REP_AVERAGE_COLORS = 0x02

const SIXEL_REP_AVERAGE_PIXELS = 0x03

const SIXEL_DIFFUSE_AUTO = 0x00

const SIXEL_DIFFUSE_NONE = 0x01

const SIXEL_DIFFUSE_ATKINSON = 0x02

const SIXEL_DIFFUSE_FS = 0x03

const SIXEL_DIFFUSE_JAJUNI = 0x04

const SIXEL_DIFFUSE_STUCKI = 0x05

const SIXEL_DIFFUSE_BURKES = 0x06

const SIXEL_DIFFUSE_A_DITHER = 0x07

const SIXEL_DIFFUSE_X_DITHER = 0x08

const SIXEL_QUALITY_AUTO = 0x00

const SIXEL_QUALITY_HIGH = 0x01

const SIXEL_QUALITY_LOW = 0x02

const SIXEL_QUALITY_FULL = 0x03

const SIXEL_QUALITY_HIGHCOLOR = 0x04

const SIXEL_BUILTIN_MONO_DARK = 0x00

const SIXEL_BUILTIN_MONO_LIGHT = 0x01

const SIXEL_BUILTIN_XTERM16 = 0x02

const SIXEL_BUILTIN_XTERM256 = 0x03

const SIXEL_BUILTIN_VT340_MONO = 0x04

const SIXEL_BUILTIN_VT340_COLOR = 0x05

const SIXEL_BUILTIN_G1 = 0x06

const SIXEL_BUILTIN_G2 = 0x07

const SIXEL_BUILTIN_G4 = 0x08

const SIXEL_BUILTIN_G8 = 0x09

const SIXEL_FORMATTYPE_COLOR = 0

const SIXEL_FORMATTYPE_GRAYSCALE = 1 << 6

const SIXEL_FORMATTYPE_PALETTE = 1 << 7

const SIXEL_PIXELFORMAT_RGB555 = SIXEL_FORMATTYPE_COLOR | 0x01

const SIXEL_PIXELFORMAT_RGB565 = SIXEL_FORMATTYPE_COLOR | 0x02

const SIXEL_PIXELFORMAT_RGB888 = SIXEL_FORMATTYPE_COLOR | 0x03

const SIXEL_PIXELFORMAT_BGR555 = SIXEL_FORMATTYPE_COLOR | 0x04

const SIXEL_PIXELFORMAT_BGR565 = SIXEL_FORMATTYPE_COLOR | 0x05

const SIXEL_PIXELFORMAT_BGR888 = SIXEL_FORMATTYPE_COLOR | 0x06

const SIXEL_PIXELFORMAT_ARGB8888 = SIXEL_FORMATTYPE_COLOR | 0x10

const SIXEL_PIXELFORMAT_RGBA8888 = SIXEL_FORMATTYPE_COLOR | 0x11

const SIXEL_PIXELFORMAT_ABGR8888 = SIXEL_FORMATTYPE_COLOR | 0x12

const SIXEL_PIXELFORMAT_BGRA8888 = SIXEL_FORMATTYPE_COLOR | 0x13

const SIXEL_PIXELFORMAT_G1 = SIXEL_FORMATTYPE_GRAYSCALE | 0x00

const SIXEL_PIXELFORMAT_G2 = SIXEL_FORMATTYPE_GRAYSCALE | 0x01

const SIXEL_PIXELFORMAT_G4 = SIXEL_FORMATTYPE_GRAYSCALE | 0x02

const SIXEL_PIXELFORMAT_G8 = SIXEL_FORMATTYPE_GRAYSCALE | 0x03

const SIXEL_PIXELFORMAT_AG88 = SIXEL_FORMATTYPE_GRAYSCALE | 0x13

const SIXEL_PIXELFORMAT_GA88 = SIXEL_FORMATTYPE_GRAYSCALE | 0x23

const SIXEL_PIXELFORMAT_PAL1 = SIXEL_FORMATTYPE_PALETTE | 0x00

const SIXEL_PIXELFORMAT_PAL2 = SIXEL_FORMATTYPE_PALETTE | 0x01

const SIXEL_PIXELFORMAT_PAL4 = SIXEL_FORMATTYPE_PALETTE | 0x02

const SIXEL_PIXELFORMAT_PAL8 = SIXEL_FORMATTYPE_PALETTE | 0x03

const SIXEL_PALETTETYPE_AUTO = 0

const SIXEL_PALETTETYPE_HLS = 1

const SIXEL_PALETTETYPE_RGB = 2

const SIXEL_ENCODEPOLICY_AUTO = 0

const SIXEL_ENCODEPOLICY_FAST = 1

const SIXEL_ENCODEPOLICY_SIZE = 2

const SIXEL_RES_NEAREST = 0

const SIXEL_RES_GAUSSIAN = 1

const SIXEL_RES_HANNING = 2

const SIXEL_RES_HAMMING = 3

const SIXEL_RES_BILINEAR = 4

const SIXEL_RES_WELSH = 5

const SIXEL_RES_BICUBIC = 6

const SIXEL_RES_LANCZOS2 = 7

const SIXEL_RES_LANCZOS3 = 8

const SIXEL_RES_LANCZOS4 = 9

const SIXEL_FORMAT_GIF = 0x00

const SIXEL_FORMAT_PNG = 0x01

const SIXEL_FORMAT_BMP = 0x02

const SIXEL_FORMAT_JPG = 0x03

const SIXEL_FORMAT_TGA = 0x04

const SIXEL_FORMAT_WBMP = 0x05

const SIXEL_FORMAT_TIFF = 0x06

const SIXEL_FORMAT_SIXEL = 0x07

const SIXEL_FORMAT_PNM = 0x08

const SIXEL_FORMAT_GD2 = 0x09

const SIXEL_FORMAT_PSD = 0x0a

const SIXEL_FORMAT_HDR = 0x0b

const SIXEL_LOOP_AUTO = 0

const SIXEL_LOOP_FORCE = 1

const SIXEL_LOOP_DISABLE = 2

const SIXEL_OPTFLAG_INPUT = Cchar('i')

const SIXEL_OPTFLAG_OUTPUT = Cchar('o')

const SIXEL_OPTFLAG_OUTFILE = Cchar('o')

const SIXEL_OPTFLAG_7BIT_MODE = Cchar('7')

const SIXEL_OPTFLAG_8BIT_MODE = Cchar('8')

const SIXEL_OPTFLAG_HAS_GRI_ARG_LIMIT = Cchar('R')

const SIXEL_OPTFLAG_COLORS = Cchar('p')

const SIXEL_OPTFLAG_MAPFILE = Cchar('m')

const SIXEL_OPTFLAG_MONOCHROME = Cchar('e')

const SIXEL_OPTFLAG_INSECURE = Cchar('k')

const SIXEL_OPTFLAG_INVERT = Cchar('i')

const SIXEL_OPTFLAG_HIGH_COLOR = Cchar('I')

const SIXEL_OPTFLAG_USE_MACRO = Cchar('u')

const SIXEL_OPTFLAG_MACRO_NUMBER = Cchar('n')

const SIXEL_OPTFLAG_COMPLEXION_SCORE = Cchar('C')

const SIXEL_OPTFLAG_IGNORE_DELAY = Cchar('g')

const SIXEL_OPTFLAG_STATIC = Cchar('S')

const SIXEL_OPTFLAG_DIFFUSION = Cchar('d')

const SIXEL_OPTFLAG_FIND_LARGEST = Cchar('f')

const SIXEL_OPTFLAG_SELECT_COLOR = Cchar('s')

const SIXEL_OPTFLAG_CROP = Cchar('c')

const SIXEL_OPTFLAG_WIDTH = Cchar('w')

const SIXEL_OPTFLAG_HEIGHT = Cchar('h')

const SIXEL_OPTFLAG_RESAMPLING = Cchar('r')

const SIXEL_OPTFLAG_QUALITY = Cchar('q')

const SIXEL_OPTFLAG_LOOPMODE = Cchar('l')

const SIXEL_OPTFLAG_PALETTE_TYPE = Cchar('t')

const SIXEL_OPTFLAG_BUILTIN_PALETTE = Cchar('b')

const SIXEL_OPTFLAG_ENCODE_POLICY = Cchar('E')

const SIXEL_OPTFLAG_BGCOLOR = Cchar('B')

const SIXEL_OPTFLAG_PENETRATE = Cchar('P')

const SIXEL_OPTFLAG_PIPE_MODE = Cchar('D')

const SIXEL_OPTFLAG_VERBOSE = Cchar('v')

const SIXEL_OPTFLAG_VERSION = Cchar('V')

const SIXEL_OPTFLAG_HELP = Cchar('H')

end # module


text:
@testset "Augmented Unscented Kalman filter" begin

    @testset "Time update" begin

        x = randn(6)
        A = randn(6,6)
        P = A'A
        B = randn(6,6)
        Q = B'B
        F = randn(6,6)
        f(x) = F * x
        f(x, noise) = F * x .+ noise

        tu = time_update(x, P, F, Q)
        tu_aug = time_update(x, P, f, Augment(Q))
        @test get_covariance(tu_aug) ≈ get_covariance(tu)
        @test get_state(tu_aug) ≈ get_state(tu)

        f!(y, x) = mul!(y, F, x)
        f!(y, x, noise) = y.= @~ F * x .+ noise
        tu_inter = AUKFTUIntermediate(6)
        tu_aug_inplace = time_update!(tu_inter, x, P, f!, Augment(Q))
        @test get_covariance(tu_aug_inplace) ≈ get_covariance(tu)
        @test get_state(tu_aug_inplace) ≈ get_state(tu)
    end

    @testset "Measurement update" begin

        x = randn(6)
        A = randn(6,6)
        P = A'A
        B = randn(3,3)
        R = B'B
        y = randn(3)
        H = randn(3,6)
        h(x) = H * x
        h(x, noise) = H * x .+ noise

        mu = measurement_update(x, P, y, H, R)
        mu_aug = measurement_update(x, P, y, h, Augment(R))
        @test get_covariance(mu_aug) ≈ get_covariance(mu)
        @test get_state(mu_aug) ≈ get_state(mu)

        h!(y, x) = mul!(y, H, x)
        h!(y, x, noise) = y .= @~ H * x .+ noise
        mu_inter = AUKFMUIntermediate(6, 3)
        mu_ukf_inplace = measurement_update!(mu_inter, x, P, y, h!, Augment(R))
        @test get_covariance(mu_ukf_inplace) ≈ get_covariance(mu)
        @test get_state(mu_ukf_inplace) ≈ get_state(mu)
    end
end


text:
using Arpack
using LinearAlgebra

"""
	approx_eigmin(M, iters)

Return the approximate smallest eigenvalue and eigenvector of a matrix
`M` in `iters` iterations.
"""
function approx_eigmin(M)
	values, vecs = eigs(M, nev=1, tol=1e-1, which=:SR)
	values[1], vec(vecs)
end