text:
module StructTypes

using UUIDs, Dates

"Abstract super type of various `StructType`s; see `StructTypes.DataType`, `StructTypes.CustomType`, `StructTypes.InterfaceType`, and `StructTypes.AbstractType` for more specific kinds of `StructType`s"
abstract type StructType end

StructType(x::T) where {T} = StructType(T)

"Default `StructTypes.StructType` for types that don't have a `StructType` defined; this ensures objects must have an explicit `StructType` to avoid unanticipated issues"
struct NoStructType <: StructType end

StructType(::Type{T}) where {T} = NoStructType()

"""
    StructTypes.StructType(::Type{T}) = StructTypes.CustomStruct()

Signal that `T` has a custom serialization/deserialization pattern that doesn't quite fit `StructTypes.DataType`
or `StructTypes.InterfaceType`. One common example are wrapper types, where you want to serialize as the wrapped
type and can reconstruct `T` manually from deserialized fields directly. Defining `CustomStruct()` requires
overloading `StructTypes.lower(x::T)`, which should return any serializable object, and optionally overload
`StructTypes.lowertype(::Type{T})`, which returns the type of the lowered object (it returns `Any` by default).
`lowertype` is used to deserialize an object, which is then passed to `StructTypes.construct(T, obj)`
for construction (which defaults to calling `T(obj)`).
"""
struct CustomStruct <: StructType end

"""
    StructTypes.lower(x::T)

"Unwrap" or otherwise transform `x` to another object that has a well-defined `StructType` definition.
This is a required method for types declaring `StructTypes.CustomStruct`.
Allows objects of type `T` to conveniently serialize/deserialize as another type, when their own
structure/definition isn't significant. Useful for wrapper types. See also
[`StructTypes.CustomStruct`](@ref) and [`StructType.lowertype`](@ref).
"""
function lower end

"""
    StructTypes.lowertype(::Type{T})

For `StructTypes.CustomStruct` types, they may optionally define `lowertype` to provide a
"deserialization" type, which defaults to `Any`. When deserializing a type `T`, the
deserializer will first call `StructTypes.lowertype(T) = S` and proceed with deserializing the
type `S` that was returned. Once `S` has been deserialized, the deserializer will call
`StructTypes.construct(T, x::S)`. With the default of `Any`, deserializers should return
an `AbstractDict` object where key/values can be enumerated/checked/retrieved to make it
decently convenient for `CustomStruct`s to construct themselves.
"""
function lowertype end

lowertype(::Type{T}) where {T} = Any

StructType(::Type{Some{T}}) where {T} = CustomStruct()
lower(x::Some) = x.value
lowertype(::Type{Some{T}}) where {T} = T

"A kind of `StructType` where an object's \"data\" is made up, at least in part, by its direct fields. When serializing, appropriate fields will be accessed directly."
abstract type DataType <: StructType end

"""
    StructTypes.StructType(::Type{T}) = StructTypes.Struct()
    StructTypes.StructType(::Type{T}) = StructTypes.UnorderedStruct()
    StructTypes.StructType(::Type{T}) = StructTypes.OrderedStruct()

Signal that `T` is an immutable type who's fields should be used directly when serializing/deserializing.
If a type is defined as `StructTypes.Struct`, it defaults to `StructTypes.UnorderedStruct`, which means its fields
are allowed to be serialized/deserialized in any order, as opposed to `StructTypes.OrderedStruct` which signals that
serialization/deserialization *must* occur in its defined field order exclusively. This can enable optimizations when
an order can be guaranteed, but care must be taken to ensure any serialization formats can properly guarantee the order
(for example, the JSON specification doesn't explicitly require ordered fields for "objects", though most implementations
have a way to support this).

For `StructTypes.UnorderedStruct`, if a field is missing from the serialization, `nothing` should be passed to the `StructTypes.construct` method.

For example, when deserializing a `Struct.OrderedStruct`, parsed input fields are passed directly, in input order to the `T` constructor, like `T(field1, field2, field3)`.
This means that field names may be ignored when deserializing; fields are directly passed to `T` in the order they're encountered.

Another example, for reading a `StructTypes.OrderedStruct()` from a JSON string input, each key-value pair is read in the order it is encountered in the JSON input, the keys are ignored, and the values are directly passed to the type at the end of the object parsing like `T(val1, val2, val3)`.
Yes, the JSON specification says that Objects are specifically **un-ordered** collections of key-value pairs,
but the truth is that many JSON libraries provide ways to maintain JSON Object key-value pair order when reading/writing.
Because of the minimal processing done while parsing, and the "trusting" that the Julia type constructor will be able to handle fields being present, missing, or even extra fields that should be ignored,
this is the fastest possible method for mapping a JSON input to a Julia structure.
If your workflow interacts with non-Julia APIs for sending/receiving JSON, you should take care to test and confirm the use of `StructTypes.OrderedStruct()` in the cases mentioned above: what if a field is missing when parsing? what if the key-value pairs are out of order? what if there extra fields get included that weren't anticipated? If your workflow is questionable on these points, or it would be too difficult to account for these scenarios in your type constructor, it would be better to consider the `StructTypes.UnorderedStruct` or `StructTypes.Mutable()` options.

```@example
struct CoolType
    val1::Int
    val2::Int
    val3::String
end

StructTypes.StructType(::Type{CoolType}) = StructTypes.OrderedStruct()

# JSON3 package as example
@assert JSON3.read("{\"val1\": 1, \"val2\": 2, \"val3\": 3}", CoolType) == CoolType(1, 2, "3")
# note how `val2` field is first, then `val1`, but fields are passed *in-order* to `CoolType` constructor; BE CAREFUL!
@assert JSON3.read("{\"val2\": 2, \"val1\": 1, \"val3\": 3}", CoolType) == CoolType(2, 1, "3")
# if we instead define `Struct`, which defaults to `StructTypes.UnorderedStruct`, then the above example works
StructTypes.StructType(::Type{CoolType}) = StructTypes.Struct()
@assert JSON3.read("{\"val2\": 2, \"val1\": 1, \"val3\": 3}", CoolType) == CoolType(1, 2, "3")
```
"""
abstract type Struct <: DataType end

struct UnorderedStruct <: Struct end
struct OrderedStruct <: Struct end

Struct() = UnorderedStruct()

StructType(u::Union) = Struct()
StructType(::Type{Any}) = Struct()

"""
    StructTypes.StructType(::Type{T}) = StructTypes.Mutable()

Signal that `T` is a mutable struct with an empty constructor for serializing/deserializing.
Though slightly less performant than `StructTypes.Struct`, `Mutable` is a much more robust method for mapping Julia struct fields for serialization. This technique requires your Julia type to be defined, **at a minimum**, like:
```julia
mutable struct T
    field1
    field2
    field3
    # etc.

    T() = new()
end
```
Note specifically that we're defining a `mutable struct` to allow field mutation, and providing a `T() = new()` inner constructor which constructs an "empty" `T` where `isbitstype` fields will be randomly initialized, and reference fields will be `#undef`. (Note that the inner constructor doesn't need to be **exactly** this, but at least needs to be callable like `T()`. If certain fields need to be intialized or zeroed out for security, then this should be accounted for in the inner constructor). For these mutable types, the type will first be initialized like `T()`, then serialization will take each key-value input pair, setting the field as the key is encountered, and converting the value to the appropriate field value. This flow has the nice properties of: allowing object construction success even if fields are missing in the input, and if "extra" fields exist in the input that aren't apart of the Julia struct's fields, they will automatically be ignored. This allows for maximum robustness when mapping Julia types to arbitrary data foramts that may be generated via web services, databases, other language libraries, etc.

There are a few additional helper methods that can be utilized by `StructTypes.Mutable()` types to hand-tune field reading/writing behavior:

* `StructTypes.names(::Type{T}) = ((:juliafield1, :serializedfield1), (:juliafield2, :serializedfield2))`: provides a mapping of Julia field name to expected serialized object key name. This affects both serializing and deserializing. When deserializing the `serializedfield1` key, the `juliafield1` field of `T` will be set. When serializing the `juliafield2` field of `T`, the output key will be `serializedfield2`. Field name mappings are provided as a `Tuple` of `Tuple{Symbol, Symbol}`s, i.e. each field mapping is a Julia field name `Symbol` (first) and serialized field name `Symbol` (second).
* `StructTypes.excludes(::Type{T}) = (:field1, :field2)`: specify fields of `T` to ignore when serializing and deserializing, provided as a `Tuple` of `Symbol`s. When deserializing, if `field1` is encountered as an input key, it's value will be read, but the field will not be set in `T`. When serializing, `field1` will be skipped when serializing out `T` fields as key-value pairs.

* `StructTypes.omitempties(::Type{T}) = (:field1, :field2)`: specify fields of `T` that shouldn't be serialized if they are "empty", provided as a `Tuple` of `Symbol`s. This only affects serializing. If a field is a collection (AbstractDict, AbstractArray, etc.) and `isempty(x) === true`, then it will not be serialized. If a field is `#undef`, it will not be serialized. If a field is `nothing`, it will not be serialized. To apply this to all fields of `T`, set `StructTypes.omitempties(::Type{T}) = true`. You can customize this behavior. For example, by default, `missing` is not considered to be "empty". If you want `missing` to be considered "empty" when serializing your type `MyType`, simply define:
```julia
@inline StructTypes.isempty(::Type{T}, ::Missing) where {T <: MyType} = true
```

* `StructTypes.keywordargs(::Type{T}) = (field1=(dateformat=dateformat"mm/dd/yyyy",), field2=(dateformat=dateformat"HH MM SS",))`: provide keyword arguments for fields of type `T` that should be passed to functions that set values for this field. Define `StructTypes.keywordargs` as a NamedTuple of NamedTuples.
"""
struct Mutable <: DataType end

"""
    StructTypes.names(::Type{T}) = ((:juliafield1, :serializedfield1), (:juliafield2, :serializedfield2))

Provides a mapping of Julia field name to expected serialized object key name.
This affects both reading and writing.
When reading the `serializedfield1` key, the `juliafield1` field of `T` will be set.
When writing the `juliafield2` field of `T`, the output key will be `serializedfield2`.
"""
function names end

names(x::T) where {T} = names(T)
names(::Type{T}) where {T} = ()

Base.@pure function julianame(names::Tuple{Vararg{Tuple{Symbol, Symbol}}}, serializationname::Symbol)
    for nm in names
        nm[2] === serializationname && return nm[1]
    end
    return serializationname
end

Base.@pure julianame(names::Tuple{}, serializationname::Int) = serializationname

Base.@pure function serializationname(names::Tuple{Vararg{Tuple{Symbol, Symbol}}}, julianame::Symbol)
    for nm in names
        nm[1] === julianame && return nm[2]
    end
    return julianame
end

Base.@pure serializationname(names::Tuple{}, julianame::Int) = julianame

"""
    StructTypes.excludes(::Type{T}) = (:field1, :field2)

Specify for a `StructTypes.Mutable` `StructType` the fields, given as a `Tuple` of `Symbol`s, that should be ignored when deserializing, and excluded from serializing.
"""
function excludes end

excludes(x::T) where {T} = excludes(T)
excludes(::Type{T}) where {T} = ()

"""
    StructTypes.omitempties(::Type{T}) = (:field1, :field2)
    StructTypes.omitempties(::Type{T}) = true

Specify for a `StructTypes.Mutable` `StructType` the fields, given as a `Tuple` of `Symbol`s, that should not be serialized if they're considered "empty".

If a field is a collection (AbstractDict, AbstractArray, etc.) and `isempty(x) === true`, then it will not be serialized. If a field is `#undef`, it will not be serialized. If a field is `nothing`, it will not be serialized. To apply this to all fields of `T`, set `StructTypes.omitempties(::Type{T}) = true`. You can customize this behavior. For example, by default, `missing` is not considered to be "empty". If you want `missing` to be considered "empty" when serializing your type `MyType`, simply define:
```julia
@inline StructTypes.isempty(::Type{T}, ::Missing) where {T <: MyType} = true
```
"""
function omitempties end

omitempties(x::T) where {T} = omitempties(T)
omitempties(::Type{T}) where {T} = ()

function isempty end

isempty(x::Union{AbstractDict, AbstractArray, AbstractString, Tuple, NamedTuple}) = Base.isempty(x)
isempty(::Number) = false
isempty(::Nothing) = true
isempty(x) = false
isempty(x, i) = isempty(Core.getfield(x, i))
isempty(::Type{T}, x) where {T} = isempty(x) # generic fallback
isempty(::Type{T}, x, i) where {T} = isempty(T, Core.getfield(x, i)) # generic fallback

"""
    StructTypes.keywordargs(::Type{MyType}) = (field1=(dateformat=dateformat"mm/dd/yyyy",), field2=(dateformat=dateformat"HH MM SS",))

Specify for a `StructTypes.Mutable` the keyword arguments by field, given as a `NamedTuple` of `NamedTuple`s, that should be passed
to the `StructTypes.construct` method when deserializing `MyType`. This essentially allows defining specific keyword arguments you'd like to be passed for each field
in your struct. Note that keyword arguments can be passed when reading, like `JSON3.read(source, MyType; dateformat=...)` and they will be passed down to each `StructTypes.construct` method.
`StructTypes.keywordargs` just allows the defining of specific keyword arguments per field.
"""
function keywordargs end

keywordargs(x::T) where {T} = keywordargs(T)
keywordargs(::Type{T}) where {T} = NamedTuple()

"""
    StructTypes.idproperty(::Type{MyType}) = :id

Specify which field of a type uniquely identifies it. The unique identifier field name is given as a Symbol.
Useful in database applications where the id field can be used to distinguish separate objects.
"""
function idproperty end

idproperty(x::T) where {T} = idproperty(T)
idproperty(::Type{T}) where {T} = :_

"""
    StructTypes.fieldprefix(::Type{MyType}, field::Symbol) = :field_

When interacting with database tables and other strictly 2D data formats, objects with aggregate fields
must be flattened into a single set of column names. When deserializing a set of columns into an
object with aggregate fields, a field type's `fieldprefix` signals that column names beginning with, in
the example above, `:field_`, should be collected together when constructing the `field` field of `MyType`.
Note the default definition is `StructTypes.fieldprefix(T, nm) = Symbol(nm, :_)`.

Here's a more concrete, albeit contrived, example:
```julia
struct Spouse
    id::Int
    name::String
end

StructTypes.StructType(::Type{Spouse}) = StructTypes.Struct()

struct Person
    id::Int
    name::String
    spouse::Spouse
end

StructTypes.StructType(::Type{Person}) = StructTypes.Struct()
StructTypes.fieldprefix(::Type{Person}, field::Symbol) = field == :spouse ? :spouse_ : :_
```
Here we have two structs, `Spouse` and `Person`, and a `Person` has a `spouse::Spouse`.
The database tables to represent these entities might look like:
```SQL
CREATE TABLE spouse (id INT, name VARCHAR);
CREATE TABLE person (id INT, name VARCHAR, spouse_id INT);
```
If we want to leverage a package like Strapping.jl to automatically handle the object construction
for us, we could write a get query like the following to ensure a full `Person` with field `spouse::Spouse`
can be constructed:
```julia
getPerson(id::Int) = Strapping.construct(Person, DBInterface.execute(db,
    \"\"\"
        SELECT person.id as id, person.name as name, spouse.id as spouse_id, spouse.name as spouse_name
        FROM person
        LEFT JOIN spouse ON person.spouse_id = spouse.id
        WHERE person.id = \$id
    \"\"\"))
```
This works because the column names in the resultset of this query are "id, name, spouse_id, spouse_name";
because we defined `StructTypes.fieldprefix` for `Person`, Strapping.jl knows that each
column starting with "spouse_" should be used in constructing the `Spouse` field of `Person`.
"""
function fieldprefix end

Base.@pure fieldprefix(::Type{T}, nm::Symbol) where {T} = Symbol(nm, :_)

"""
    StructTypes.InterfaceType

An abstract type used in the API for "interface types" to map Julia types to a "standard" set of common types. See docs for the following concrete subtypes for more details:

  * StructTypes.DictType
  * StructTypes.ArrayType
  * StructTypes.StringType
  * StructTypes.NumberType
  * StructTypes.BoolType
  * StructTypes.NullType
"""
abstract type InterfaceType <: StructType end

"""
    StructTypes.construct(T, args...; kw...)

Function that custom types can overload for their `T` to construct an instance, given `args...` and `kw...`.
The default definition is `StructTypes.construct(T, args...; kw...) = T(args...; kw...)`.
"""
construct(T, args...; kw...) = T(args...; kw...)
construct(::Type{T}, x::T; kw...) where {T} = x

"""
    StructTypes.StructType(::Type{T}) = StructTypes.DictType()

Declare that `T` should map to a dict-like object of unordered key-value pairs, where keys are `Symbol`, `String`, or `Int64`, and values are any other type (or `Any`).

Types already declared as `StructTypes.DictType()` include:
  * Any subtype of `AbstractDict`
  * Any `NamedTuple` type
  * The `Pair` type

So if your type subtypes `AbstractDict` and implements its interface, then it will inherit the `DictType` definition and serializing/deserializing should work automatically.

Otherwise, the interface to satisfy `StructTypes.DictType()` for deserializing is:

  * `T(x::Dict{Symbol, Any})`: implement a constructor that takes a `Dict{Symbol, Any}` of input key-value pairs
  * `StructTypes.construct(::Type{T}, x::Dict; kw...)`: alternatively, you may overload the `StructTypes.construct` method for your type if defining a constructor is undesirable (or would cause other clashes or ambiguities)

The interface to satisfy for serializing is:

  * `pairs(x)`: implement the `pairs` iteration function (from Base) to iterate key-value pairs to be serialized
  * `StructTypes.keyvaluepairs(x::T)`: alternatively, you can overload the `StructTypes.keyvaluepairs` function if overloading `pairs` isn't possible for whatever reason
"""
struct DictType <: InterfaceType end

StructType(::Type{<:AbstractDict}) = DictType()
StructType(::Type{<:Pair}) = DictType()

keyvaluepairs(x) = pairs(x)
keyvaluepairs(x::Pair) = (x,)

construct(::Type{NamedTuple}, x::Dict; kw...) = NamedTuple{Tuple(keys(x))}(values(x))
construct(::Type{NamedTuple{names}}, x::Dict; kw...) where {names} = NamedTuple{names}(Tuple(x[nm] for nm in names))
construct(::Type{NamedTuple{names, types}}, x::Dict; kw...) where {names, types} = NamedTuple{names, types}(Tuple(x[nm] for nm in names))

"""
    StructTypes.StructType(::Type{T}) = StructTypes.ArrayType()

Declare that `T` should map to an array of ordered elements, homogenous or otherwise.

Types already declared as `StructTypes.ArrayType()` include:
  * Any subtype of `AbstractArray`
  * Any subtype of `AbstractSet`
  * Any `Tuple` type

So if your type already subtypes these and satifies their interface, things should just work.

Otherwise, the interface to satisfy `StructTypes.ArrayType()` for deserializing is:

  * `T(x::Vector)`: implement a constructor that takes a `Vector` argument of values and constructs a `T`
  * `StructTypes.construct(::Type{T}, x::Vecto; kw...)`: alternatively, you may overload the `StructTypes.construct` method for your type if defining a constructor isn't possible
  * Optional: `Base.IteratorEltype(::Type{T}) = Base.HasEltype()` and `Base.eltype(x::T)`: this can be used to signal that elements for your type are expected to be a homogenous type

The interface to satisfy for serializing is:

  * `iterate(x::T)`: just iteration over each element is required; note if you subtype `AbstractArray` and define `getindex(x::T, i::Int)`, then iteration is inherited for your type
"""
struct ArrayType <: InterfaceType end

StructType(::Type{<:AbstractArray}) = ArrayType()
StructType(::Type{<:AbstractSet}) = ArrayType()
StructType(::Type{<:Tuple}) = ArrayType()

"""
    StructTypes.StructType(::Type{T}) = StructTypes.StringType()

Declare that `T` should map to a string value.

Types already declared as `StructTypes.StringType()` include:
  * Any subtype of `AbstractString`
  * The `Symbol` type
  * Any subtype of `Enum` (values are written with their symbolic name)
  * Any subtype of `AbstractChar`
  * The `UUID` type
  * Any `Dates.TimeType` subtype (`Date`, `DateTime`, `Time`, etc.)

So if your type is an `AbstractString` or `Enum`, then things should already work.

Otherwise, the interface to satisfy `StructTypes.StringType()` for deserializing is:

  * `T(x::String)`: define a constructor for your type that takes a single String argument
  * `StructTypes.construct(::Type{T}, x::String; kw...)`: alternatively, you may overload `StructTypes.construct` for your type
  * `StructTypes.construct(::Type{T}, ptr::Ptr{UInt8}, len::Int; kw...)`: another option is to overload `StructTypes.construct` with pointer and length arguments, if it's possible for your custom type to take advantage of avoiding the full string materialization; note that your type should implement both `StructTypes.construct` methods, since direct pointer/length deserialization may not be possible for some inputs

The interface to satisfy for serializing is:

  * `Base.string(x::T)`: overload `Base.string` for your type to return a "stringified" value, or more specifically, that returns an `AbstractString`, and should implement `ncodeunits(x)` and `codeunit(x, i)`.
"""
struct StringType <: InterfaceType end

StructType(::Type{<:AbstractString}) = StringType()
StructType(::Type{Symbol}) = StringType()
StructType(::Type{<:Enum}) = StringType()
StructType(::Type{<:AbstractChar}) = StringType()
StructType(::Type{UUID}) = StringType()
StructType(::Type{T}) where {T <: Dates.TimeType} = StringType()
StructType(::Type{VersionNumber}) = StringType()

function construct(::Type{Char}, str::String; kw...)
    if length(str) == 1
        return Char(str[1])
    else
        throw(ArgumentError("invalid conversion from string to Char: '$str'"))
    end
end

_symbol(ptr, len) = ccall(:jl_symbol_n, Ref{Symbol}, (Ptr{UInt8}, Int), ptr, len)

construct(::Type{E}, ptr::Ptr{UInt8}, len::Int; kw...) where {E <: Enum} = construct(E, _symbol(ptr, len))
construct(::Type{E}, str::String; kw...) where {E <: Enum} = construct(E, Symbol(str))

function construct(::Type{E}, sym::Symbol; kw...) where {E <: Enum}
    @static if VERSION < v"1.2.0-DEV.272"
        Core.eval(parentmodule(E), sym)
    else
        for (k, v) in Base.Enums.namemap(E)
            sym == v && return E(k)
        end
        throw(ArgumentError("invalid $E string value: \"$sym\""))
    end
end

construct(::Type{T}, str::String; kw...) where {T <: AbstractString} = convert(T, str)
construct(::Type{T}, ptr::Ptr{UInt8}, len::Int; kw...) where {T} = construct(T, unsafe_string(ptr, len); kw...)
construct(::Type{Symbol}, ptr::Ptr{UInt8}, len::Int; kw...) = _symbol(ptr, len)
construct(::Type{T}, str::String; dateformat::Dates.DateFormat=Dates.default_format(T), kw...) where {T <: Dates.TimeType} = T(str, dateformat)

"""
    StructTypes.StructType(::Type{T}) = StructTypes.NumberType()

Declare that `T` should map to a number value.

Types already declared as `StructTypes.NumberType()` include:
  * Any subtype of `Signed`
  * Any subtype of `Unsigned`
  * Any subtype of `AbstractFloat`

In addition to declaring `StructTypes.NumberType()`, custom types can also specify a specific, **existing** number type it should map to. It does this like:
```julia
StructTypes.numbertype(::Type{T}) = Float64
```

In this case, `T` declares it should map to an already-supported number type: `Float64`. This means that when deserializing, an input will be parsed/read/deserialiezd as a `Float64` value, and then call `T(x::Float64)`. Note that custom types may also overload `StructTypes.construct(::Type{T}, x::Float64; kw...)` if using a constructor isn't possible. Also note that the default for any type declared as `StructTypes.NumberType()` is `Float64`.

Similarly for serializing, `Float64(x::T)` will first be called before serializing the resulting `Float64` value.
"""
struct NumberType <: InterfaceType end

StructType(::Type{<:Real}) = NumberType()
numbertype(::Type{T}) where {T <: Real} = T
numbertype(x) = Float64

"""
    StructTypes.StructType(::Type{T}) = StructTypes.BoolType()

Declare that `T` should map to a boolean value.

Types already declared as `StructTypes.BoolType()` include:
  * `Bool`

The interface to satisfy for deserializing is:
  * `T(x::Bool)`: define a constructor that takes a single `Bool` value
  * `StructTypes.construct(::Type{T}, x::Bool; kw...)`: alternatively, you may overload `StructTypes.construct`

The interface to satisfy for serializing is:
  * `Bool(x::T)`: define a conversion to `Bool` method
"""
struct BoolType <: InterfaceType end

StructType(::Type{Bool}) = BoolType()

"""
    StructTypes.StructType(::Type{T}) = StructTypes.NullType()

Declare that `T` should map to a "null" value.

Types already declared as `StructTypes.NullType()` include:
  * `nothing`
  * `missing`

The interface to satisfy for serializing is:
  * `T()`: an empty constructor for `T`
  * `StructTypes.construct(::Type{T}, x::Nothing; kw...)`: alternatively, you may overload `StructTypes.construct`

There is no interface for serializing; if a custom type is declared as `StructTypes.NullType()`, then serializing will be handled specially; writing `null` in JSON, `NULL` in SQL, etc.
"""
struct NullType <: InterfaceType end

StructType(::Type{Nothing}) = NullType()
StructType(::Type{Missing}) = NullType()
construct(::Type{T}, ::Nothing; kw...) where {T} = T()

"""
    StructTypes.StructType(::Type{T}) = StructTypes.AbstractType()

Signal that `T` is an abstract type, and when deserializing, one of its concrete subtypes will be materialized,
based on a "type" key/field in the serialization object.

Thus, `StructTypes.AbstractType`s *must* define `StructTypes.subtypes`, which should be a NamedTuple with subtype keys mapping to concrete Julia subtype values. You may optionally define `StructTypes.subtypekey` that indicates which input key/field name should be used for identifying the appropriate concrete subtype. A quick example using the JSON3.jl package should help illustrate proper use of this `StructType`:
```julia
abstract type Vehicle end

struct Car <: Vehicle
    type::String
    make::String
    model::String
    seatingCapacity::Int
    topSpeed::Float64
end

struct Truck <: Vehicle
    type::String
    make::String
    model::String
    payloadCapacity::Float64
end

StructTypes.StructType(::Type{Vehicle}) = StructTypes.AbstractType()
StructTypes.StructType(::Type{Car}) = StructTypes.Struct()
StructTypes.StructType(::Type{Truck}) = StructTypes.Struct()
StructTypes.subtypekey(::Type{Vehicle}) = :type
StructTypes.subtypes(::Type{Vehicle}) = (car=Car, truck=Truck)

# example from StructTypes deserialization
car = JSON3.read(\"\"\"
{
    "type": "car",
    "make": "Mercedes-Benz",
    "model": "S500",
    "seatingCapacity": 5,
    "topSpeed": 250.1
}\"\"\", Vehicle)
```
Here we have a `Vehicle` type that is defined as a `StructTypes.AbstractType()`.
We also have two concrete subtypes, `Car` and `Truck`. In addition to the `StructType` definition,
we also define `StructTypes.subtypekey(::Type{Vehicle}) = :type`, which signals that when deserializing,
when it encounters the `type` key, it should use the **value**, in the above example: `car`,
to discover the appropriate concrete subtype to parse the structure as, in this case `Car`.
The mapping of subtype key value to concrete Julia subtype is defined in our example via
`StructTypes.subtypes(::Type{Vehicle}) = (car=Car, truck=Truck)`.
Thus, `StructTypes.AbstractType` is useful when the object to deserialize includes a "subtype"
key-value pair that can be used to parse a specific, concrete type; in our example,
parsing the structure as a `Car` instead of a `Truck`.
"""
struct AbstractType <: StructType end

subtypekey(x::T) where {T} = subtypekey(T)
subtypekey(::Type{T}) where {T} = :type
subtypes(x::T) where {T} = subtypes(T)
subtypes(::Type{T}) where {T} = NamedTuple()

# helper functions for type-stable reflection while operating on struct fields

"""
    StructTypes.construct(f, T) => T

Apply function `f(i, name, FT)` over each field index `i`, field name `name`, and field type `FT`
of type `T`, passing the function results to `T` for construction, like `T(x_1, x_2, ...)`.
Note that any `StructTypes.names` mappings are applied, as well as field-specific keyword arguments via `StructTypes.keywordargs`.
"""
@inline function construct(f, ::Type{T}) where {T}
    N = fieldcount(T)
    N == 0 && return T()
    nms = names(T)
    kwargs = keywordargs(T)
    constructor = T <: Tuple ? tuple : T <: NamedTuple ? ((x...) -> T(tuple(x...))) : T
    # unroll first 32 fields
    Base.@nexprs 32 i -> begin
        k_i = fieldname(T, i)
        if haskey(kwargs, k_i)
            x_i = f(i, serializationname(nms, k_i), fieldtype(T, i); kwargs[k_i]...)
        else
            x_i = f(i, serializationname(nms, k_i), fieldtype(T, i))
        end
        if N == i
            return Base.@ncall(i, constructor, x)
        end
    end
    vals = []
    for i = 33:N
        k_i = fieldname(T, i)
        if haskey(kwargs, k_i)
            x_i = f(i, serializationname(nms, k_i), fieldtype(T, i); kwargs[k_i]...)
        else
            x_i = f(i, serializationname(nms, k_i), fieldtype(T, i))
        end
        push!(vals, x_i)
    end
    return constructor(x_1, x_2, x_3, x_4, x_5, x_6, x_7, x_8, x_9, x_10, x_11, x_12, x_13, x_14, x_15, x_16,
             x_17, x_18, x_19, x_20, x_21, x_22, x_23, x_24, x_25, x_26, x_27, x_28, x_29, x_30, x_31, x_32, vals...)
end

Base.@pure function symbolin(names::Union{Tuple{Vararg{Symbol}}, Bool}, name)
    names isa Bool && return names
    for nm in names
        nm === name && return true
    end
    return false
end

"""
    StructTypes.foreachfield(f, x::T) => Nothing

Apply function `f(i, name, FT, v; kw...)` over each field index `i`, field name `name`, field type `FT`,
field value `v`, and any `kw` keyword arguments defined in `StructTypes.keywordargs` for `name` in `x`.
Nothing is returned and results from `f` are ignored. Similar to `Base.foreach` over collections.

Various "configurations" are respected when applying `f` to each field:
  * If keyword arguments have been defined for a field via `StructTypes.keywordargs`, they will be passed like `f(i, name, FT, v; kw...)`
  * If `StructTypes.names` has been defined, `name` will be the serialization name instead of the defined julia field name
  * If a field is undefined or empty and `StructTypes.omitempties` is defined, `f` won't be applied to that field
  * If a field has been excluded via `StructTypes.excludes`, it will be skipped
"""
@inline function foreachfield(f, x::T) where {T}
    N = fieldcount(T)
    N == 0 && return
    excl = excludes(T)
    nms = names(T)
    kwargs = keywordargs(T)
    emp = omitempties(T) === true ? fieldnames(T) : omitempties(T)
    Base.@nexprs 32 i -> begin
        k_i = fieldname(T, i)
        if !symbolin(excl, k_i) && isdefined(x, i)
            v_i = Core.getfield(x, i)
            if !symbolin(emp, k_i) || !isempty(T, x, i)
                if haskey(kwargs, k_i)
                    f(i, serializationname(nms, k_i), fieldtype(T, i), v_i; kwargs[k_i]...)
                else
                    f(i, serializationname(nms, k_i), fieldtype(T, i), v_i)
                end
            end
        end
        N == i && @goto done
    end
    if N > 32
        for i = 33:N
            k_i = fieldname(T, i)
            if !symbolin(excl, k_i) && isdefined(x, i)
                v_i = Core.getfield(x, i)
                if !symbolin(emp, k_i) || !isempty(T, x, i)
                    if haskey(kwargs, k_i)
                        f(i, serializationname(nms, k_i), fieldtype(T, i), v_i; kwargs[k_i]...)
                    else
                        f(i, serializationname(nms, k_i), fieldtype(T, i), v_i)
                    end
                end
            end
        end
    end

@label done
    return
end

"""
    StructTypes.foreachfield(f, T) => Nothing

Apply function `f(i, name, FT; kw...)` over each field index `i`, field name `name`, field type `FT`,
and any `kw` keyword arguments defined in `StructTypes.keywordargs` for `name` on type `T`.
Nothing is returned and results from `f` are ignored. Similar to `Base.foreach` over collections.

Various "configurations" are respected when applying `f` to each field:
  * If keyword arguments have been defined for a field via `StructTypes.keywordargs`, they will be passed like `f(i, name, FT, v; kw...)`
  * If `StructTypes.names` has been defined, `name` will be the serialization name instead of the defined julia field name
  * If a field has been excluded via `StructTypes.excludes`, it will be skipped
"""
@inline function foreachfield(f, ::Type{T}) where {T}
    N = fieldcount(T)
    N == 0 && return
    excl = excludes(T)
    nms = names(T)
    kwargs = keywordargs(T)
    Base.@nexprs 32 i -> begin
        k_i = fieldname(T, i)
        if !symbolin(excl, k_i)
            if haskey(kwargs, k_i)
                f(i, serializationname(nms, k_i), fieldtype(T, i); kwargs[k_i]...)
            else
                f(i, serializationname(nms, k_i), fieldtype(T, i))
            end
        end
        N == i && @goto done
    end
    if N > 32
        for i = 33:N
            k_i = fieldname(T, i)
            if !symbolin(excl, k_i)
                if haskey(kwargs, k_i)
                    f(i, serializationname(nms, k_i), fieldtype(T, i); kwargs[k_i]...)
                else
                    f(i, serializationname(nms, k_i), fieldtype(T, i))
                end
            end
        end
    end

@label done
    return
end

"""
    StructTypes.mapfields!(f, x::T)

Applys the function `f(i, name, FT; kw...)` to each field index `i`, field name `name`, field type `FT`,
and any `kw` defined in `StructTypes.keywordargs` for `name` of `x`, and calls `setfield!(x, name, y)`
where `y` is returned from `f`.

This is a convenience function for working with `StructTypes.Mutable`, where a function can be
applied over the fields of the mutable struct to set each field value. It respects the various
StructTypes configurations in terms of skipping/naming/passing keyword arguments as defined.
"""
@inline function mapfields!(f, x::T) where {T}
    N = fieldcount(T)
    N == 0 && return
    excl = excludes(T)
    nms = names(T)
    kwargs = keywordargs(T)
    Base.@nexprs 32 i -> begin
        k_i = fieldname(T, i)
        if !symbolin(excl, k_i)
            if haskey(kwargs, k_i)
                setfield!(x, k_i, f(i, serializationname(nms, k_i), fieldtype(T, i); kwargs[k_i]...))
            else
                setfield!(x, k_i, f(i, serializationname(nms, k_i), fieldtype(T, i)))
            end
        end
        N == i && @goto done
    end
    if N > 32
        for i = 33:N
            k_i = fieldname(T, i)
            if !symbolin(excl, k_i)
                if haskey(kwargs, k_i)
                    setfield!(x, k_i, f(i, serializationname(nms, k_i), fieldtype(T, i); kwargs[k_i]...))
                else
                    setfield!(x, k_i, f(i, serializationname(nms, k_i), fieldtype(T, i)))
                end
            end
        end
    end

@label done
    return
end

"""
    StructTypes.applyfield!(f, x::T, nm::Symbol) => Bool

Convenience function for working with a `StructTypes.Mutable` object. For a given serialization name `nm`,
apply the function `f(i, name, FT; kw...)` to the field index `i`, field name `name`, field type `FT`, and
any keyword arguments `kw` defined in `StructTypes.keywordargs`, setting the field value to the return
value of `f`. Various StructType configurations are respected like keyword arguments, names, and exclusions.
`applyfield!` returns whether `f` was executed or not; if `nm` isn't a valid field name on `x`, `false`
will be returned (important for applications where the input still needs to consume the field, like json parsing).
Note that the input `nm` is treated as the serialization name, so any `StructTypes.names`
mappings will be applied, and the function will be passed the Julia field name.
"""
@inline function applyfield!(f, x::T, nm::Symbol) where {T}
    N = fieldcount(T)
    excl = excludes(T)
    nms = names(T)
    kwargs = keywordargs(T)
    nm = julianame(nms, nm)
    f_applied = false
    # unroll the first 32 field checks to avoid dynamic dispatch if possible
    Base.@nif(
        33,
        i -> (i <= N && fieldname(T, i) === nm && !symbolin(excl, nm)),
        i -> begin
            FT_i = fieldtype(T, i)
            if haskey(kwargs, nm)
                y_i = f(i, nm, FT_i; kwargs[nm]...)
            else
                y_i = f(i, nm, FT_i)
            end
            setfield!(x, i, y_i)
            f_applied = true
        end,
        i -> begin
            for j in 33:N
                (fieldname(T, j) === nm && !symbolin(excl, nm)) || continue
                FT_j = fieldtype(T, j)
                if haskey(kwargs, nm)
                    y_j = f(j, nm, FT_j; kwargs[nm]...)
                else
                    y_j = f(j, nm, FT_j)
                end
                setfield!(x, j, y_j)
                f_applied = true
                break
            end
        end
    )
    return f_applied
end

"""
    StructTypes.applyfield(f, ::Type{T}, nm::Symbol) => Bool

Convenience function for working with a `StructTypes.Mutable` object. For a given serialization name `nm`,
apply the function `f(i, name, FT; kw...)` to the field index `i`, field name `name`, field type `FT`, and
any keyword arguments `kw` defined in `StructTypes.keywordargs`.
Various StructType configurations are respected like keyword arguments, names, and exclusions.
`applyfield` returns whether `f` was executed or not; if `nm` isn't a valid field name on `x`, `false`
will be returned (important for applications where the input still needs to consume the field, like json parsing).
Note that the input `nm` is treated as the serialization name, so any `StructTypes.names`
mappings will be applied, and the function will be passed the Julia field name.
"""
@inline function applyfield(f, ::Type{T}, nm::Symbol) where {T}
    N = fieldcount(T)
    excl = excludes(T)
    nms = names(T)
    kwargs = keywordargs(T)
    nm = julianame(nms, nm)
    f_applied = false
    # unroll the first 32 field checks to avoid dynamic dispatch if possible
    Base.@nif(
        33,
        i -> (i <= N && fieldname(T, i) === nm && !symbolin(excl, nm)),
        i -> begin
            FT_i = fieldtype(T, i)
            if haskey(kwargs, nm)
                y_i = f(i, nm, FT_i; kwargs[nm]...)
            else
                y_i = f(i, nm, FT_i)
            end
            f_applied = true
        end,
        i -> begin
            for j in 33:N
                (fieldname(T, j) === nm && !symbolin(excl, nm)) || continue
                FT_j = fieldtype(T, j)
                if haskey(kwargs, nm)
                    y_j = f(j, nm, FT_j; kwargs[nm]...)
                else
                    y_j = f(j, nm, FT_j)
                end
                f_applied = true
                break
            end
        end
    )
    return f_applied
end

@inline function construct(values::Vector{Any}, ::Type{T}) where {T}
    N = fieldcount(T)
    N == 0 && return T()
    nms = names(T)
    kwargs = keywordargs(T)
    constructor = T <: Tuple ? tuple : T <: NamedTuple ? ((x...) -> T(tuple(x...))) : T
    # unroll first 32 fields
    Base.@nexprs 32 i -> begin
        if isassigned(values, i)
            x_i = values[i]::fieldtype(T, i)
        else
            x_i = nothing
        end
        if N == i
            return Base.@ncall(i, constructor, x)
        end
    end
    return constructor(x_1, x_2, x_3, x_4, x_5, x_6, x_7, x_8, x_9, x_10, x_11, x_12, x_13, x_14, x_15, x_16,
             x_17, x_18, x_19, x_20, x_21, x_22, x_23, x_24, x_25, x_26, x_27, x_28, x_29, x_30, x_31, x_32, map(i->isassigned(values, i) ? values[i] : nothing, 33:N)...)
end

@static if Base.VERSION < v"1.2"
    function hasfield(::Type{T}, name::Symbol) where T
        return name in fieldnames(T)
    end
end

"""
    StructTypes.constructfrom(T, obj)
    StructTypes.constructfrom!(x::T, obj)

Construct an object of type `T` (`StructTypes.construtfrom`) or populate an existing
object of type `T` (`StructTypes.constructfrom!`) from another object `obj`. Utilizes
and respects StructTypes.jl package properties, querying the `StructType` of `T`
and respecting various serialization/deserialization names, keyword args, etc.

Most typical use-case is construct a custom type `T` from an `obj::AbstractDict`, but
`constructfrom` is fully generic, so the inverse is also supported (turning any custom
struct into an `AbstractDict`). For example, an external service may be providing JSON
data with an evolving schema; as opposed to trying a strict "typed parsing" like
`JSON3.read(json, T)`, it may be preferrable to setup a local custom struct with just
the desired properties and call `StructTypes.constructfrom(T, JSON3.read(json))`. This
would first do a generic parse of the JSON data into a `JSON3.Object`, which is an
`AbstractDict`, which is then used as a "property source" to populate the fields of
our custom type `T`.
"""
function constructfrom end

constructfrom(::Type{T}, obj) where {T} =
    constructfrom(StructType(T), T, obj)

constructfrom!(x::T, obj) where {T} =
    constructfrom!(StructType(T), x, obj)

function constructfrom(::Struct, U::Union, obj)
    try
        return constructfrom(StructType(U.a), U.a, obj)
    catch e
        return constructfrom(StructType(U.b), U.b, obj)
    end
end

constructfrom(::Struct, ::Type{Any}, obj) = obj

constructfrom(::Union{StringType, BoolType, NullType}, ::Type{T}, obj) where {T} =
    construct(T, obj)

constructfrom(::NumberType, ::Type{T}, obj) where {T} =
    construct(T, numbertype(T)(obj))

constructfrom(::ArrayType, ::Type{T}, obj) where {T} =
    constructfrom(ArrayType(), T, Base.IteratorEltype(T) == Base.HasEltype() ? eltype(T) : Any, obj)
constructfrom(::ArrayType, ::Type{Vector}, obj) =
    constructfrom(ArrayType(), Vector, Any, obj)

function constructfrom(::ArrayType, ::Type{T}, ::Type{eT}, obj) where {T, eT}
    if Base.haslength(obj)
        x = Vector{eT}(undef, length(obj))
        for (i, val) in enumerate(obj)
            @inbounds x[i] = constructfrom(eT, val)
        end
    else
        x = eT[]
        for val in obj
            push!(x, constructfrom(eT, val))
        end
    end
    return construct(T, x)
end

constructfrom(::DictType, ::Type{T}, obj) where {T} =
    constructfrom(DictType(), T, Any, Any, obj)
constructfrom(::DictType, ::Type{NamedTuple}, obj) =
    constructfrom(DictType(), NamedTuple, Symbol, Any, obj)
constructfrom(::DictType, ::Type{Dict}, obj) =
    constructfrom(DictType(), Dict, Any, Any, obj)
constructfrom(::DictType, ::Type{AbstractDict}, obj) =
    constructfrom(DictType(), Dict, Any, Any, obj)
constructfrom(::DictType, ::Type{T}, obj) where {T <: AbstractDict} =
    constructfrom(DictType(), T, keytype(T), valtype(T), obj)

@inline constructfrom(::DictType, ::Type{T}, ::Type{K}, ::Type{V}, obj::S) where {T, K, V, S} =
    constructfrom(DictType(), T, K, V, StructType(S), obj)

function constructfrom(::DictType, ::Type{T}, ::Type{K}, ::Type{V}, ::DictType, obj) where {T, K, V}
    d = Dict{K, V}()
    for (k, v) in keyvaluepairs(obj)
        d[constructfrom(K, k)] = constructfrom(V, v)
    end
    return construct(T, d)
end

struct ToDictClosure{K, V}
    x::Dict{K, V}
end

@inline function (f::ToDictClosure{K, V})(i, nm, ::Type{FT}, v::T; kw...) where {K, V, FT, T}
    f.x[constructfrom(K, nm)] = constructfrom(V, v)
    return
end

function constructfrom(::DictType, ::Type{T}, ::Type{K}, ::Type{V}, ::Union{Struct, Mutable}, obj) where {T, K, V}
    d = Dict{K, V}()
    foreachfield(ToDictClosure(d), obj)
    return construct(T, d)
end

constructfrom(::Mutable, ::Type{T}, obj) where {T} =
    constructfrom!(Mutable(), T(), obj)

struct Closure{T}
    v::T
end

@inline (f::Closure{T})(i, nm, TT; kw...) where {T} =
    constructfrom(TT, f.v)

constructfrom!(::Mutable, x::T, obj::S) where {T, S} =
    constructfrom!(Mutable(), x::T, StructType(S), obj)

function constructfrom!(::Mutable, x::T, ::DictType, obj) where {T}
    for (k, v) in keyvaluepairs(obj)
        applyfield!(Closure(v), x, k)
    end
    return x
end

struct ObjClosure{T}
    x::T
end

@inline function (f::ObjClosure{TT})(i, nm, ::Type{FT}, v::T; kw...) where {TT, FT, T}
    applyfield!(Closure(v), f.x, nm)
    return
end

function constructfrom!(::Mutable, x::T, ::Union{Struct, Mutable}, obj) where {T}
    foreachfield(ObjClosure(x), obj)
    return x
end

constructfrom(::Struct, ::Type{T}, obj::S) where {T, S} =
    constructfrom(Struct(), T, StructType(S), obj)

struct DictClosure{T}
    obj::T
end

@inline function (f::DictClosure{T})(i, nm, ::Type{FT}) where {T, FT}
    if haskey(f.obj, nm)
        return constructfrom(FT, getindex(f.obj, nm))
    end
    return nothing
end

constructfrom(::Struct, ::Type{T}, ::DictType, obj) where {T} =
    construct(DictClosure(obj), T)

struct StructClosure{T}
    obj::T
end

@inline function (f::StructClosure{T})(i, nm, ::Type{FT}) where {T, FT}
    if hasfield(T, nm)
        return constructfrom(FT, getfield(f.obj, nm))
    end
    return nothing
end

constructfrom(::Struct, ::Type{T}, ::Union{Struct, Mutable}, obj) where {T} =
    construct(StructClosure(obj), T)

struct TupleClosure{T}
    obj::T
end

@inline (f::TupleClosure{T})(i, nm, ::Type{FT}) where {T, FT} =
    constructfrom(FT, f.obj[i])

constructfrom(::ArrayType, ::Type{T}, obj) where {T <: Tuple} =
    construct(TupleClosure(obj), T)

function constructfrom(::AbstractType, ::Type{T}, obj::S) where {T, S}
    types = subtypes(T)
    if length(types) == 1
        return constructfrom(types[1], obj)
    end
    skey = subtypekey(T)
    TT = types[Symbol(StructType(S) == DictType() ? obj[skey] : getfield(obj, skey))]
    return constructfrom(TT, obj)
end

StructType(::Type{T}) where {T <: NamedTuple} = UnorderedStruct()

constructfrom(t::Type{NamedTuple}, obj::NamedTuple) =
    constructfrom(StructType(t), t, StructType(typeof(obj)), obj)

constructfrom(::Union{Struct, UnorderedStruct}, t::Type{T}, ::Union{Struct, UnorderedStruct}, obj::NamedTuple) where {T <: NamedTuple} =
     construct_nt(t, obj)

construct_nt(::Type{NamedTuple}, obj) = obj

function construct_nt(t::Type{T}, obj) where {T <: NamedTuple}
    keys = fieldnames(t)
    keyset = Set(keys)
    result = Dict()
    for (key, value) in pairs(obj)
        field = construct(Symbol, key)
        if field in keyset
            result[field] = constructfrom(fieldtype(t, field), value)
            delete!(keyset, field)
        else
            msg = "Extraneous field $field in $obj, not in type $t"
            try
                throw(ArgumentError(msg))
            catch err
                @debug msg exception=err,catch_backtrace()
            end
        end
    end
    for key in keyset
        type = fieldtype(t, key)
        if nothing isa type
            result[key] = nothing
        elseif missing isa type
            result[key] = missing
        else
            continue
        end
        delete!(keyset, key)
    end
    length(keyset) > 0 && throw(ArgumentError("Missing required fields $(join(keyset, ",")) from $obj in type $t"))
    t(result[key] for key in keys)
end

include("macros.jl")

end # module


text:
using Base.Test
using Base.Test: Fail, Error, Broken
using LyapunovExponents
using LyapunovExponents: objname, dimension
using LyapunovExponents.TestTools: test_same_dynamics,
    test_tangent_dynamics_against_autodiff,
    @test_isapprox_elemwise, @_test_isapprox_elemwise_result

@testset "@test_isapprox_elemwise" begin
    @test_isapprox_elemwise([0], [0])
    @test_isapprox_elemwise([1], [1.1], rtol=0.2)

    io = IOBuffer()
    result = @_test_isapprox_elemwise_result([0], [1], io=io)
    msg = readstring(seek(io, 0))
    @test contains(msg, "Not pairwise isapprox")
    @test result isa Fail

    # Marked as skip
    io = IOBuffer()
    result = @_test_isapprox_elemwise_result([0], [1], io=io, skip=true)
    @test result isa Broken

    # Marked as broken, but pass
    io = IOBuffer()
    result = @_test_isapprox_elemwise_result([0], [0], io=io, broken=true)
    @test result isa Error
    @test result.test_type == :test_unbroken

    # Marked as broken, and broken
    io = IOBuffer()
    result = @_test_isapprox_elemwise_result([0], [1], io=io, broken=true)
    @test result isa Broken
end

@testset "test_same_dynamics: $(objname(f))" for f in [
        LyapunovExponents.henon_map,
        LyapunovExponents.van_der_pol,
        ]
    p1 = f().prob.phase_prob
    p2 = f().prob.phase_prob
    num_u = 5  # number of random states at which the systems are tested
    test_same_dynamics(p1, p2, num_u)
    test_same_dynamics(p1, p2, num_u; evolve=true)
    test_same_dynamics(p1, p2, [p1.u0, p1.u0 + 0.1])
end

@testset "test_tangent_dynamics_against_autodiff: dim_lyap=$dim_lyap" for
        dim_lyap in 1:3
    prob = LEProblem(LyapunovExponents.lorenz_63().example; dim_lyap=dim_lyap)
    @test size(prob.tangent_prob.u0) == (3, 1 + dim_lyap)
    num_u = 5
    test_tangent_dynamics_against_autodiff(prob, num_u)
end

ext: jl


text:
function diag_davidson( pw::PWGrid, Vpot, X0;
                        tol=1e-5, tol_avg=1e-7, NiterMax=200, verbose=false )

    # get size info
    Ncols = size(X0)[2]
    Ngwx  = size(X0)[1]

    if Ncols <= 0
        @printf("diag_davidson requires at least one initial wave function!\n");
        return
    end
    # orthonormalize the initial wave functions.
    X = ortho_gram_schmidt(X0)  # normalize (again)?

    evals    = zeros(Float64, Ncols)
    R        = zeros(ComplexF64, Ngwx, Ncols)
    Hred     = zeros(ComplexF64, 2*Ncols, 2*Ncols)
    Sred     = zeros(ComplexF64, 2*Ncols, 2*Ncols)
    res      = zeros(Float64, Ncols)
    res_norm = zeros(Float64, Ncols)

    HX = op_H( pw, Vpot, X )

    # Initial eigenvalues
    for ic = 1:Ncols
        for ig = 1:Ngwx
            evals[ic] = evals[ic] + real( conj(X[ig,ic]) * HX[ig,ic] )
        end
    end

    # Calculate residuals
    for ic = 1:Ncols
        for ig = 1:Ngwx
            R[ig,ic] = evals[ic]*X[ig,ic] - HX[ig,ic]
        end
    end

    for ic = 1:Ncols
        res[ic] = 0.0
        for ig = 1:Ngwx
            res[ic] = res[ic] + real( R[ig,ic] * conj(R[ig,ic]) )
        end
        res[ic] = sqrt( res[ic] )
    end

    EPS = eps()

    set1 = 1:Ncols
    set2 = Ncols+1:2*Ncols

    sum_evals = sum(evals)
    sum_evals_old = sum_evals

    for iter = 1:NiterMax

        res_norm[:] .= 1.0

        for ic = 1:Ncols
            if EPS < res[ic]
                res_norm[ic] = 1.0/res[ic]
            end
        end

        for ic = 1:Ncols
            for ig = 1:Ngwx
                R[ig,ic] = res_norm[ic] * R[ig,ic]
            end
        end

        R = Kprec(pw, R)

        HR = op_H( pw, Vpot, R )

        # FIXME: Pull this outside the loop?
        if iter == 1
            Hred[set1,set1] = X' * HX
        else
            Hred[1:Ncols,1:Ncols] .= 0.0 + im*0.0
            for ic = 1:Ncols
                Hred[ic,ic] = evals[ic] + im*0.0 # use diagm ?
            end
            #Hred[set1,set1] = diagm(evals[:])
        end

        Hred[set1,set2] = X' * HR
        Hred[set2,set2] = R' * HR
        Hred[set2,set1] = Hred[set1,set2]'

        Sred[set1,set1] = Matrix(Diagonal(ones(ComplexF64,Ncols)))
        Sred[set1,set2] = X' * R
        Sred[set2,set2] = R' * R
        Sred[set2,set1] = Sred[set1,set2]'

        Hred = (Hred + Hred')/2.0
        Sred = (Sred + Sred')/2.0

        λ_red, X_red = eigen( Hermitian(Hred), Hermitian(Sred) )

        evals = λ_red[1:Ncols]

        #for ic = 1:2*Ncols
        #  print(ic); print(" "); println(λ_red[ic])
        #end

        X  = X  * X_red[set1,set1] + R  * X_red[set2,set1]
        HX = HX * X_red[set1,set1] + HR * X_red[set2,set1]

        # Calculate residuals
        for ic = 1:Ncols
            for ig = 1:Ngwx
                R[ig,ic] = evals[ic]*X[ig,ic] - HX[ig,ic]
            end
        end

        for ic = 1:Ncols
            res[ic] = 0.0
            for ig = 1:Ngwx
                res[ic] = res[ic] + real( R[ig,ic] * conj(R[ig,ic]) )
            end
            res[ic] = sqrt( res[ic] )
            @printf("%4d %18.10f %18.10f\n", ic, evals[ic], res[ic] )
        end

        sum_evals = sum(evals)
        diffSum = abs(sum_evals - sum_evals_old)
        @printf("iter %d tol_avg = %18.10e\n", iter, abs(diffSum))
        if diffSum < tol_avg
            if verbose
                @printf("Davidson convergence: tol_avg\n")
            end
            break
        end
        sum_evals_old = sum_evals
    end

    return evals, X

end


text:
# https://www.hackerrank.com/challenges/30-loops/problem

n = parse(Int, readline())

function show_loop(v::Int)
    for i in 1:10
        r = v * i
        println("$v x $i = $r")
    end
end

show_loop(n)

max_stars_repo_path: Practice/Tutorials/30 Days of Code/Day 05 Loops.jl

max_stars_repo_name: mtaufanr/hackerrank-code-julia

max_issues_repo_name: mtaufanr/hackerrank-code-julia

max_forks_repo_name: mtaufanr/hackerrank-code-julia


text:
struct ImaginaryTimeGF{T, scalar} <: AbstractTimeGF{T, scalar}
  grid::ImaginaryTimeGrid
  mat::PeriodicStorage{T,scalar}
  ξ::GFSignEnum
end

norbitals(G::ImaginaryTimeGF) = G.mat.norb

function ImaginaryTimeGF(::Type{T}, grid::ImaginaryTimeGrid, norb=1, ξ::GFSignEnum=fermionic, scalar=false) where T <: Number
  ntau = grid.ntau
  mat = PeriodicStorage(T, ntau, norb, scalar)
  ImaginaryTimeGF(grid, mat, ξ)
end
ImaginaryTimeGF(grid::ImaginaryTimeGrid, norb=1, ξ::GFSignEnum=fermionic, scalar=false) = ImaginaryTimeGF(ComplexF64, grid, norb, ξ, scalar)

@inline function Base.getindex(G::ImaginaryTimeGF, k, l, t1::TimeGridPoint, t2::TimeGridPoint, greater=true)
  greater = t1 == t2 ? greater : heaviside(t1.bpoint, t2.bpoint)

  i = t1.ridx
  j = t2.ridx
  ξ = Int(G.ξ)
  ntau = G.grid.ntau

  greater ? G.mat[k,l,i,j,greater] : ξ * G.mat[k,l,i,j,greater]
end

function Base.setindex!(G::ImaginaryTimeGF, v, k, l, t1::TimeGridPoint, t2::TimeGridPoint)
  greater = heaviside(t1.bpoint, t2.bpoint)

  i = t1.ridx
  j = t2.ridx
  ξ = Int(G.ξ)

  if greater
    G.mat[k,l,i,j] = v
  else
    G.mat[k,l,i,j] = ξ * v
  end
end

function Base.similar(G::T, ξ::GFSignEnum) where T <: ImaginaryTimeGF
  T(G.grid, similar(G.mat), ξ)
end

function Base.zero(G::T, ξ::GFSignEnum) where T <: ImaginaryTimeGF
  T(G.grid, zero(G.mat), ξ)
end

function Base.similar(G::T) where T <: ImaginaryTimeGF
  similar(G, G.ξ)
end

function Base.zero(G::T) where T <: ImaginaryTimeGF
  zero(G, G.ξ)
end

function TimeDomain(G::ImaginaryTimeGF)
  grid = G.grid
  tau = grid[imaginary_branch]

  ntau = G.grid.ntau

  points = Tuple{TimeGridPoint, TimeGridPoint}[]

  for i in 1:ntau
    push!(points, (tau[i], tau[1])) # matsubara
  end

  return TimeDomain(points)
end

function ImaginaryTimeGF(f::Function, ::Type{T}, grid::ImaginaryTimeGrid, norb=1, ξ::GFSignEnum=fermionic, scalar=false) where T <: Number
  G = ImaginaryTimeGF(T, grid, norb, ξ, scalar)
  D = TimeDomain(G)

  for (t1, t2) in D.points
    G[t1, t2] = f(t1, t2)
  end

  return G
end

ImaginaryTimeGF(f::Function, grid::ImaginaryTimeGrid, norb=1, ξ::GFSignEnum=fermionic, scalar=false) = ImaginaryTimeGF(f, ComplexF64, grid, norb, ξ, scalar)

function ImaginaryTimeGF(dos::AbstractDOS, grid::ImaginaryTimeGrid)
  β = grid.contour.β
  ImaginaryTimeGF(grid, 1, fermionic, true) do t1, t2
    Keldysh.dos2gf(dos, β, t1.bpoint, t2.bpoint)
  end
end


text:
module FoldsTapir

export TapirEx, StaticTapirEx

using Base.Experimental: Tapir

import Transducers
const FoldsBase = Transducers

import ConstructionBase
using Accessors: @set
using Preferences
using SplittablesBase: amount
using Transducers:
    @return_if_reduced,
    Reduced,
    Transducer,
    Transducers,
    combine,
    complete,
    opcompose,
    start,
    transduce,
    unreduced

# TODO: Don't import internals from Transducers:
using Transducers:
    CancellableDACContext,
    DefaultInitOf,
    EmptyResultError,
    NoopDACContext,
    Reducible,
    SizedReducible,
    _halve,
    _might_return_reduced,
    _reduce_basecase,
    _reducingfunction,
    cancel!,
    combine_right_reduced,
    foldl_nocomplete,
    issmall,
    maybe_usesimd,
    retransform,
    should_abort,
    splitcontext

const USE_TAPIR_OUTPUT =
    @load_preference("use_tapir_output", isdefined(Tapir, Symbol("@output")))

set_use_tapir_output(use::Bool) = @set_preferences!("use_tapir_output" => use)

include("utils.jl")
include("tapir.jl")
include("static.jl")
include("misc.jl")

_static_nthreads_(::Any) = Val(1)
static_nthreads() = _static_nthreads_(nothing)

isprecompiling() = ccall(:jl_generating_output, Cint, ()) == 1

function __init__()
    isprecompiling() && return
    @eval _static_nthreads_(::Nothing) = Val($(Threads.nthreads()))
end

end


text:
# set correct path
try
  cd("DWPSDE model")
catch
  warn("Already in the DWP-SDE folder.")
end

# load case models
cd("..")
include(pwd()*"\\select case\\selectcase.jl")
cd("DWPSDE model")

# load files and functions
include("set_up.jl")

using PyPlot
using StatsBase

## Old data

Z = load_data()

Z = Z[:]

summarystats(Z)

PyPlot.figure()
PyPlot.plot(Z)

PyPlot.figure()
PyPlot.plt[:hist](Z,50)

## New data

file = open("new_data_set.txt")
data = readlines(file)
close(file)

Z_data = zeros(length(data)-1)

idx = 2:length(data)

for i in idx
    try
        Z_data[i-1] = readdlm(IOBuffer(data[i]), Float64)[2]
    catch
        Z_data[i-1] = parse(data[i][end-1-4:end-1])
    end
end

summarystats(Z_data)

PyPlot.figure()
PyPlot.plot(Z_data)

PyPlot.figure()
PyPlot.plt[:hist](Z_data,50)

# thinned data
thinning = 100
idx_thinned = 1:thinning:length(Z_data)
Z_data = Z_data[idx_thinned]

# linear transformation of data to obtain a scaling where it is easier to
# construct the dwp model
Z_data_trans1 = 50*Z_data

summarystats(Z_data_trans1)

PyPlot.figure()
PyPlot.plot(Z_data_trans1)

PyPlot.figure()
PyPlot.plt[:hist](Z_data_trans1,50)

# linear transformation of data to obtain a scaling where it is easier to
# construct the dwp model
Z_data_trans1 = 50*Z_data
Z_data_trans1 = (Z_data-mean(Z_data_trans1) ) / (maximum(Z_data_trans1) - minimum(Z_data_trans1))

summarystats(Z_data_trans1)

PyPlot.figure()
PyPlot.plot(Z_data_trans1)

PyPlot.figure()
PyPlot.plt[:hist](Z_data_trans1,50)

max_stars_repo_name: SamuelWiqvist/adamcmcpaper

max_issues_repo_name: SamuelWiqvist/adamcmcpaper

max_forks_repo_name: SamuelWiqvist/adamcmcpaper


text:
const src_options = Dict(
    :degree_max => filter_vertices_degree_max,
    :degree_min => filter_vertices_degree_min,
    :degree => filter_vertices_degree,
    :size_biggest_clique_max => filter_vertices_size_biggest_clique_max,
    :size_biggest_clique_min => filter_vertices_size_biggest_clique_min,
    :size_biggest_clique => filter_vertices_size_biggest_clique,
    :size_smallest_clique_max => filter_vertices_size_smallest_clique_max,
    :size_smallest_clique_min => filter_vertices_size_smallest_clique_min,
    :size_smallest_clique => filter_vertices_size_smallest_clique,
)

const dst_options = Dict(
    :degree_max => filter_vertices_degree_max,
    :degree_min => filter_vertices_degree_min,
    :degree => filter_vertices_degree,
    :size_biggest_clique_max => filter_vertices_size_biggest_clique_max,
    :size_biggest_clique_min => filter_vertices_size_biggest_clique_min,
    :size_biggest_clique => filter_vertices_size_biggest_clique,
    :size_smallest_clique_max => filter_vertices_size_smallest_clique_max,
    :size_smallest_clique_min => filter_vertices_size_smallest_clique_min,
    :size_smallest_clique => filter_vertices_size_smallest_clique,
    :distance_max => filter_vertices_distance_max,
    :distance_min => filter_vertices_distance_min,
    :distance => filter_vertices_distance,
    :same_clique => filter_vertices_same_clique,
    :no_same_clique => filter_vertices_no_same_clique,
    :no_edge => filter_vertices_no_edge,
    :has_edge => filter_vertices_has_edge
)

function fill_options_arguments(options, graph)
    # TODO: Make this less uglier
    filled_options = Dict(key => convert(Dict{Symbol,Union{Int64, Base.OneTo{Int64}, AbstractGraph, Array{Int}, Vector{Vector{Int}}, Array{Any}}}, val) for (key, val) in options)
    for option in collect(keys(filled_options))
        argnames = getkwargs(Generate.dst_options[option])
        cliques_arg = :cliques in argnames
        choleskygraph_arg = :choleskygraph in argnames
        if cliques_arg || choleskygraph_arg
            if !cliques_arg
                filled_options[option][:choleskygraph] = SimpleGraph(get_cholesky_graph(graph)[1])
            else
                cliques, cliquetree, nb_added_edges, choleskygraph = decompose(graph)
                filled_options[option][:cliques] = cliques
                choleskygraph_arg && (filled_options[option][:choleskygraph] = SimpleGraph(choleskygraph))
            end
        end
        if :graph in argnames
            filled_options[option][:graph] = graph
        end
    end
    return filled_options
end

"""
    check_options_src(options_src::AbstractDict{Symbol})

Check if the sources options are valids.
"""
function check_options_src(options_src::AbstractDict{Symbol})
    options_names = keys(options_src)
    if issubset((:degree_max, :degree_min), options_names) && options_src[:degree_max]["degree"] < options_src[:degree_min]["degree"]
        error(":degree_max cannot be strictly inferior to :degree_min")
    end
    if issubset((:size_biggest_clique_max, :size_biggest_clique_min), options_names) &&
       options_src[:size_biggest_clique_max]["size"] < options_src[:size_biggest_clique_min]["size"]
        error(":size_biggest_clique_max cannot be strictly inferior to :size_biggest_clique_min")
    end
    if issubset((:size_smallest_clique_max, :size_smallest_clique_min), options_names) &&
       options_src[:size_smallest_clique_max]["size"] < options_src[:size_smallest_clique_min]["size"]
        error(":size_smallest_clique_max cannot be strictly inferior to :size_smallest_clique_min")
    end
end

"""
    check_options_dst(options_dst::AbstractDict{Symbol})

Check if the destinations options are valids.
"""
function check_options_dst(options_dst::AbstractDict{Symbol})
    check_options_src(options_dst)
    options_names = keys(options_dst)
    if issubset((:distance_max, :distance_min), options_names) && options_dst[:distance_max]["dist"] < options_dst[:distance_min]["dist"]
        error(":distance_max cannot be strictly inferior to :distance_min")
    end
    if issubset((:same_clique, :no_same_clique), options_names)
        error(":same_clique and :no_same_clique cannot be set together")
    end
    if issubset((:has_edge, :no_edge), options_names)
        error(":has_edge and :no_edge cannot be set together")
    end
end

"""
    get_valid_srcs(vertices::AbstractVector{Int}, options::AbstractDict{Symbol})

Return the vertices in `vertices` validating the options passed in parameters.
"""
function get_valid_srcs(vertices::AbstractVector{Int}, options::AbstractDict{Symbol})
    for (option, args) in options
        args[:vertices] = vertices
        vertices = src_options[option](;args...)
        if isempty(vertices)
            return vertices
        end
    end
    return vertices
end

"""
    get_valid_dsts(vertices::AbstractVector{Int}, options::AbstractDict{Symbol}, src::Int)

Return the vertices in `vertives` validating the options passed in parameters.
"""
function get_valid_dsts(vertices::AbstractVector{Int}, options::AbstractDict{Symbol}, src::Int)
    for (option, args) in options
        args[:vertices] = vertices
        kwargs = getkwargs(dst_options[option])
        if :src in kwargs
            args[:src] = src
        end
        vertices = dst_options[option](;args...)
        if !isempty(vertices)
            return vertices
        end
    end
    return vertices
end

"""
    add_edge_by!(graph::AbstractGraph,
                 options_src::AbstractDict{Symbol},
                 options_dst::AbstractDict{Symbol},
                 )

Add a single edge to `graph` validating the options passed in parameters.
If no edge corresponds to the options then the function returns `nothing`.
"""
function add_edge_by!(graph::AbstractGraph,
                      options_src::AbstractDict{Symbol},
                      options_dst::AbstractDict{Symbol})::Vector{Int}
    src = nothing
    dst = nothing
    srcs = get_valid_srcs(vertices(graph), options_src)
    isempty(srcs) && return nothing
    while !isempty(srcs)
        src = srcs[rand(1:end)]
        dsts = get_valid_dsts(setdiff(vertices(graph), src), options_dst, src)
        if isempty(dsts)
            srcs = setdiff(srcs, src)
            continue
        else
            dst = dsts[rand(1:end)]
            break
        end
    end
    if dst == nothing
        return nothing
    end
    add_edge!(graph, src, dst)
    return [src, dst]
end

"""
    add_edges_by!(graph::AbstractGraph,
                  options_src::AbstractDict{Symbol},
                  options_dst::AbstractDict{Symbol},
                  nb_edges)

Add multiple edges to `graph` validating the options passed in parameters.
"""
function add_edges_by!(graph::AbstractGraph,
                       options_src::AbstractDict{Symbol},
                       options_dst::AbstractDict{Symbol},
                       nb_edges)::Vector{Vector{Int}}
    added_edges = []
    for i in 1:nb_edges
        edge = add_edge_by!(graph, options_src, options_dst)
        if  edge == nothing
            @warn "Could not add $(nb_edges) with the given options, added $(length(added_edges)) instead"
            break
        else
            push!(added_edges, edge)
        end
    end
    return added_edges
end

"""
    add_edges_by!(graphs::AbstractVector{AbstractGraph},
                  options_src::AbstractDict{Symbol},
                  options_dst::AbstractDict{Symbol},
                  nb_edges)

Add multiple edges to all the graph in `graphs` validating the options passed in parameters.
"""
function add_edges_by!(graphs::AbstractVector{AbstractGraph},
                       options_src::AbstractDict{Symbol},
                       options_dst::AbstractDict{Symbol},
                       nb_edges)
    added_edges = []
    for graph in graphs
        push!(added_edges, add_edges_by!(graph, options_src, options_dst, nb_edges))
    end
    return added_edges
end

function add_edges!(graph::AbstractGraph, options_src, options_dst, nb_added_edges)
    nb_added_edges == 0 && return 0
    options_src_copy = fill_options_arguments(options_src, graph)
    options_dst_copy = fill_options_arguments(options_dst, graph)
    added_edges = add_edges_by!(graph, options_src_copy, options_dst_copy, nb_added_edges)
    return length(added_edges)
end

max_stars_repo_name: charlyalizadeh/OPFdecgen

max_issues_repo_name: charlyalizadeh/OPFdecgen

max_forks_repo_name: charlyalizadeh/OPFdecgen


text:
# MIT License
#
# Copyright (c) 2020 Joshua E Gentry

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

mutable struct ChunkState
    chaining::CHAINING
    chunks::UInt64
    block::Buffer{MVector{64, UInt8}}
    blocks::Int
    flags::UInt32
end

function ChunkState(
            key::CHAINING,
            chunks::UInt64,
            flags::UInt32
        )::ChunkState
    return ChunkState(key, chunks, Buffer(), 0, flags)
end

function clear(
            cs::ChunkState,
            key::CHAINING,
            chunks::UInt64,
            flags::UInt32
        )::Nothing
    cs.chaining = key
    cs.chunks   = chunks
    cs.blocks   = 0
    cs.flags    = flags
    clear(cs.block)
    nothing
end

function Base.length(
            cs::ChunkState
        )::UInt
    return BLOCK_LEN * cs.blocks + length(cs.block)
end

function flags(
            cs::ChunkState
        )::UInt32
    return cs.blocks == 0 ? (CHUNK_START | cs.flags) : (cs.flags)
end

function fill(
            cs::ChunkState,
            input::Buffer
        )::Nothing
    bytes = min(remaining(cs.block), remaining(input))
    @inbounds copyto!(
        view(cs.block, bytes), 1,
        view(input, bytes), 1,
        bytes
    )
    nothing
end

function fill_and_complete(
            cs::ChunkState,
            input::Buffer
        )::Output

    if remaining(cs.block) > 0
        offset = fill(cs, input)
    end

    return Output(
        cs.chaining,
        as_block(cs.block),
        UInt32(BLOCK_LEN),
        cs.chunks,
        flags(cs) | CHUNK_END
    )
end

function fill_and_compress(
            cs::ChunkState,
            input::Buffer
        )::Nothing
    if remaining(cs.block) > 0
        offset = fill(cs, input)
    end

    cs.chaining = compress(
        cs.chaining,
        as_block(cs.block),
        UInt32(BLOCK_LEN),
        cs.chunks,
        flags(cs)
    )
    cs.blocks += 1
    clear(cs.block)
    nothing
end

function compress(
            cs::ChunkState,
            input::Buffer
        )::Nothing
    cs.chaining = compress(
        cs.chaining,
        block_view(input),
        UInt32(BLOCK_LEN),
        cs.chunks,
        flags(cs)
    )
    cs.blocks += 1
    nothing
end

function complete(
            cs::ChunkState,
            input::Buffer
        )::Output
    return Output(
        cs.chaining,
        block_view(input),
        UInt32(BLOCK_LEN),
        cs.chunks,
        flags(cs) | CHUNK_END
    )
end

function complete(
            cs::ChunkState
        )::Output
    return Output(
        cs.chaining,
        as_block(cs.block),
        length(cs.block),
        cs.chunks,
        flags(cs) | CHUNK_END
    )
end

function update(
            cs::ChunkState,
            input::Buffer
        )::Union{Output, Nothing}

    # Not enough to process, copy the bytes to our buffer and return.
    if remaining(input) <= remaining(cs.block)
        fill(cs, input)
        return nothing
    end

    # If we have data in our buffer and we're on the last block complete it.
    if length(cs.block) > 0 && cs.blocks == 15
        return fill_and_complete(cs, input)
    end

    # If we have data in our buffer, compress that.
    if length(cs.block) > 0
        fill_and_compress(cs, input)
    end

    # Process while we have MORE than a full block but NOT a full chunk.
    while cs.blocks < 15 && remaining(input) > BLOCK_LEN
        compress(cs, input)
    end

    # If we have MORE data than a full block, then we can complete the current chunk.
    if remaining(input) > BLOCK_LEN
        return complete(cs, input)
    end

    # If there is still data remaining save it.
    if remaining(input) > 0
        fill(cs, input)
    end

    return nothing
end


text:
using BGEN
using Test, Printf
const example_8bits = BGEN.datadir("example.8bits.bgen")
const example_10bits = BGEN.datadir("example.10bits.bgen")
const example_16bits = BGEN.datadir("example.16bits.bgen")
const example_sample = BGEN.datadir("example.sample")
include("utils.jl")
const gen_data = load_gen_data()
const vcf_data = load_vcf_data()
const haps_data = load_haps_data()
include("test_basics.jl")
include("test_getters.jl")
include("test_select_region.jl")
include("test_index.jl")
include("test_load_example_files.jl")
include("test_minor_allele_dosage.jl")
include("test_utils.jl")


text:
# __precompile__()

module StudentHousing

using StatsBase, Distributions,
    JuMP, Gurobi, CPLEX, SDDP, SDDiP

export
    StudentHousingData,
    onestagemodel, multistagemodel, solve_house_generation, solve_ia_generation,
    solve_pattern_generation, solve_ia_p_generation,
    house_allowedby, house_fits_pattern, beds_needed, beds_avail

include("housing/binary_hacks.jl")
include("housing/data.jl")
include("housing/model.jl")
include("GAP/gap_h.jl")
include("GAP/gap_p.jl")
include("investandassign/investandassign_h.jl")
include("investandassign/investandassign_p.jl")

end # module


text:
import Adapt: adapt, adapt_
using Test

# trivial test

struct Matrix
    mat::AbstractArray
end

adapt_(::Type{<:Matrix}, xs::AbstractArray) =
  Matrix(xs)

testmat = [12;34;56;78]

testresult = Matrix(testmat)

@test adapt(Matrix, testmat) == testresult


text:
struct PolyaGammaGammaSumSamplerOld <:
       Distributions.Sampleable{Distributions.Univariate,Distributions.Continuous}
    h::Float64
    z::Float64
end

const bvec = map(x -> (x - 0.5)^2 * __PI^2 * 4, 1:_TERMS)

## draw sum of gammas
function Distributions.rand(rng::Distributions.AbstractRNG, s::PolyaGammaGammaSumSamplerOld)
    g = Distributions.rand(rng, Distributions.Gamma(s.h), _TERMS)
    # x = @distributed (+) for k in bvec
    # 2 * Distributions.rand(rng, g) / (s.z^2 + k)
    # end
    # return x
    # b =  mapreduce(k -> 1 / (k + s.z^2), +, bvec2)
    #return Distributions.rand(rng,Gamma(2 * s.h * b))#
    # return (2 * sum( map(bvec, g) do k, g_i
    #          g_i / (k + s.z^2)
    #         end )
    return 2 * mapreduce((k, g_i) -> Random.rand(rng, g) / (k + s.z^2), +, bvec, g)
end


text:
version = v"1.2.3"
api_version = v"1.1.2"

# Collection of sources required to build this package
#
# The level zero repository contains both the API headers and a loader,
# which are versioned independently.
sources = [
    GitSource("https://github.com/oneapi-src/level-zero.git",
              "0d30b1fa712253c68bfdfa3863d380df4301b8a4"),
]

max_stars_repo_name: waralex/Yggdrasil

max_issues_repo_name: waralex/Yggdrasil

max_forks_repo_name: waralex/Yggdrasil


text:
module TestAtom
using SimpleMolecule
using Base.Test

include("run_test_comparison.jl")
include("run_test_obtain.jl")
include("run_test_constructor.jl")
include("run_test_clone.jl")

function run_tests()
    run_test_comparison()
    run_test_obtain()
    run_test_constructor()
    run_test_clone()
end

end


text:
function THD_TimeSeries(TimeDataIn::Vector;MaxHarmonics=10, PlotOn=true)
    #TimeDataIn is the data in question
    #This method has no defined sampling rate, so it is assumed to be normalized by Frequency

    FFTFreqs, FT_1s_Mag = FFT_1s(TimeDataIn) #take FT of time domain

    return THD_Core(abs.(FT_1s_Mag),FFTFreqs;PlotOn=PlotOn,Normalized=true,MaxHarmonics=MaxHarmonics)
end

function THD_TimeSeries(TimeDataIn::Vector,fs;MaxHarmonics=10, PlotOn=true,Normalized=false)

    #TimeDataIn is the data in question
    #fs is the sampling rate in Hz

    FFTFreqs, FT_1s_Mag = FFT_1s(TimeDataIn,fs=fs) #take FT of time domain
    return THD_Core(abs.(FT_1s_Mag),FFTFreqs;PlotOn=PlotOn,Normalized=Normalized,MaxHarmonics=MaxHarmonics)
end

function THD_Core(FT_1s_Mag,FFTFreqs;PlotOn=true, Normalized=true,MaxHarmonics=10)

    FundamentalMag, FundamentalIndex = findmax(FT_1s_Mag)

    NumHarmonics = Int(floor(length(FT_1s_Mag)/FundamentalIndex))
    HarmonicArray = [FundamentalIndex FundamentalMag]

    for n in 1:NumHarmonics-1
        Harm_index=FundamentalIndex .+ n*(FundamentalIndex-1)

        HarmonicArray = [HarmonicArray;[Harm_index  FT_1s_Mag[Harm_index]]]
        if n==MaxHarmonics
            break
        end
    end

    THD = sqrt(sum(HarmonicArray[2:end,2].^2))/HarmonicArray[1,2]
    THD_dB = 20*log10(THD)
    if PlotOn
        semilogy(FFTFreqs,FT_1s_Mag)
        semilogy(FFTFreqs[Int.(HarmonicArray[:,1])],HarmonicArray[:,2],"ro")
        title("THD = $THD_dB dB")
        if Normalized
            xlabel("Normalized Frequency (cycles/sample)")
        else
            xlabel("Frequency (Hz)")
        end
        ylabel("Amplitude")
    end
    return THD
end


text:
module DiscreteElasticRods

using LinearAlgebra
using SparseArrays
export DER

const DER = DiscreteElasticRods
const PTRANSPORT_TOL = 0.06

include("rod_types.jl")
include("rod_nonmutating.jl")
include("rod_mutating.jl")
include("rod_specification.jl")
include("rod_gradients.jl")
include("rod_sparsity.jl")
include("rod_plot.jl")

end


text:
module InterpolationWrapper

using JavaCall

const AkimaSplineInterpolator = @jimport org.hipparchus.analysis.interpolation.AkimaSplineInterpolator
const BicubicFunction = @jimport org.hipparchus.analysis.interpolation.BicubicFunction
const BicubicInterpolatingFunction = @jimport org.hipparchus.analysis.interpolation.BicubicInterpolatingFunction
const BicubicInterpolator = @jimport org.hipparchus.analysis.interpolation.BicubicInterpolator
const BilinearInterpolatingFunction = @jimport org.hipparchus.analysis.interpolation.BilinearInterpolatingFunction
const BilinearInterpolator = @jimport org.hipparchus.analysis.interpolation.BilinearInterpolator
const BivariateFunction = @jimport org.hipparchus.analysis.BivariateFunction
const BivariateGridInterpolator = @jimport org.hipparchus.analysis.interpolation.BivariateGridInterpolator
const Class = @jimport java.lang.Class
const Derivative = @jimport org.hipparchus.analysis.differentiation.Derivative
const DerivativeStructure = @jimport org.hipparchus.analysis.differentiation.DerivativeStructure
const DividedDifferenceInterpolator = @jimport org.hipparchus.analysis.interpolation.DividedDifferenceInterpolator
const Field = @jimport org.hipparchus.Field
const FieldBivariateFunction = @jimport org.hipparchus.analysis.FieldBivariateFunction
const FieldElement = @jimport org.hipparchus.FieldElement
const FieldHermiteInterpolator = @jimport org.hipparchus.analysis.interpolation.FieldHermiteInterpolator
const FieldPolynomialSplineFunction = @jimport org.hipparchus.analysis.polynomials.FieldPolynomialSplineFunction
const FieldUnivariateInterpolator = @jimport org.hipparchus.analysis.interpolation.FieldUnivariateInterpolator
const GridAxis = @jimport org.hipparchus.analysis.interpolation.GridAxis
const HermiteInterpolator = @jimport org.hipparchus.analysis.interpolation.HermiteInterpolator
const InterpolatingMicrosphere = @jimport org.hipparchus.analysis.interpolation.InterpolatingMicrosphere
const InterpolatingMicrosphere2D = @jimport org.hipparchus.analysis.interpolation.InterpolatingMicrosphere2D
const JString = @jimport java.lang.String
const LinearInterpolator = @jimport org.hipparchus.analysis.interpolation.LinearInterpolator
const LoessInterpolator = @jimport org.hipparchus.analysis.interpolation.LoessInterpolator
const MicrosphereProjectionInterpolator = @jimport org.hipparchus.analysis.interpolation.MicrosphereProjectionInterpolator
const MultivariateFunction = @jimport org.hipparchus.analysis.MultivariateFunction
const MultivariateInterpolator = @jimport org.hipparchus.analysis.interpolation.MultivariateInterpolator
const NevilleInterpolator = @jimport org.hipparchus.analysis.interpolation.NevilleInterpolator
const Object = @jimport java.lang.Object
const PiecewiseBicubicSplineInterpolatingFunction = @jimport org.hipparchus.analysis.interpolation.PiecewiseBicubicSplineInterpolatingFunction
const PiecewiseBicubicSplineInterpolator = @jimport org.hipparchus.analysis.interpolation.PiecewiseBicubicSplineInterpolator
const PolynomialFunction = @jimport org.hipparchus.analysis.polynomials.PolynomialFunction
const PolynomialFunctionLagrangeForm = @jimport org.hipparchus.analysis.polynomials.PolynomialFunctionLagrangeForm
const PolynomialFunctionNewtonForm = @jimport org.hipparchus.analysis.polynomials.PolynomialFunctionNewtonForm
const PolynomialSplineFunction = @jimport org.hipparchus.analysis.polynomials.PolynomialSplineFunction
const RealFieldBivariateFunction = @jimport org.hipparchus.analysis.RealFieldBivariateFunction
const RealFieldElement = @jimport org.hipparchus.RealFieldElement
const RealFieldUnivariateFunction = @jimport org.hipparchus.analysis.RealFieldUnivariateFunction
const SplineInterpolator = @jimport org.hipparchus.analysis.interpolation.SplineInterpolator
const TricubicFunction = @jimport org.hipparchus.analysis.interpolation.TricubicFunction
const TricubicInterpolatingFunction = @jimport org.hipparchus.analysis.interpolation.TricubicInterpolatingFunction
const TricubicInterpolator = @jimport org.hipparchus.analysis.interpolation.TricubicInterpolator
const TrivariateFunction = @jimport org.hipparchus.analysis.TrivariateFunction
const TrivariateGridInterpolator = @jimport org.hipparchus.analysis.interpolation.TrivariateGridInterpolator
const UnitSphereRandomVectorGenerator = @jimport org.hipparchus.random.UnitSphereRandomVectorGenerator
const UnivariateFunction = @jimport org.hipparchus.analysis.UnivariateFunction
const UnivariateInterpolator = @jimport org.hipparchus.analysis.interpolation.UnivariateInterpolator
const UnivariatePeriodicInterpolator = @jimport org.hipparchus.analysis.interpolation.UnivariatePeriodicInterpolator

export AkimaSplineInterpolator
export BicubicFunction
export BicubicInterpolatingFunction
export BicubicInterpolator
export BilinearInterpolatingFunction
export BilinearInterpolator
export BivariateGridInterpolator
export DividedDifferenceInterpolator
export FieldHermiteInterpolator
export FieldUnivariateInterpolator
export GridAxis
export HermiteInterpolator
export InterpolatingMicrosphere
export InterpolatingMicrosphere2D
export LinearInterpolator
export LoessInterpolator
export MicrosphereProjectionInterpolator
export MultivariateInterpolator
export NevilleInterpolator
export PiecewiseBicubicSplineInterpolatingFunction
export PiecewiseBicubicSplineInterpolator
export SplineInterpolator
export TricubicFunction
export TricubicInterpolatingFunction
export TricubicInterpolator
export TrivariateGridInterpolator
export UnivariateInterpolator
export UnivariatePeriodicInterpolator
export add_sample_point
export copy
export derivatives
export equals
export get_class
export get_dimension
export get_n
export get_polynomials
export get_size
export get_x_inf
export get_x_sup
export get_y_inf
export get_y_sup
export hash_code
export interpolate
export interpolation_index
export is_valid_point
export node
export notify
export notify_all
export size
export smooth
export to_real_field_bivariate_function
export to_string
export value
export wait

include("akima_spline_interpolator.jl")
include("bicubic_function.jl")
include("bicubic_interpolating_function.jl")
include("bicubic_interpolator.jl")
include("bilinear_interpolating_function.jl")
include("bilinear_interpolator.jl")
include("bivariate_grid_interpolator.jl")
include("divided_difference_interpolator.jl")
include("field_hermite_interpolator.jl")
include("field_univariate_interpolator.jl")
include("grid_axis.jl")
include("hermite_interpolator.jl")
include("interpolating_microsphere.jl")
include("interpolating_microsphere2_d.jl")
include("linear_interpolator.jl")
include("loess_interpolator.jl")
include("microsphere_projection_interpolator.jl")
include("multivariate_interpolator.jl")
include("neville_interpolator.jl")
include("piecewise_bicubic_spline_interpolating_function.jl")
include("piecewise_bicubic_spline_interpolator.jl")
include("spline_interpolator.jl")
include("tricubic_function.jl")
include("tricubic_interpolating_function.jl")
include("tricubic_interpolator.jl")
include("trivariate_grid_interpolator.jl")
include("univariate_interpolator.jl")
include("univariate_periodic_interpolator.jl")

end


text:
# 点でのrmpシミュレーション

using Dates
using LinearAlgebra
using Parameters
using Plots

include("rmp.jl")
include("utils.jl")

"""ルンゲクッタ法（4次）"""
function solve_RungeKutta(;f, x₀, t_span, Δt)

    t = range(t_span..., step = Δt)  # 時間軸
    x = Vector{typeof(x₀)}(undef, length(t))  # 解を格納する1次元配列
    fg_norm = Vector{typeof(x₀[1])}(undef, length(t))
    fo_norm = Vector{typeof(x₀[1])}(undef, length(t))

    x[1] = x₀  # 初期値
    _, f_norms = ddx(x₀[1:2], x₀[3:4])
    fg_norm[1] = f_norms[1]
    fo_norm[1] = f_norms[2]

    for i in 1:length(x)-1

        k₁, _ = f(x[i])
        k₂, _ = f(x[i] .+ k₁ .* Δt/2)
        k₃, _ = f(x[i] .+ k₂ .* Δt/2)
        k₄, _ = f(x[i] .+ k₃ .* Δt)

        x[i+1] = x[i] .+ (k₁ .+ 2k₂ .+ 2k₃ .+k₄) .* Δt/6

        _, f_norms = ddx(x[i+1][1:2], x[i+1][3:4])
        fg_norm[i+1] = f_norms[1]
        fo_norm[i+1] = f_norms[2]
    end

    t, x, fg_norm, fo_norm
end

"""multi-robot-rmpのアトラクタrmp"""
function multi_attractor_rmp(x::Vector{T}, dx::Vector{T}, g::Vector{T}) where T
    wu = 10.0
    wl = 1.0
    sigma = 1.0
    alpha = 1.0
    eta = 2.0
    gain = 1.0
    tol = 0.005

    z = x - g
    dz = dx
    #J = Matrix{T}(I, 2, 2)
    #dJ = zeros(T, 2, 2)

    z_norm = norm(z)
    beta = exp(-z_norm^2 / 2 / (sigma^2))
    w = (wu - wl) * beta + wl
    s = (1-exp(-2*alpha*z_norm)) / (1+exp(-2*alpha*z_norm))

    G = Matrix{T}(I, 2, 2) * w

    if z_norm > tol
        grad_phi = s / z_norm * w * z * gain
    else
        grad_phi = 0.0
    end

    Bx_dot = eta * w * dz
    grad_w = -beta * (wu-wl) / sigma^2*z

    dz_norm = norm(dz)

    xi = -0.5 * (dz_norm^2 * grad_w - 2 * dz * dz' * dz * dz' * grad_w)

    M = G
    f = -grad_phi .- Bx_dot .- xi

    return f, M
end

"""multi-robot-rmpの障害物回避rmp"""
function multi_avoidance_rmp(x::Vector{T}, dx::Vector{T}, o::Vector{T}) where T
    R = 1.0
    alpha = 1e-5
    eta = 0.0
    epsilon = 0.2

    z_vec = (x - o)
    dz_vec = dx

    # ## マルチロボットの安全距離付きタスク写像 ###
    # z = norm(z_vec)/R - 1
    # J = 1/norm(z_vec) * (z_vec)' / R
    # dJ = (dz_vec' * (-1/norm(z_vec)^3 * (z_vec) * (z_vec)' + 1/norm(z_vec) * Matrix{T}(I, 2, 2))) / R

    ### 普通の障害物との距離関数のタスク写像 ###
    z = norm(z_vec)
    dz = (1/z .* dot((z_vec), (dz_vec)))[1]
    J = 1/z * (z_vec)'
    dJ = -z^(-2) .* (dz_vec' .- ((z_vec)' .* dz))

    dz = J * dx

    if z < 0
        w = 1.0e10
        grad_w = 0.0
    else
        w = 1 / z^4
        grad_w = -4 / z^5
    end

    u = epsilon + min(0.0, dz) * dz
    g = w * u

    grad_u = 2 * min(0.0, dz)
    grad_phi = alpha * w * grad_w
    xi = 1/2 * dz^2 * u * grad_w

    _M = g + 1/2 * dz * w * grad_u
    M = min(max(_M, -1e5), 1e5)

    Bx_dot = eta * g * dz

    _f = - grad_phi - xi -Bx_dot
    f = min(max(_f, -1e10), 1e10)

    pulled_f = J' * (f .- M * dJ * dx)[1]

    pulled_M = J' * M * J

    return pulled_f, pulled_M
end

const p = (
    obs_avoidance = OriginalRMPCollisionAvoidance(
    scale_rep = 0.1,
    scale_damp = 0.1,
    ratio = 1.0,
    gain = 0.0,
    r = 0.1
    ),
    # obs_avoidance = RMPfromGDSCollisionAvoidance(
    #     rw = 0.5,
    #     sigma = 1.0,
    #     alpha = 1.0
    # ),
    # attractor = OriginalRMPAttractor(
    #     max_speed = 1.0,
    #     gain = 1.5,
    #     ddq_damp_r = 1.0,
    #     sigma_W = 1.0,
    #     sigma_H = 1.0,
    #     metric_damp_r = 0.1
    # )
    attractor = RMPfromGDSAttractor(
        max_speed = 1.0,
        gain = 1.5,
        f_alpha = 0.15,
        sigma_alpha = 1.0,
        sigma_gamma = 1.0,
        wu = 10.0,
        wl = 1.0,
        alpha = 0.15,
        epsilon = 1.0e-5
    )
)

const o = [0.0, 0.0]
const g = [-3.0, 3.0]

"""所望の加速度"""
function ddx(x, dx)

    #fg, Mg = get_natural(p.attractor, x, dx, g)
    #fo, Mo = get_natural(p.obs_avoidance, x, dx, o)

    #fo = zero(fg)
    #Mo = zero(Mg)

    fg, Mg = multi_attractor_rmp(x, dx, g)
    fo, Mo = multi_avoidance_rmp(x, dx, o)

    root_f = fg .+ fo
    root_M = Mg .+ Mo

    z = pinv(root_M) * root_f
    return z, (norm(fg), norm(fo))
end

function dX(X)

    x = X[1:2]
    dx = X[3:4]
    _ddx, f_norms = ddx(x, dx)
    return [dx; _ddx], f_norms
end

function run()

    x0 = [2.5, -3.2]
    dx0 = [-1.0, 1.0]

    #x0 = [-3.0, 3.0]
    #dx0 = [0.0, 0.0]

    t, X, fg, fo = solve_RungeKutta(
        f = dX,
        x₀ = [x0; dx0],
        t_span = (0.0, 35.0),
        Δt = 0.001
    )

    # グラフ化
    x, y, dx, dy = split_vec_of_arrays(X)

    fig = plot(x, y, label = "x", legend=:outerright)
    scatter!(fig, [x[1]], [y[1]], label="x0")
    scatter!(fig, [x[end]], [y[end]], label="xend")
    scatter!(fig, [g[1]], [g[2]], markershape=:star6, label="g")
    scatter!(fig, [o[1]], [o[2]], label="o")

    function circle()
        theta = LinRange(0, 2*pi, 500)
        o[1] .+ 1*sin.(theta), o[2] .+ 1*cos.(theta)
    end
    plot!(
        fig, circle(), seriestype=[:shape,], lw=0.5,
        legend=false, fillalpha=0.2, aspect_ratio=1,
    )

    fig2 = plot(t, x, label="_x", legend=:outerright)
    plot!(fig2, t, y, label="_y")
    plot!(fig2, t, dx, label="dx")
    plot!(fig2, t, dy, label="dy")

    fig3 = plot(t, fg, label="fg", legend=:outerright)
    plot!(fig3, t, fo, label="fo")

    plot(
        fig, fig2, fig3,
        layout=(3, 1), size=(500,900)
    )
end

"""データ保存先のパス

シミュレーション結果を保存するディレクトリのパスを返す
ないなら新規作成
"""
function get_time_string()
    _nd = now()  # 時刻取得
    _Y = _nd |> Dates.year |> string
    _M = _nd |> Dates.month |> string
    _D = _nd |> Dates.day |> string
    _h = _nd |> Dates.hour |> string
    _m = _nd |> Dates.minute |> string
    _s = _nd |> Dates.second |> string
    t = _Y * _M * _D * "-" * _h * _m * _s

    linpath = "../result_of_manipulator_dynamics/" * _Y * _M * _D * "/"
    if !ispath(linpath)
        mkdir(linpath)
    end
    path = linpath * t
    return path
end

path = get_time_string()  # 実行時のデータ保存パス

@time run()


text:
export TRARC2_MP

function TRARC2_MP(nlp 		:: AbstractNLPModel,
                nlp_stop 	:: NLPStopping;
                TR 			:: TrustRegion = TrustRegion(eltype(nlp.meta.x0)(10.0)),
			    c 			:: Combi = Combi{eltype(nlp.meta.x0)}(hessian_dense, PDataLDLt{eltype(nlp.meta.x0)}, solve_modelTRDiag, preprocessLDLt, decreaseFact, Tparam{eltype(nlp.meta.x0)}()),
				correction  :: Symbol = :None,
			    robust 		:: Bool = true,
                verbose 	:: Bool = false
                )

	T = eltype(nlp.meta.x0)
	@show correction
	# @assert T == Float32

	nlp_at_x = nlp_stop.current_state
    hessian_rep, PData, solve_model, pre_process, decrease, params = extract(c)

    α = T.(TR.α₀)  # initial Trust Region size
	# @show typeof(α)
    xt, xtnext, d, Df = copy(nlp.meta.x0), copy(nlp.meta.x0), copy(nlp.meta.x0), 0.0
	# @show typeof(xt[1])
    xopt = xt
    λ = 1.0

    n = length(xt)
    ∇f = Array{T}(undef, n)
    ∇fnext = Array{T}(undef, n)

    ft = obj(nlp, xt)
	# @show typeof(ft)
    fopt = ft
    grad!(nlp, xt, ∇f)
	OK = update_and_start!(nlp_stop, x = xt, fx = ft, gx = ∇f, g0 = ∇f)

	norm_∇f = norm(nlp_at_x.gx)
    norm_∇f0 = norm_∇f
    ∇fopt = ∇f
    norm_∇fopt = norm_∇f
	!OK && update!(nlp_at_x, Hx = hessian_rep(nlp, xt))

    ftnext = ft
    iter = 0

    verbose && display_header_iterations()
    verbose && display_success(iter, ftnext, norm_∇f0, 0.0, α)

    succ, unsucc, verysucc, unsuccinarow = 0, 0, 0, 0

    max_unsuccinarow = TR.max_unsuccinarow

    calls = [0, 0, 0, 0]

	global xdemi = NaN * rand(n)
	global conversion = false

    while !OK
        PData = pre_process(nlp_at_x.Hx, ∇f, params, calls, nlp_stop.meta.max_eval)

		# printstyled("On a PData \n", color = :bold)

		# @show typeof(PData.L[1])
		# @show typeof(PData.D[1])

        if ~PData.OK
			@warn("Something wrong with PData")
			return nlp_at_x, nlp_stop.meta.optimal
		end

        success = false
		Ht = nothing
		# printstyled("On a succes = $success \n", color = :bold)
		# @show Ht

        while !success & !OK & (unsuccinarow < TR.max_unsuccinarow)
			# printstyled("On recommence une fois dans le while \n", color = :bold)
            try
                d, xdemi, λ = solve_model(nlp_stop, PData, α)
            catch
                println(" Problem in solve_model")
                return nlp_at_x, nlp_stop.meta.optimal
            end
			# printstyled("On a d \n", color = :bold)
			# @show typeof(d[1])

            Δq = -(∇f + T(0.5) * nlp_at_x.Hx * d)⋅d
			# printstyled("On a Δq = $Δq \n", color = :bold)
			# @show eltype(Δq)

            if Δq < 0.0 println("*******   Ascent direction in SolveModel: Δq = $Δq")
                println("  g⋅d = $(∇f⋅d), 0.5 d'Hd = $(0.5*(nlp_at_x.Hx*d)⋅d)  α = $α  λ = $λ")
				#@bp
                #try println(" cond(H) = $(cond(full(H)))") catch println("sparse hessian, no cond") end
                #D, Q = eig(full(H))
                #lm = findmin(D); lM = findmax(D)
                #println("λ min (H) = $lm  λ max (H) = $lM")
                #try printtln(" cond(L) = $(cond(PData.L))") catch println("Spectral") end
                #try println(" ||H - reconstructH|| = $(norm(H-reconstructH(PData)))")
                #catch
                #    println(" reconstruct H no available ")
                #end
                println("  calls  $calls   iters $verysucc; $succ; $unsucc")

				return nlp_at_x, nlp_stop.meta.optimal
            end
            slope = ∇f ⋅ d
			# printstyled("On a slope = $slope \n", color = :bold)
            # xtnext = xt + d
			if !(true in isnan.(xdemi))
				xtnext = xdemi + d
			else
				xtnext = xt + d
			end
			# printstyled("On a xtnext = $xtnext \n", color = :bold)

            iter += 1

            try
                ftnext = obj(nlp, xtnext)
            catch
                ftnext = Inf;
            end

            if isnan(ftnext)
                ftnext = Inf
            end

			# printstyled("On a ftnext = $ftnext \n", color = :bold)

            Δf = ft - ftnext

			# printstyled("On a Δf = $Δf \n", color = :bold)

            r, good_grad, ∇fnext = compute_r(nlp, ft, Δf, Δq, slope, d, xtnext, ∇fnext, robust)
			# printstyled("On a r = $r \n", color = :bold)
			# printstyled("On a good_grad = $good_grad \n", color = :bold)
			# printstyled("On a ∇fnext = $∇fnext \n", color = :bold)

            if r < TR.acceptance_threshold
                verbose && display_failure(iter, ftnext, λ, α)
	        	unsucc = unsucc + 1
	        	unsuccinarow = unsuccinarow + 1
	        	α = decrease(PData, α, TR)
                # fbidon = obj(nlp, xt)
	    	else
	        	success = true

	        	unsuccinarow = 0
	        	xt = copy(xtnext)
	        	ft = ftnext

	        	if good_grad
					∇f = copy(∇fnext)
            	else
					grad!(nlp, xt, ∇f);
                end

                norm_∇f = stop_norm(∇f)
				verysucc += 1
                Ht = hessian_rep(nlp, xt)
		        if r > TR.increase_threshold
					# printstyled("avant d'augmenté α et T = $(typeof(α)) \n", color = :blue)
		            α = increase(PData, α, TR)
					# printstyled("on a augmenté α et T = $(typeof(α)) \n", color = :blue)
		            verbose && display_v_success(iter, ft, norm_∇f, λ, α)
		        else
	                if r < TR.reduce_threshold
						# printstyled("avant de réduire α et T = $(typeof(α)) \n", color = :blue)
	                    α = decrease(PData, α, TR)
						# printstyled("on a réduit α et T = $(typeof(α)) \n", color = :blue)
	                end
		            verbose && display_success(iter, ft, norm_∇f, λ, α)
		            succ += 1
		        end
	    	end
        end # while !succes

		OK = update_and_stop!(nlp_stop, x = xt, fx = ft, gx = ∇f, Hx = Ht)
		nlp_stop.meta.nb_of_stop = iter
		# @show eltype(nlp_at_x.x)

		# if norm(nlp_at_x.gx) < T(1.0e-03)
		# 	printstyled("on a une norme plus petite que 1.0e-03 \n", color = :green)
		# 	T = Float128
		# 	TR = convert_TR(T, TR)
		# 	nlp_at_x = convert_nlp(T, nlp_at_x)
		# 	stopmeta = StoppingMeta(atol = sqrt(eps(T)), rtol = eps(T), max_iter = 50)
		# 	nlp_stop = NLPStopping(nlp, Stopping.unconstrained, nlp_at_x, meta = stopmeta)
		# 	@show eltype(d)
		# 	@show eltype(α)
		# 	@show eltype(nlp_at_x.x)
		# 	@show eltype(TR.α)
		if (norm_∇f < T(0.001)) && !(conversion) && (T != BigFloat)
			printstyled("on change de type! \n", color = :green)
			T = BigFloat
			TR = convert_TR(T, TR)
			nlp_at_x = convert_nlp(T, nlp_at_x)
			stopmeta = StoppingMeta(atol = sqrt(eps(T)), rtol = eps(T), max_iter = 50)
			nlp_stop = NLPStopping(nlp, Stopping.unconstrained, nlp_at_x, meta = stopmeta)
			# @show eltype(d)
			# @show eltype(α)
			# @show eltype(nlp_at_x.x)
			# @show eltype(TR.α)
			conversion = true
		end
		# printstyled("On est sorti du if \n", color = :bold)
        calls = [nlp.counters.neval_obj,  nlp.counters.neval_grad, nlp.counters.neval_hess, nlp.counters.neval_hprod]
		# printstyled("On a calls \n", color = :bold)
		# @show OK
    end # while !OK

    xopt = xt
    fopt = ft
    ∇fopt = ∇f
    norm_∇fopt = norm_∇f

    niter = verysucc + succ + unsucc;
    verbose && @printf(" %10i unsuccessful iterations \n", unsucc)
    verbose && @printf(" %10i successful iterations \n", succ)
    verbose && @printf(" %10i very successful iterations \n", verysucc)
    verbose && @printf(" %10i total successful iterations \n", verysucc + succ)
    verbose && @printf(" %10i total iterations \n", niter)

    calls = [nlp.counters.neval_obj, nlp.counters.neval_grad, nlp.counters.neval_hess, nlp.counters.neval_hprod]

    total_calls = nlp.counters.neval_obj + nlp.counters.neval_grad + n * nlp.counters.neval_hess + nlp.counters.neval_hprod
     verbose && @printf(" %10i total f %10i g %10i H calls  %10i Hv calls  \n", nlp.counters.neval_obj, nlp.counters.neval_grad, nlp.counters.neval_hess, nlp.counters.neval_hprod)

    if OK && verbose  @printf("\n     Final cost(x) = %f  ||g|| = %9.2e  nb of f/g/H evals = %i  converged\n", fopt, norm_∇fopt, total_calls)
    elseif verbose
        @printf("\n     Final cost(x) = %f  ||g|| = %9.2e  nb of f/g/H evals  = %i  NOT converged\n\n", fopt, norm_∇fopt, total_calls)
    end;

    return nlp_at_x, nlp_stop.meta.optimal

end


text:
abstract SetEx
abstract RelEx

include("sex.jl")

include("rex.jl")