text:
# Julia wrapper for header: Audio.h
# Automatically generated using Clang.jl

# Julia wrapper for header: Export.h
# Automatically generated using Clang.jl

# Julia wrapper for header: Listener.h
# Automatically generated using Clang.jl

function sfListener_setGlobalVolume(volume)
    ccall((:sfListener_setGlobalVolume, libcsfml_audio), Cvoid, (Cfloat,), volume)
end

function sfListener_getGlobalVolume()
    ccall((:sfListener_getGlobalVolume, libcsfml_audio), Cfloat, ())
end

function sfListener_setPosition(position)
    ccall((:sfListener_setPosition, libcsfml_audio), Cvoid, (sfVector3f,), position)
end

function sfListener_getPosition()
    ccall((:sfListener_getPosition, libcsfml_audio), sfVector3f, ())
end

function sfListener_setDirection(direction)
    ccall((:sfListener_setDirection, libcsfml_audio), Cvoid, (sfVector3f,), direction)
end

function sfListener_getDirection()
    ccall((:sfListener_getDirection, libcsfml_audio), sfVector3f, ())
end

function sfListener_setUpVector(upVector)
    ccall((:sfListener_setUpVector, libcsfml_audio), Cvoid, (sfVector3f,), upVector)
end

function sfListener_getUpVector()
    ccall((:sfListener_getUpVector, libcsfml_audio), sfVector3f, ())
end
# Julia wrapper for header: Music.h
# Automatically generated using Clang.jl

function sfMusic_createFromFile(filename)
    ccall((:sfMusic_createFromFile, libcsfml_audio), Ptr{sfMusic}, (Cstring,), filename)
end

function sfMusic_createFromMemory(data, sizeInBytes)
    ccall((:sfMusic_createFromMemory, libcsfml_audio), Ptr{sfMusic}, (Ptr{Cvoid}, Csize_t), data, sizeInBytes)
end

function sfMusic_createFromStream(stream)
    ccall((:sfMusic_createFromStream, libcsfml_audio), Ptr{sfMusic}, (Ptr{sfInputStream},), stream)
end

function sfMusic_destroy(music)
    ccall((:sfMusic_destroy, libcsfml_audio), Cvoid, (Ptr{sfMusic},), music)
end

function sfMusic_setLoop(music, loop)
    ccall((:sfMusic_setLoop, libcsfml_audio), Cvoid, (Ptr{sfMusic}, sfBool), music, loop)
end

function sfMusic_getLoop(music)
    ccall((:sfMusic_getLoop, libcsfml_audio), sfBool, (Ptr{sfMusic},), music)
end

function sfMusic_getDuration(music)
    ccall((:sfMusic_getDuration, libcsfml_audio), sfTime, (Ptr{sfMusic},), music)
end

function sfMusic_getLoopPoints(music)
    ccall((:sfMusic_getLoopPoints, libcsfml_audio), sfTimeSpan, (Ptr{sfMusic},), music)
end

function sfMusic_setLoopPoints(music, timePoints)
    ccall((:sfMusic_setLoopPoints, libcsfml_audio), Cvoid, (Ptr{sfMusic}, sfTimeSpan), music, timePoints)
end

function sfMusic_play(music)
    ccall((:sfMusic_play, libcsfml_audio), Cvoid, (Ptr{sfMusic},), music)
end

function sfMusic_pause(music)
    ccall((:sfMusic_pause, libcsfml_audio), Cvoid, (Ptr{sfMusic},), music)
end

function sfMusic_stop(music)
    ccall((:sfMusic_stop, libcsfml_audio), Cvoid, (Ptr{sfMusic},), music)
end

function sfMusic_getChannelCount(music)
    ccall((:sfMusic_getChannelCount, libcsfml_audio), UInt32, (Ptr{sfMusic},), music)
end

function sfMusic_getSampleRate(music)
    ccall((:sfMusic_getSampleRate, libcsfml_audio), UInt32, (Ptr{sfMusic},), music)
end

function sfMusic_getStatus(music)
    ccall((:sfMusic_getStatus, libcsfml_audio), sfSoundStatus, (Ptr{sfMusic},), music)
end

function sfMusic_getPlayingOffset(music)
    ccall((:sfMusic_getPlayingOffset, libcsfml_audio), sfTime, (Ptr{sfMusic},), music)
end

function sfMusic_setPitch(music, pitch)
    ccall((:sfMusic_setPitch, libcsfml_audio), Cvoid, (Ptr{sfMusic}, Cfloat), music, pitch)
end

function sfMusic_setVolume(music, volume)
    ccall((:sfMusic_setVolume, libcsfml_audio), Cvoid, (Ptr{sfMusic}, Cfloat), music, volume)
end

function sfMusic_setPosition(music, position)
    ccall((:sfMusic_setPosition, libcsfml_audio), Cvoid, (Ptr{sfMusic}, sfVector3f), music, position)
end

function sfMusic_setRelativeToListener(music, relative)
    ccall((:sfMusic_setRelativeToListener, libcsfml_audio), Cvoid, (Ptr{sfMusic}, sfBool), music, relative)
end

function sfMusic_setMinDistance(music, distance)
    ccall((:sfMusic_setMinDistance, libcsfml_audio), Cvoid, (Ptr{sfMusic}, Cfloat), music, distance)
end

function sfMusic_setAttenuation(music, attenuation)
    ccall((:sfMusic_setAttenuation, libcsfml_audio), Cvoid, (Ptr{sfMusic}, Cfloat), music, attenuation)
end

function sfMusic_setPlayingOffset(music, timeOffset)
    ccall((:sfMusic_setPlayingOffset, libcsfml_audio), Cvoid, (Ptr{sfMusic}, sfTime), music, timeOffset)
end

function sfMusic_getPitch(music)
    ccall((:sfMusic_getPitch, libcsfml_audio), Cfloat, (Ptr{sfMusic},), music)
end

function sfMusic_getVolume(music)
    ccall((:sfMusic_getVolume, libcsfml_audio), Cfloat, (Ptr{sfMusic},), music)
end

function sfMusic_getPosition(music)
    ccall((:sfMusic_getPosition, libcsfml_audio), sfVector3f, (Ptr{sfMusic},), music)
end

function sfMusic_isRelativeToListener(music)
    ccall((:sfMusic_isRelativeToListener, libcsfml_audio), sfBool, (Ptr{sfMusic},), music)
end

function sfMusic_getMinDistance(music)
    ccall((:sfMusic_getMinDistance, libcsfml_audio), Cfloat, (Ptr{sfMusic},), music)
end

function sfMusic_getAttenuation(music)
    ccall((:sfMusic_getAttenuation, libcsfml_audio), Cfloat, (Ptr{sfMusic},), music)
end
# Julia wrapper for header: Sound.h
# Automatically generated using Clang.jl

function sfSound_create()
    ccall((:sfSound_create, libcsfml_audio), Ptr{sfSound}, ())
end

function sfSound_copy(sound)
    ccall((:sfSound_copy, libcsfml_audio), Ptr{sfSound}, (Ptr{sfSound},), sound)
end

function sfSound_destroy(sound)
    ccall((:sfSound_destroy, libcsfml_audio), Cvoid, (Ptr{sfSound},), sound)
end

function sfSound_play(sound)
    ccall((:sfSound_play, libcsfml_audio), Cvoid, (Ptr{sfSound},), sound)
end

function sfSound_pause(sound)
    ccall((:sfSound_pause, libcsfml_audio), Cvoid, (Ptr{sfSound},), sound)
end

function sfSound_stop(sound)
    ccall((:sfSound_stop, libcsfml_audio), Cvoid, (Ptr{sfSound},), sound)
end

function sfSound_setBuffer(sound, buffer)
    ccall((:sfSound_setBuffer, libcsfml_audio), Cvoid, (Ptr{sfSound}, Ptr{sfSoundBuffer}), sound, buffer)
end

function sfSound_getBuffer(sound)
    ccall((:sfSound_getBuffer, libcsfml_audio), Ptr{sfSoundBuffer}, (Ptr{sfSound},), sound)
end

function sfSound_setLoop(sound, loop)
    ccall((:sfSound_setLoop, libcsfml_audio), Cvoid, (Ptr{sfSound}, sfBool), sound, loop)
end

function sfSound_getLoop(sound)
    ccall((:sfSound_getLoop, libcsfml_audio), sfBool, (Ptr{sfSound},), sound)
end

function sfSound_getStatus(sound)
    ccall((:sfSound_getStatus, libcsfml_audio), sfSoundStatus, (Ptr{sfSound},), sound)
end

function sfSound_setPitch(sound, pitch)
    ccall((:sfSound_setPitch, libcsfml_audio), Cvoid, (Ptr{sfSound}, Cfloat), sound, pitch)
end

function sfSound_setVolume(sound, volume)
    ccall((:sfSound_setVolume, libcsfml_audio), Cvoid, (Ptr{sfSound}, Cfloat), sound, volume)
end

function sfSound_setPosition(sound, position)
    ccall((:sfSound_setPosition, libcsfml_audio), Cvoid, (Ptr{sfSound}, sfVector3f), sound, position)
end

function sfSound_setRelativeToListener(sound, relative)
    ccall((:sfSound_setRelativeToListener, libcsfml_audio), Cvoid, (Ptr{sfSound}, sfBool), sound, relative)
end

function sfSound_setMinDistance(sound, distance)
    ccall((:sfSound_setMinDistance, libcsfml_audio), Cvoid, (Ptr{sfSound}, Cfloat), sound, distance)
end

function sfSound_setAttenuation(sound, attenuation)
    ccall((:sfSound_setAttenuation, libcsfml_audio), Cvoid, (Ptr{sfSound}, Cfloat), sound, attenuation)
end

function sfSound_setPlayingOffset(sound, timeOffset)
    ccall((:sfSound_setPlayingOffset, libcsfml_audio), Cvoid, (Ptr{sfSound}, sfTime), sound, timeOffset)
end

function sfSound_getPitch(sound)
    ccall((:sfSound_getPitch, libcsfml_audio), Cfloat, (Ptr{sfSound},), sound)
end

function sfSound_getVolume(sound)
    ccall((:sfSound_getVolume, libcsfml_audio), Cfloat, (Ptr{sfSound},), sound)
end

function sfSound_getPosition(sound)
    ccall((:sfSound_getPosition, libcsfml_audio), sfVector3f, (Ptr{sfSound},), sound)
end

function sfSound_isRelativeToListener(sound)
    ccall((:sfSound_isRelativeToListener, libcsfml_audio), sfBool, (Ptr{sfSound},), sound)
end

function sfSound_getMinDistance(sound)
    ccall((:sfSound_getMinDistance, libcsfml_audio), Cfloat, (Ptr{sfSound},), sound)
end

function sfSound_getAttenuation(sound)
    ccall((:sfSound_getAttenuation, libcsfml_audio), Cfloat, (Ptr{sfSound},), sound)
end

function sfSound_getPlayingOffset(sound)
    ccall((:sfSound_getPlayingOffset, libcsfml_audio), sfTime, (Ptr{sfSound},), sound)
end
# Julia wrapper for header: SoundBuffer.h
# Automatically generated using Clang.jl

function sfSoundBuffer_createFromFile(filename)
    ccall((:sfSoundBuffer_createFromFile, libcsfml_audio), Ptr{sfSoundBuffer}, (Cstring,), filename)
end

function sfSoundBuffer_createFromMemory(data, sizeInBytes)
    ccall((:sfSoundBuffer_createFromMemory, libcsfml_audio), Ptr{sfSoundBuffer}, (Ptr{Cvoid}, Csize_t), data, sizeInBytes)
end

function sfSoundBuffer_createFromStream(stream)
    ccall((:sfSoundBuffer_createFromStream, libcsfml_audio), Ptr{sfSoundBuffer}, (Ptr{sfInputStream},), stream)
end

function sfSoundBuffer_createFromSamples(samples, sampleCount, channelCount, sampleRate)
    ccall((:sfSoundBuffer_createFromSamples, libcsfml_audio), Ptr{sfSoundBuffer}, (Ptr{sfInt16}, sfUint64, UInt32, UInt32), samples, sampleCount, channelCount, sampleRate)
end

function sfSoundBuffer_copy(soundBuffer)
    ccall((:sfSoundBuffer_copy, libcsfml_audio), Ptr{sfSoundBuffer}, (Ptr{sfSoundBuffer},), soundBuffer)
end

function sfSoundBuffer_destroy(soundBuffer)
    ccall((:sfSoundBuffer_destroy, libcsfml_audio), Cvoid, (Ptr{sfSoundBuffer},), soundBuffer)
end

function sfSoundBuffer_saveToFile(soundBuffer, filename)
    ccall((:sfSoundBuffer_saveToFile, libcsfml_audio), sfBool, (Ptr{sfSoundBuffer}, Cstring), soundBuffer, filename)
end

function sfSoundBuffer_getSamples(soundBuffer)
    ccall((:sfSoundBuffer_getSamples, libcsfml_audio), Ptr{sfInt16}, (Ptr{sfSoundBuffer},), soundBuffer)
end

function sfSoundBuffer_getSampleCount(soundBuffer)
    ccall((:sfSoundBuffer_getSampleCount, libcsfml_audio), sfUint64, (Ptr{sfSoundBuffer},), soundBuffer)
end

function sfSoundBuffer_getSampleRate(soundBuffer)
    ccall((:sfSoundBuffer_getSampleRate, libcsfml_audio), UInt32, (Ptr{sfSoundBuffer},), soundBuffer)
end

function sfSoundBuffer_getChannelCount(soundBuffer)
    ccall((:sfSoundBuffer_getChannelCount, libcsfml_audio), UInt32, (Ptr{sfSoundBuffer},), soundBuffer)
end

function sfSoundBuffer_getDuration(soundBuffer)
    ccall((:sfSoundBuffer_getDuration, libcsfml_audio), sfTime, (Ptr{sfSoundBuffer},), soundBuffer)
end
# Julia wrapper for header: SoundBufferRecorder.h
# Automatically generated using Clang.jl

function sfSoundBufferRecorder_create()
    ccall((:sfSoundBufferRecorder_create, libcsfml_audio), Ptr{sfSoundBufferRecorder}, ())
end

function sfSoundBufferRecorder_destroy(soundBufferRecorder)
    ccall((:sfSoundBufferRecorder_destroy, libcsfml_audio), Cvoid, (Ptr{sfSoundBufferRecorder},), soundBufferRecorder)
end

function sfSoundBufferRecorder_start(soundBufferRecorder, sampleRate)
    ccall((:sfSoundBufferRecorder_start, libcsfml_audio), sfBool, (Ptr{sfSoundBufferRecorder}, UInt32), soundBufferRecorder, sampleRate)
end

function sfSoundBufferRecorder_stop(soundBufferRecorder)
    ccall((:sfSoundBufferRecorder_stop, libcsfml_audio), Cvoid, (Ptr{sfSoundBufferRecorder},), soundBufferRecorder)
end

function sfSoundBufferRecorder_getSampleRate(soundBufferRecorder)
    ccall((:sfSoundBufferRecorder_getSampleRate, libcsfml_audio), UInt32, (Ptr{sfSoundBufferRecorder},), soundBufferRecorder)
end

function sfSoundBufferRecorder_getBuffer(soundBufferRecorder)
    ccall((:sfSoundBufferRecorder_getBuffer, libcsfml_audio), Ptr{sfSoundBuffer}, (Ptr{sfSoundBufferRecorder},), soundBufferRecorder)
end

function sfSoundBufferRecorder_setDevice(soundBufferRecorder, name)
    ccall((:sfSoundBufferRecorder_setDevice, libcsfml_audio), sfBool, (Ptr{sfSoundBufferRecorder}, Cstring), soundBufferRecorder, name)
end

function sfSoundBufferRecorder_getDevice(soundBufferRecorder)
    ccall((:sfSoundBufferRecorder_getDevice, libcsfml_audio), Cstring, (Ptr{sfSoundBufferRecorder},), soundBufferRecorder)
end
# Julia wrapper for header: SoundRecorder.h
# Automatically generated using Clang.jl

function sfSoundRecorder_create(onStart, onProcess, onStop, userData)
    ccall((:sfSoundRecorder_create, libcsfml_audio), Ptr{sfSoundRecorder}, (sfSoundRecorderStartCallback, sfSoundRecorderProcessCallback, sfSoundRecorderStopCallback, Ptr{Cvoid}), onStart, onProcess, onStop, userData)
end

function sfSoundRecorder_destroy(soundRecorder)
    ccall((:sfSoundRecorder_destroy, libcsfml_audio), Cvoid, (Ptr{sfSoundRecorder},), soundRecorder)
end

function sfSoundRecorder_start(soundRecorder, sampleRate)
    ccall((:sfSoundRecorder_start, libcsfml_audio), sfBool, (Ptr{sfSoundRecorder}, UInt32), soundRecorder, sampleRate)
end

function sfSoundRecorder_stop(soundRecorder)
    ccall((:sfSoundRecorder_stop, libcsfml_audio), Cvoid, (Ptr{sfSoundRecorder},), soundRecorder)
end

function sfSoundRecorder_getSampleRate(soundRecorder)
    ccall((:sfSoundRecorder_getSampleRate, libcsfml_audio), UInt32, (Ptr{sfSoundRecorder},), soundRecorder)
end

function sfSoundRecorder_isAvailable()
    ccall((:sfSoundRecorder_isAvailable, libcsfml_audio), sfBool, ())
end

function sfSoundRecorder_setProcessingInterval(soundRecorder, interval)
    ccall((:sfSoundRecorder_setProcessingInterval, libcsfml_audio), Cvoid, (Ptr{sfSoundRecorder}, sfTime), soundRecorder, interval)
end

function sfSoundRecorder_getAvailableDevices(count)
    ccall((:sfSoundRecorder_getAvailableDevices, libcsfml_audio), Ptr{Cstring}, (Ptr{Csize_t},), count)
end

function sfSoundRecorder_getDefaultDevice()
    ccall((:sfSoundRecorder_getDefaultDevice, libcsfml_audio), Cstring, ())
end

function sfSoundRecorder_setDevice(soundRecorder, name)
    ccall((:sfSoundRecorder_setDevice, libcsfml_audio), sfBool, (Ptr{sfSoundRecorder}, Cstring), soundRecorder, name)
end

function sfSoundRecorder_getDevice(soundRecorder)
    ccall((:sfSoundRecorder_getDevice, libcsfml_audio), Cstring, (Ptr{sfSoundRecorder},), soundRecorder)
end

function sfSoundRecorder_setChannelCount(soundRecorder, channelCount)
    ccall((:sfSoundRecorder_setChannelCount, libcsfml_audio), Cvoid, (Ptr{sfSoundRecorder}, UInt32), soundRecorder, channelCount)
end

function sfSoundRecorder_getChannelCount(soundRecorder)
    ccall((:sfSoundRecorder_getChannelCount, libcsfml_audio), UInt32, (Ptr{sfSoundRecorder},), soundRecorder)
end
# Julia wrapper for header: SoundStatus.h
# Automatically generated using Clang.jl

# Julia wrapper for header: SoundStream.h
# Automatically generated using Clang.jl

function sfSoundStream_create(onGetData, onSeek, channelCount, sampleRate, userData)
    ccall((:sfSoundStream_create, libcsfml_audio), Ptr{sfSoundStream}, (sfSoundStreamGetDataCallback, sfSoundStreamSeekCallback, UInt32, UInt32, Ptr{Cvoid}), onGetData, onSeek, channelCount, sampleRate, userData)
end

function sfSoundStream_destroy(soundStream)
    ccall((:sfSoundStream_destroy, libcsfml_audio), Cvoid, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_play(soundStream)
    ccall((:sfSoundStream_play, libcsfml_audio), Cvoid, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_pause(soundStream)
    ccall((:sfSoundStream_pause, libcsfml_audio), Cvoid, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_stop(soundStream)
    ccall((:sfSoundStream_stop, libcsfml_audio), Cvoid, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_getStatus(soundStream)
    ccall((:sfSoundStream_getStatus, libcsfml_audio), sfSoundStatus, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_getChannelCount(soundStream)
    ccall((:sfSoundStream_getChannelCount, libcsfml_audio), UInt32, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_getSampleRate(soundStream)
    ccall((:sfSoundStream_getSampleRate, libcsfml_audio), UInt32, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_setPitch(soundStream, pitch)
    ccall((:sfSoundStream_setPitch, libcsfml_audio), Cvoid, (Ptr{sfSoundStream}, Cfloat), soundStream, pitch)
end

function sfSoundStream_setVolume(soundStream, volume)
    ccall((:sfSoundStream_setVolume, libcsfml_audio), Cvoid, (Ptr{sfSoundStream}, Cfloat), soundStream, volume)
end

function sfSoundStream_setPosition(soundStream, position)
    ccall((:sfSoundStream_setPosition, libcsfml_audio), Cvoid, (Ptr{sfSoundStream}, sfVector3f), soundStream, position)
end

function sfSoundStream_setRelativeToListener(soundStream, relative)
    ccall((:sfSoundStream_setRelativeToListener, libcsfml_audio), Cvoid, (Ptr{sfSoundStream}, sfBool), soundStream, relative)
end

function sfSoundStream_setMinDistance(soundStream, distance)
    ccall((:sfSoundStream_setMinDistance, libcsfml_audio), Cvoid, (Ptr{sfSoundStream}, Cfloat), soundStream, distance)
end

function sfSoundStream_setAttenuation(soundStream, attenuation)
    ccall((:sfSoundStream_setAttenuation, libcsfml_audio), Cvoid, (Ptr{sfSoundStream}, Cfloat), soundStream, attenuation)
end

function sfSoundStream_setPlayingOffset(soundStream, timeOffset)
    ccall((:sfSoundStream_setPlayingOffset, libcsfml_audio), Cvoid, (Ptr{sfSoundStream}, sfTime), soundStream, timeOffset)
end

function sfSoundStream_setLoop(soundStream, loop)
    ccall((:sfSoundStream_setLoop, libcsfml_audio), Cvoid, (Ptr{sfSoundStream}, sfBool), soundStream, loop)
end

function sfSoundStream_getPitch(soundStream)
    ccall((:sfSoundStream_getPitch, libcsfml_audio), Cfloat, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_getVolume(soundStream)
    ccall((:sfSoundStream_getVolume, libcsfml_audio), Cfloat, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_getPosition(soundStream)
    ccall((:sfSoundStream_getPosition, libcsfml_audio), sfVector3f, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_isRelativeToListener(soundStream)
    ccall((:sfSoundStream_isRelativeToListener, libcsfml_audio), sfBool, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_getMinDistance(soundStream)
    ccall((:sfSoundStream_getMinDistance, libcsfml_audio), Cfloat, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_getAttenuation(soundStream)
    ccall((:sfSoundStream_getAttenuation, libcsfml_audio), Cfloat, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_getLoop(soundStream)
    ccall((:sfSoundStream_getLoop, libcsfml_audio), sfBool, (Ptr{sfSoundStream},), soundStream)
end

function sfSoundStream_getPlayingOffset(soundStream)
    ccall((:sfSoundStream_getPlayingOffset, libcsfml_audio), sfTime, (Ptr{sfSoundStream},), soundStream)
end
# Julia wrapper for header: Types.h
# Automatically generated using Clang.jl


text:
##
# Abstract relation.
abstract type AbstractRelation end

##
# Abstract proposotional relation; it is used for completeness.
abstract type AbstractPropositionalRelation     <: AbstractRelation end
# Abstract modal relation; e.g., existential relation ◊.
abstract type AbstractModalRelation             <: AbstractRelation end
# Abstract binary relation; e.g., conjunction ∧.
abstract type AbstractBinaryRelation            <: AbstractRelation end

##
# Abstract temporal relation.
abstract type AbstractTemporalRelation          <: AbstractModalRelation end
# Abstract spatial relation.
abstract type AbstractSpatialRelation           <: AbstractModalRelation end
# Abstract spatio-temporal relation.
abstract type AbstractSpatioTemporalRelation    <: AbstractModalRelation end

##
# Abstract point (temporal) relation.
abstract type AbstractPointRelation             <: AbstractTemporalRelation end
# Abstract interval (temporal) relation.
abstract type AbstractIntervalRelation          <: AbstractTemporalRelation end
# Abstract rectangle (spatial) relation.
abstract type AbstractRectangleRelation         <: AbstractSpatialRelation end
# Abstract cube (spatio-temporal) relation.
abstract type AbstractCubeRelation              <: AbstractSpatioTemporalRelation end

# Using the singleton design pattern with parametric (data) type; similar to Val{T}.
struct BinaryRelation{T}            <: AbstractBinaryRelation end
BinaryRelation(s::AbstractString)   = BinaryRelation{Symbol(s)}()
BinaryRelation(s::Symbol)           = BinaryRelation{s}()

const conjunction = BinaryRelation(:∧)
const disjunction = BinaryRelation(:∨)
const implication = BinaryRelation(:→)

show(io::IO, ::BinaryRelation{:∧})  = print(io, "∧")
show(io::IO, ::BinaryRelation{:∨})  = print(io, "∨")
show(io::IO, ::BinaryRelation{:→})  = print(io, "→")

### Interval relations

##
# Abstract existential interval (temporal) relation.
abstract type AbstractExistentialIntervalRelation   <: AbstractIntervalRelation end
# Abstract universal interval (temporal) relation.
abstract type AbstractUniversalIntervalRelation     <: AbstractIntervalRelation end

##
#
# struct IdentityIntervalRelation         <: AbstractIntervalRelation end

# Using the singleton design pattern with parametric (data) type; similar to Val{T}.
struct ExistentialIntervalRelation{T}           <: AbstractExistentialIntervalRelation end
ExistentialIntervalRelation(s::AbstractString)  = ExistentialIntervalRelation{Symbol(s)}()
ExistentialIntervalRelation(s::Symbol)          = ExistentialIntervalRelation{s}()

struct UniversalIntervalRelation{T}             <: AbstractUniversalIntervalRelation end
UniversalIntervalRelation(s::AbstractString)    = UniversalIntervalRelation{Symbol(s)}()
UniversalIntervalRelation(s::Symbol)            = UniversalIntervalRelation{s}()

const exIntRel(s::AbstractString)   = ExistentialIntervalRelation(s)
const exIntRel(s::Symbol)           = ExistentialIntervalRelation(s)
const univIntRel(s::AbstractString) = UniversalIntervalRelation(s)
const univIntRel(s::Symbol)         = UniversalIntervalRelation(s)

eltype(::ExistentialIntervalRelation{T}) where T    = T
eltype(::UniversalIntervalRelation{T}) where T      = T

# const exG       = ExistentialIntervalRelation(:G)
# const exL       = ExistentialIntervalRelation(:L)
# const exA       = ExistentialIntervalRelation(:A)
# const exO       = ExistentialIntervalRelation(:O)
# const exE       = ExistentialIntervalRelation(:E)
# const exD       = ExistentialIntervalRelation(:D)
# const exB       = ExistentialIntervalRelation(:B)
# const exInvL    = ExistentialIntervalRelation(:InvL)
# const exInvA    = ExistentialIntervalRelation(:InvA)
# const exInvO    = ExistentialIntervalRelation(:InvO)
# const exInvE    = ExistentialIntervalRelation(:InvE)
# const exInvD    = ExistentialIntervalRelation(:InvD)
# const exInvB    = ExistentialIntervalRelation(:InvB)

# const univG     = UniversalIntervalRelation(:G)
# const univL     = UniversalIntervalRelation(:L)
# const univA     = UniversalIntervalRelation(:A)
# const univO     = UniversalIntervalRelation(:O)
# const univE     = UniversalIntervalRelation(:E)
# const univD     = UniversalIntervalRelation(:D)
# const univB     = UniversalIntervalRelation(:B)
# const univInvL  = UniversalIntervalRelation(:InvL)
# const univInvA  = UniversalIntervalRelation(:InvA)
# const univInvO  = UniversalIntervalRelation(:InvO)
# const univInvE  = UniversalIntervalRelation(:InvE)
# const univInvD  = UniversalIntervalRelation(:InvD)
# const univInvB  = UniversalIntervalRelation(:InvB)

##
# show(io::IO, ::IdentityIntervalRelation)           = print(io, "|=|")

show(io::IO, ::ExistentialIntervalRelation{:G})    = print(io, "⟨G⟩")
show(io::IO, ::ExistentialIntervalRelation{:L})    = print(io, "⟨L⟩")
show(io::IO, ::ExistentialIntervalRelation{:A})    = print(io, "⟨A⟩")
show(io::IO, ::ExistentialIntervalRelation{:O})    = print(io, "⟨O⟩")
show(io::IO, ::ExistentialIntervalRelation{:E})    = print(io, "⟨E⟩")
show(io::IO, ::ExistentialIntervalRelation{:D})    = print(io, "⟨D⟩")
show(io::IO, ::ExistentialIntervalRelation{:B})    = print(io, "⟨B⟩")
show(io::IO, ::ExistentialIntervalRelation{:InvL}) = print(io, "⟨InvL⟩")
show(io::IO, ::ExistentialIntervalRelation{:InvA}) = print(io, "⟨InvA⟩")
show(io::IO, ::ExistentialIntervalRelation{:InvO}) = print(io, "⟨InvO⟩")
show(io::IO, ::ExistentialIntervalRelation{:InvE}) = print(io, "⟨InvE⟩")
show(io::IO, ::ExistentialIntervalRelation{:InvD}) = print(io, "⟨InvD⟩")
show(io::IO, ::ExistentialIntervalRelation{:InvB}) = print(io, "⟨InvB⟩")

show(io::IO, ::UniversalIntervalRelation{:G})      = print(io, "[G]")
show(io::IO, ::UniversalIntervalRelation{:L})      = print(io, "[L]")
show(io::IO, ::UniversalIntervalRelation{:A})      = print(io, "[A]")
show(io::IO, ::UniversalIntervalRelation{:O})      = print(io, "[O]")
show(io::IO, ::UniversalIntervalRelation{:E})      = print(io, "[E]")
show(io::IO, ::UniversalIntervalRelation{:D})      = print(io, "[D]")
show(io::IO, ::UniversalIntervalRelation{:B})      = print(io, "[B]")
show(io::IO, ::UniversalIntervalRelation{:InvL})   = print(io, "[InvL]")
show(io::IO, ::UniversalIntervalRelation{:InvA})   = print(io, "[InvA]")
show(io::IO, ::UniversalIntervalRelation{:InvO})   = print(io, "[InvO]")
show(io::IO, ::UniversalIntervalRelation{:InvE})   = print(io, "[InvE]")
show(io::IO, ::UniversalIntervalRelation{:InvD})   = print(io, "[InvD]")
show(io::IO, ::UniversalIntervalRelation{:InvB})   = print(io, "[InvB]")

####################
# @@@ ALGEBRAS @@@ #
####################

# Abstract Algebra.
abstract type AbstractAlgebra end

# (Generic) Algebra.
struct Algebra <: AbstractAlgebra
    name::String
    domain::Vector{Float64}
    meet::Function
    join::Function
    impl::Function
    # eq::Function
    # lt::Function
    bottom::Float64
    top::Float64
end

_name(a::Algebra)   = a.name
_domain(a::Algebra) = a.domain
_meet(a::Algebra)   = a.meet
_join(a::Algebra)   = a.join
_impl(a::Algebra)   = a.impl
# _eq(a::Algebra)     = a.eq
# _lt(a::Algebra)     = a.lt
_bottom(a::Algebra) = a.bottom
_top(a::Algebra)    = a.top

# Boolean and: ∧ (\wedge).
∧(a::Bool,b::Bool)          = ifelse(a == true && a == b, true, false)
∧(a::Int64,b::Int64)        = ifelse(a == 1 && a == b, 1, 0)
∧(a::Float64,b::Float64)    = ifelse(a == 1.0 && a == b, 1.0, 0.0)
∧(a,b,args...)              = ∧(∧(a,b),args...)

# Boolean or: ∨ (\vee).
∨(a::Bool,b::Bool)          = ifelse(a == false && a == b, false, true)
∨(a::Int64,b::Int64)        = ifelse(a == 0 && a == b, 0, 1)
∨(a::Float64,b::Float64)    = ifelse(a == 0.0 && a == b, 0.0, 1.0)
∨(a,b,args...)              = ∨(∨(a,b),args...)

# Boolean implication: → (\to).
→(a::Bool,b::Bool)          = ifelse(a == true && b == false, false, true)
→(a::Int64,b::Int64)        = ifelse(a == 1 && b == 0, 0, 1)
→(a::Float64,b::Float64)    = ifelse(a == 1.0 && b == 0.0, 0.0, 1.0)
→(a,b,args...)              = →(→(a,b),args...)

# Heyting chain meet: ⊓ (\sqcap).
⊓(args...)                 = min(args...)

# Heyting chain join: ⊔ (\sqcup).
⊔(args...)                 = max(args...)

# Heyting chain implication: ↦ (\mapsto).
(↦)(a::Float64, b::Float64) = a ≤ b ? float(1) : b

const BooleanAlgebra = Algebra("Boolean Algebra",[0.0,1.0],∧,∨,→,0.0,1.0)
const HeytingChainAlgebra = Algebra("Heyting Chain Algebra", [x for x ∈ 0.0:0.1:1.0],min,max,↦,0.0,1.0) # TODO: 0.0:0.1:1.0 is good as range ?

###########################
# @@@ End of ALGEBRAS @@@ #
###########################

########################
# @@@ PROPOSITIONS @@@ #
########################

abstract type AbstractProposition end

struct Proposition <: AbstractProposition
    A::Int
    rel::Function
    a::Float64
end

show(io::IO, p::Proposition) = print(io, "A", p.A, p.rel, p.a)

###############################
# @@@ End of PROPOSITIONS @@@ #
###############################

#########################
# @@@ FORMULA NODES @@@ #
#########################

abstract type Node end

mutable struct FormulaNode{T} <: Node
    data::T
    parent::FormulaNode
    left::FormulaNode
    right::FormulaNode

    # Root constructor.
    FormulaNode{T}(data::T) where T = new{T}(data)
end

FormulaNode(data::T) where T = FormulaNode{T}(data)
_datatype(::FormulaNode{T}) where T = T
_parent(ν::FormulaNode) = ν.parent
_left(ν::FormulaNode) = ν.left
_right(ν::FormulaNode) = ν.right
_parent!(ν::FormulaNode, p::FormulaNode) = ν.parent = p
_left!(ν::FormulaNode, l::FormulaNode) = ν.left = l
_right!(ν::FormulaNode, r::FormulaNode) = ν.right = r

function _size(ν::FormulaNode)
    leftsz = isdefined(ν, :left) ? _size(ν.left) : 0
    rightsz = isdefined(ν, :right) ? _size(ν.right) : 0
    # Modal relations count +2 because they are, e.g., ◊(φ₁ ∧ φ₂) so that +1 for the ◊ and +1 for the ∧;
    # then recursively compute the size for φ₁ and φ₂.
    sz = 0
    if typeof(ν.data) != ExistentialIntervalRelation{:G} && typeof(ν.data) != UniversalIntervalRelation{:G} && _datatype(ν) <: AbstractModalRelation
        sz = sz + 2
    else
        sz = sz + 1
    end
    return sz + leftsz + rightsz
end

function _upwards_md(ν::FormulaNode)
    md = isdefined(ν, :parent) ? _upwards_md(ν.parent) : 0
    md += _datatype(ν) <: AbstractModalRelation ? 1 : 0
    return md
end

function _upwards_depth(ν::FormulaNode)
    depth = isdefined(ν, :parent) ? _upwards_depth(_parent(ν)) : 0
    return depth + 1
end

function _isleaf(ν::FormulaNode)
    return (!isdefined(ν, :left) && !isdefined(ν, :right) ? true : false)
end

################################
# @@@ End of FORMULA NODES @@@ #
################################

################################
# @@@ CLASSIFICATION RULES @@@ #
################################

# Abstract rule.
abstract type AbstractRule end

# Classification rule.
mutable struct ClassificationRule <: AbstractRule
    antecedent::FormulaNode
    consequent::String
end

_antecedent(ρ::ClassificationRule)                  = ρ.antecedent
_consequent(ρ::ClassificationRule)                  = ρ.consequent
_antecedent!(ρ::ClassificationRule, a::FormulaNode) = ρ.antecedent = a
_consequent!(ρ::ClassificationRule, c::String)      = ρ.consequent = c

_size(ρ::ClassificationRule) = _size(_antecedent(ρ)) + 2 # +2 for the ⟹ and the class.

function _print_rule(io::IO, ρ::ClassificationRule)
    _print_antecedent(io, _antecedent(ρ))
    print(io, " ⟹  ")
    _print_consequent(io, _consequent(ρ))
    println(io)
    return
end

function _print_antecedent(io::IO, ν::FormulaNode)
    if _datatype(ν) <: AbstractUniversalIntervalRelation # TODO: use traits to ask isexistential(ν) or isuniversal(ν)
        print(io, ν.data)
        print(io, "(")
        if isdefined(ν, :left) && isdefined(ν, :right)
            _print_antecedent(io, ν.left)
            print(io, " ", implication, " ")
            _print_antecedent(io, ν.right)
        elseif isdefined(ν, :right)
            _print_antecedent(io, ν.right)
        end
        print(io, ")")
        return
    elseif _datatype(ν) <: AbstractExistentialIntervalRelation # TODO
        print(io, ν.data)
        print(io, "(")
        if isdefined(ν, :left) && isdefined(ν, :right)
            _print_antecedent(io, ν.left)
            print(io, " ", conjunction, " ")
            _print_antecedent(io, ν.right)
        elseif isdefined(ν, :right)
            _print_antecedent(io, ν.right)
        end
        print(io, ")")
        return
    elseif _datatype(ν) <: AbstractBinaryRelation
        print(io, "(")
        _print_antecedent(io, ν.left)
        print(io, " ", ν.data, " ")
        _print_antecedent(io, ν.right)
        print(io, ")")
        return
    else
        print(io, ν.data)
        return
    end
end

function _print_consequent(io::IO, c::String)
    print(io, c)
end

# function _collect_subformulas(ρ::ClassificationRule)
#     sub = FormulaNode[]
#     _collect_subformulas(_antecedent(ρ), sub)
#     return collect(Set(sub))
# end

# function _collect_subformulas(ν::FormulaNode, sub::Vector{FormulaNode})
#     push!(sub, ν)
#     if isdefined(ν, :left) && isdefined(ν, :right)
#         _collect_subformulas(ν.left, sub)
#         _collect_subformulas(ν.right, sub)
#     end
# end

function _collect_sorted_subformulas(ρ::ClassificationRule)
    return sort!(_collect_nodes(ρ), by=x -> _size(x), alg=Base.Sort.QuickSort)
end

function _collect_nodes(ρ::ClassificationRule)
    nodes = FormulaNode[]
    _collect_nodes(_antecedent(ρ), nodes)
    return nodes
end

function _collect_nodes(ν::FormulaNode, nodes::Vector{FormulaNode})
    if isdefined(ν, :left)
        _collect_nodes(ν.left, nodes)
    end
    if isdefined(ν, :right)
        _collect_nodes(ν.right, nodes)
    end
    push!(nodes, ν)
end

show(io::IO, ν::FormulaNode) = _print_antecedent(io, ν)
show(io::IO, ρ::ClassificationRule) = _print_rule(io, ρ)

function _rand_antecedent(init_relations::Vector{<:AbstractRelation},
        relations::Vector{<:AbstractRelation},
        algebra::Algebra,
        ds::ClassificationDataset,
        orders::Vector{Function};
        minmd::Int=1,
        maxmd::Int=3,
        maxdepth::Int=4,
        isroot::Bool=true)

    if maxmd ≤ 0 || maxdepth ≤ 0 | (rand() ≤ 0.3 && minmd ≤ 0) # maxmd ≤ 0 || maxdepth ≤ 0 | (rand() ≤ 0.2 && minmd ≤ 0)
        case = rand(1:100)
        # Proposition case.
        if case ≤ 90
            return FormulaNode(_rand_proposition(length(attributes(ds)[1]),orders,ds.domains[1])) # TODO: works only for the first frame [1]
        # Algebra value case.
        else
            return FormulaNode(rand(_domain(algebra)))
        end
    end

    if isroot
        root = FormulaNode(init_relations[rand(1:length(init_relations))])
        minmd = minmd - 1
        maxmd = maxmd - 1
        maxdepth = maxdepth - 1
        rchild = _rand_antecedent(init_relations,relations,algebra,ds,orders;minmd=minmd,maxmd=maxmd,maxdepth=maxdepth,isroot=false)
        _right!(root, rchild)
        _parent!(rchild, root)
    else
        rel = relations[rand(1:length(relations))]
        root = FormulaNode(rel)
        if typeof(rel) <: AbstractBinaryRelation
            maxdepth = maxdepth - 1
        elseif typeof(rel) <: AbstractModalRelation
            minmd = minmd - 1
            maxmd = maxmd - 1
            maxdepth = maxdepth - 1
        end
        lchild = _rand_antecedent(init_relations,relations,algebra,ds,orders;minmd=minmd,maxmd=maxmd,maxdepth=maxdepth,isroot=false)
        rchild = _rand_antecedent(init_relations,relations,algebra,ds,orders;minmd=minmd,maxmd=maxmd,maxdepth=maxdepth,isroot=false)
        _left!(root, lchild)
        _right!(root, rchild)
        _parent!(lchild, root)
        _parent!(rchild, root)
    end
    return root
end

function _rand_proposition(num_attributes::Int,
        orders::Vector{Function},
        domains::Dict{Symbol, Vector{Number}})
    i = rand(1:num_attributes)
    return Proposition(i, orders[rand(1:length(orders))], domains[Symbol("A" * string(i))][rand(1:length(domains[Symbol("A" * string(i))]))])
end

function _rand_consequent(classes::Vector{String})
    return classes[rand(1:length(classes))]
end

function _rand_rule(init_relations::Vector{<:AbstractRelation},
        relations::Vector{<:AbstractRelation},
        algebra::Algebra,
        ds::ClassificationDataset,
        orders::Vector{Function};
        minmd::Int=1,
        maxmd::Int=3,
        maxdepth::Int=4,
        isroot::Bool=true)
    antecedent = _rand_antecedent(init_relations,relations,algebra,ds,orders;minmd,maxmd,maxdepth,isroot)
    consequent = _rand_consequent(ds.unique_classes)
    return ClassificationRule(antecedent,consequent)
end

mutable struct ClassificationRules
    rules::Vector{ClassificationRule}
    horizon::Int # TODO: works only for specific functions; for example, in the future, add α and β for sigmoid-like functions
    init_relations::Vector{<:AbstractRelation}
    relations::Vector{<:AbstractRelation}
    algebra::Algebra
    ds::ClassificationDataset
    orders::Vector{Function}
    minmd::Int
    maxmd::Int
    minnumrules::Int
    maxnumrules::Int
    minh::Int
    maxh::Int
    maxdepth::Int
end

_rules(Γ::ClassificationRules) = Γ.rules
_horizon(Γ::ClassificationRules) = Γ.horizon
_horizon!(Γ::ClassificationRules, h::Int) = Γ.horizon = h
_init_relations(Γ::ClassificationRules) = Γ.init_relations
_classes(Γ::ClassificationRules) = collect(Set([_consequent(ρ) for ρ in Γ]))
_complexity(Γ::ClassificationRules) = reduce(+, [_size(ρ) for ρ in Γ])

# Defined, otherwise it errs.
getindex(Γ::ClassificationRules, inds...) = getindex(_rules(Γ), inds...)
setindex(Γ::ClassificationRules, ρ::ClassificationRule, i::Int) = Γ[i] = ρ
length(Γ::ClassificationRules) = length(_rules(Γ))
iterate(Γ::ClassificationRules) = iterate(_rules(Γ))
iterate(Γ::ClassificationRules, i::Int) = iterate(_rules(Γ), i)
firstindex(Γ::ClassificationRules) = 1
lastindex(Γ::ClassificationRules) = length(Γ)
ndims(::Type{ClassificationRules}) = 1
size(Γ::ClassificationRules) = (length(Γ),)
copyto!(Γ::ClassificationRules, b::Base.Broadcast.Broadcasted) = copyto!(_rules(Γ), b)

function _rand_rules(init_relations::Vector{<:AbstractRelation},
    relations::Vector{<:AbstractRelation},
    algebra::Algebra,
    ds::ClassificationDataset,
    orders::Vector{Function};
    minmd::Int=1,
    maxmd::Int=3,
    minnumrules::Int=2,
    maxnumrules::Int=10,
    minh::Int=1,
    maxh::Int=4,
    maxdepth::Int=4)
    numrules = rand(minnumrules:maxnumrules)
    rules = ClassificationRule[]
    horizon = rand(minh:maxh)
    for _ in 1:numrules
        push!(rules, _rand_rule(init_relations,relations,algebra,ds,orders;minmd=minmd,maxmd=maxmd,maxdepth=maxdepth,isroot=true))
    end
    return ClassificationRules(rules,horizon,init_relations,relations,algebra,ds,orders,minmd,maxmd,minnumrules,maxnumrules,minh,maxh,maxdepth)
end

function _print_rules(io::IO, Γ::ClassificationRules)
    for i in 1:length(Γ)
        _print_rule(io, Γ[i])
    end
end

function show(io::IO, Γ::ClassificationRules)
    println(io, "Horizon = ", _horizon(Γ), "\n")
    _print_rules(io, Γ)
end

#######################################
# @@@ End of CLASSIFICATION RULES @@@ #
#######################################

##################
# @@@ TRAITS @@@ #
##################

# Ontological traits.
abstract type OntologicalTrait end
struct PropositionalTrait   <: OntologicalTrait end
struct PointTrait           <: OntologicalTrait end
struct IntervalTrait        <: OntologicalTrait end

# Default behavior is propositional.
OntologicalTrait(::Type) = PropositionalTrait()
# Propositional trait.
OntologicalTrait(::Type{<:AbstractPropositionalRelation})   = PropositionalTrait()
# Point trait.
OntologicalTrait(::Type{<:AbstractPointRelation})           = PointTrait()
# Interval trait.
OntologicalTrait(::Type{<:AbstractIntervalRelation})        = IntervalTrait()

ispropositional(x::T) where {T}             = ispropositional(OntologicalTrait(T), x)
ispropositional(::PropositionalTrait, x)    = true
ispropositional(::OntologicalTrait, x)      = false

ispoint(x::T) where {T}     = ispoint(OntologicalTrait(T), x)
ispoint(::PointTrait, x)    = true
ispoint(::OntologicalTrait) = false

isinterval(x::T) where {T}          = isinterval(OntologicalTrait(T), x)
isinterval(::IntervalTrait, x)      = true
isinterval(::OntologicalTrait, x)   = false

#########################
# @@@ End of TRAITS @@@ #
#########################

Base.:(==)(x::Proposition, y::Proposition) = x.A == y.A && x.rel == y.rel && x.a == y.a
Base.:(==)(x::AbstractRelation, y::AbstractRelation) = typeof(x) == typeof(y)
Base.:(==)(x::FormulaNode, y::FormulaNode) = _isequal(x, y)
Base.:(==)(ρ₁::ClassificationRule, ρ₂::ClassificationRule) = _isequal(_antecedent(ρ₁), _antecedent(ρ₂)) && _consequent(ρ₁) == _consequent(ρ₂)
function _isequal(x::FormulaNode, y::FormulaNode)
    if isdefined(x, :left) && isdefined(x, :right) && isdefined(y, :left) && isdefined(y, :right)
        return x.data == y.data && _isequal(x.left, y.left) && _isequal(x.right, y.right)
    elseif !isdefined(x, :left) && isdefined(x, :right) && !isdefined(y, :left) && isdefined(y, :right)
        return x.data == y.data && _isequal(x.right, y.right)
    elseif !isdefined(x, :left) && !isdefined(x, :right) && !isdefined(y, :left) && !isdefined(y, :right)
        return x.data == y.data
    end
    return false
end

# TODO: remove?
function Base.:(==)(Γ₁::ClassificationRules, Γ₂::ClassificationRules)
    if length(_rules(Γ₁)) == length(_rules(Γ₂))
        res = true
        # SS = Set(_rules(S))
        for i ∈ 1:length(_rules(Γ₂))
            (!(Γ₁[i] ∈ Γ₂)) && (res = false)
        end
        # (Set(rules(R)) != Set(rules(S))) && (res = false)
        (classes(Γ₁) != classes(Γ₂)) && (res = false)
        return res
    else
        return false
    end
end

hash(p::Proposition) = hash(p.A) + hash(p.rel) + hash(p.a)
function hash(x::FormulaNode)
    if isdefined(x, :left) && isdefined(x, :right)
        return hash(x.data) + hash(x.left) + hash(x.right)
    elseif !isdefined(x, :left) && isdefined(x, :right)
        return hash(x.data) + hash(x.right)
    elseif !isdefined(x, :left) && !isdefined(x, :right)
        return hash(x.data)
    end
end
hash(ρ::ClassificationRule) = hash(_antecedent(ρ)) + hash(_consequent(ρ))
hash(Γ::ClassificationRules) = reduce(+, [hash(ρ) for ρ in Γ])

hash(x::Tuple{FormulaNode, Tuple{Int64, Int64}}) = hash(x[1]) + hash(x[2][1]) + hash(x[2][2]) # TODO: needed?

ext: jl


text:
using TemporalGPs: Gaussian

# This is a ridiculous definition that makes no sense. Don't use this anywhere.
Base.zero(x::Gaussian) = Gaussian(zero(x.m), zero(x.P))

function fd_isapprox(x_ad::Gaussian, x_fd::Gaussian, rtol, atol)
    return fd_isapprox(x_ad.m, x_fd.m, rtol, atol) &&
        fd_isapprox(x_ad.P, x_fd.P, rtol, atol)
end

@testset "Gaussian" begin
    N = 11
    @test TemporalGPs.dim(Gaussian(randn(N), randn(N, N))) == N

    @testset "Reversion test for #56" begin
        x = Gaussian(SVector{3}(randn(3)), zeros(3, 3))
        @test rand(x) ≈ mean(x) rtol=1e-4
    end
end


text:
using ImageFiltering, OffsetArrays
using Base.Test

@testset "1d" begin
    # Cascades don't result in out-of-bounds values
    img = 1:8
    k1 = centered([0.25, 0.5, 0.25])
    k2 = OffsetArray([0.5, 0.5], 1:2)
    casc = imfilter(img, (k1, k2, k1))
    A0 = padarray(img, Pad(:replicate, (2,), (4,)))
    A1 = imfilter(A0, k1, Inner())
    @test_approx_eq A1 OffsetArray([1.0,1.25,2.0,3.0,4.0,5.0,6.0,7.0,7.75,8.0,8.0,8.0], 0:11)
    A2 = imfilter(A1, k2, Inner())
    @test_approx_eq A2 OffsetArray([1.625,2.5,3.5,4.5,5.5,6.5,7.375,7.875,8.0,8.0], 0:9)
    A3 = imfilter(A2, k1, Inner())
    @test_approx_eq casc A3
    @test size(casc) == size(img)
    # copy! kernels, presence/order doesn't matter
    kc = centered([1])
    @test ImageFiltering.iscopy(kc)
    @test_approx_eq imfilter(img, (k1,)) [1.25,2.0,3.0,4.0,5.0,6.0,7.0,7.75]
    @test_approx_eq imfilter(img, (kc, k1)) [1.25,2.0,3.0,4.0,5.0,6.0,7.0,7.75]
    @test_approx_eq imfilter(img, (k1, kc)) [1.25,2.0,3.0,4.0,5.0,6.0,7.0,7.75]
end

@testset "3d" begin
    img = trues(10,10,10)
    kernel = centered(trues(3,3,3)/27)
    for border in ("replicate", "circular", "symmetric", "reflect", Fill(true))
        for alg in (Algorithm.FIR(), Algorithm.FFT())
            @test_approx_eq imfilter(img, kernel, border) img
        end
    end
    # Fill(0)
    target = convert(Array{Float64}, img)
    for i in (1,10)
        target[:,:,i] = target[:,i,:] = target[i,:,:] = 2/3
    end
    for i in (1,10)
        for j in (1,10)
            target[:,i,j] = target[i,:,j] = target[i,j,:] = (2/3)^2
        end
    end
    for i in (1,10)
        for j in (1,10)
            for k in (1,10)
                target[i,j,k] = (2/3)^3
            end
        end
    end
    @test_approx_eq imfilter(img, kernel, Fill(0)) target
    @test_approx_eq imfilter(img, kernel, Fill(0), Algorithm.FFT()) target
    # Inner
    @test_approx_eq imfilter(img, kernel, Inner()) OffsetArray(trues(8,8,8), 2:9, 2:9, 2:9)
end

nothing


text:
# Compose pseudo-forms for simple symbols, all parameterized by center and size

using Compose: x_measure, y_measure

function square(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
    n = max(length(xs), length(ys), length(rs))

    rect_xs = Vector{Measure}(n)
    rect_ys = Vector{Measure}(n)
    rect_ws = Vector{Measure}(n)
    s = 1/sqrt(2)
    for i in 1:n
        x = x_measure(xs[1 + i % length(xs)])
        y = y_measure(ys[1 + i % length(ys)])
        r = rs[1 + i % length(rs)]

        rect_xs[i] = x - s*r
        rect_ys[i] = y - s*r
        rect_ws[i] = 2*s*r
    end

    return rectangle(rect_xs, rect_ys, rect_ws, rect_ws)
end

function diamond(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
    n = max(length(xs), length(ys), length(rs))

    polys = Vector{Vector{Tuple{Measure, Measure}}}(n)
    for i in 1:n
        x = x_measure(xs[1 + i % length(xs)])
        y = y_measure(ys[1 + i % length(ys)])
        r = rs[1 + i % length(rs)]
        polys[i] = Tuple{Measure, Measure}[(x, y - r), (x + r, y), (x, y + r), (x - r, y)]
    end

    return polygon(polys)
end

function cross(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
  n = max(length(xs), length(ys), length(rs))
  polys = Vector{Vector{Tuple{Measure, Measure}}}(n)
  for i in 1:n
    x = x_measure(xs[mod1(i, length(xs))])
    y = y_measure(ys[mod1(i, length(ys))])
    r = rs[mod1(i, length(rs))]
    u = 0.4r

    # make a "plus sign"
    polys[i] = Tuple{Measure, Measure}[
      (x-r, y-u), (x-r, y+u), # L edge
      (x-u, y+u),             # BL inside
      (x-u, y+r), (x+u, y+r), # B edge
      (x+u, y+u),             # BR inside
      (x+r, y+u), (x+r, y-u), # R edge
      (x+u, y-u),             # TR inside
      (x+u, y-r), (x-u, y-r), # T edge
      (x-u, y-u)              # TL inside
    ]
  end

  return polygon(polys)
end

function xcross(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
  n = max(length(xs), length(ys), length(rs))
  polys = Vector{Vector{Tuple{Measure, Measure}}}(n)
  s = 1/sqrt(5)
  for i in 1:n
    x = x_measure(xs[mod1(i, length(xs))])
    y = y_measure(ys[mod1(i, length(ys))])
    r = rs[mod1(i, length(rs))]
    u = s*r
    polys[i] = Tuple{Measure, Measure}[
      (x, y - u), (x + u, y - 2u), (x + 2u, y - u),
      (x + u, y), (x + 2u, y + u), (x + u, y + 2u),
      (x, y + u), (x - u, y + 2u), (x - 2u, y + u),
      (x - u, y), (x - 2u, y - u), (x - u, y - 2u)
    ]
  end

  return polygon(polys)
end

function utriangle(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray, scalar = 1)
  n = max(length(xs), length(ys), length(rs))
  polys = Vector{Vector{Tuple{Measure, Measure}}}(n)
  s = 0.8
  for i in 1:n
    x = x_measure(xs[mod1(i, length(xs))])
    y = y_measure(ys[mod1(i, length(ys))])
    r = rs[mod1(i, length(rs))]
    u = 0.8 * scalar * r
    polys[i] = Tuple{Measure, Measure}[
      (x - r, y + u),
      (x + r, y + u),
      (x, y - u)
    ]
  end

  return polygon(polys)
end

function dtriangle(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
    utriangle(xs, ys, rs, -1)
end

function star1(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray, scalar = 1)
  n = max(length(xs), length(ys), length(rs))
  polys = Vector{Vector{Tuple{Measure, Measure}}}(n)

  # some magic scalars
  sx = 0.7
  sy1, sy2 = 1.2, 0.4

  for i in 1:n
    x = x_measure(xs[mod1(i, length(xs))])
    y = y_measure(ys[mod1(i, length(ys))])
    r = rs[mod1(i, length(rs))]
    polys[i] = Tuple{Measure, Measure}[
      (x-sx*r, y+r),      # BL
      (x,      y-sy1*r),  # T
      (x+sx*r, y+r),      # BR
      (x-r,    y-sy2*r),  # L
      (x+r,    y-sy2*r)   # R
    ]
  end

  return polygon(polys)
end

function star2(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray, scalar = 1)
  n = max(length(xs), length(ys), length(rs))
  polys = Vector{Vector{Tuple{Measure, Measure}}}(n)
  for i in 1:n
    x = x_measure(xs[mod1(i, length(xs))])
    y = y_measure(ys[mod1(i, length(ys))])
    r = rs[mod1(i, length(rs))]
    u = 0.4r
    polys[i] = Tuple{Measure, Measure}[
      (x-u, y),   (x-r, y-r), # TL
      (x,   y-u), (x+r, y-r), # TR
      (x+u, y),   (x+r, y+r), # BR
      (x,   y+u), (x-r, y+r)  # BL
    ]
  end

  return polygon(polys)
end

function hexagon(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
  n = max(length(xs), length(ys), length(rs))

  polys = Vector{Vector{Tuple{Compose.Measure, Compose.Measure}}}(n)
  for i in 1:n
    x = Compose.x_measure(xs[mod1(i, length(xs))])
    y = Compose.y_measure(ys[mod1(i, length(ys))])
    r = rs[mod1(i, length(rs))]
    u = 0.6r

    polys[i] = Tuple{Compose.Measure, Compose.Measure}[
      (x-r, y-u), (x-r, y+u), # L edge
      (x, y+r),               # B
      (x+r, y+u), (x+r, y-u), # R edge
      (x, y-r)                # T
    ]
  end

  return Gadfly.polygon(polys)
end

function octagon(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
  n = max(length(xs), length(ys), length(rs))

  polys = Vector{Vector{Tuple{Measure, Measure}}}(n)
  for i in 1:n
    x = x_measure(xs[mod1(i, length(xs))])
    y = y_measure(ys[mod1(i, length(ys))])
    r = rs[mod1(i, length(rs))]
    u = 0.4r

    polys[i] = Tuple{Measure, Measure}[
      (x-r, y-u), (x-r, y+u), # L edge
      (x-u, y+r), (x+u, y+r), # B edge
      (x+r, y+u), (x+r, y-u), # R edge
      (x+u, y-r), (x-u, y-r)  # T edge
    ]
  end

  return polygon(polys)
end


text:
module LatticeSites

using StaticArrays, Random

include("spins.jl")
include("space.jl")
include("flip.jl")
include("lattices.jl")

end


text:
"""
    swapping_even_odd_bits(n::Int64))

For a given integer value, all odd bits will be swapped with even bits.

# Arguments
- `n::Int64`: Integer value of the number, which has to be swapped.

# Examples
```julia-repl
julia> import ClassicAlgorithmsCollections
julia> ClassicAlgorithmsCollections.function swapping_even_odd_bits(23)
43
```

# Notes
---
The definition of the even and odd bits:
```julia-repl
even_bits = n & 0xAAAAAAAA
odd_bits = n & 0x55555555
```
"""
function swapping_even_odd_bits(n::Int64)
    # Defining even and odd bits
    even_bits = n & 0xAAAAAAAA
    odd_bits = n & 0x55555555

    # Shifting the even and odd bits
    even_bits >>= 1
    odd_bits <<= 1
    return (even_bits | odd_bits)
end

max_stars_repo_name: Anselmoo/ClassicAlgorithmsCollections

max_issues_repo_name: Anselmoo/ClassicAlgorithmsCollections

max_forks_repo_name: Anselmoo/ClassicAlgorithmsCollections


text:
function ellipse_traj(x_start, x_goal, z, T)
	dist = x_goal - x_start
	a = 0.5 * dist
	b = z
	z̄ = 0.0
	# x = range(x_start, stop = x_goal, length = T)
	x = circular_projection_range(x_start, stop = x_goal, length = T)
	z = sqrt.(max.(0.0, (b^2) * (1.0 .- ((x .- (x_start + a)).^2.0) / (a^2.0))))
	return x, z
end

function circular_projection_range(start; stop=1.0, length=10)
	dist = stop - start
	θr = range(π, stop=0, length=length)
	r = start .+ dist * ((1 .+ cos.(θr))./2)
	return r
end


text:
"""
    predict(method, model, input[; device, context])

Predict a `target` from `input` using `model`. Optionally apply function `device`
to `x` before passing to `model` and use `context` instead of the default
context [`Inference`](#).
"""
function predict(method, model, input; device = identity, undevice = identity, context = Inference())
    if shouldbatch(method)
        return predictbatch(method, model, [input,]; device = device, undevice = undevice, context = context) |> only
    else
        return decodeŷ(method, context, undevice(model(device(encodeinput(method, context, input)))))
    end
end

"""
    predictbatch(method, model, inputs[; device, context])

Predict `targets` from a vector of `inputs` using `model` by batching them.
Optionally apply function `device` to batch before passing to `model` and
use `context` instead of the default [`Inference`](#).
"""
function predictbatch(method, model, inputs; device = identity, undevice = identity, context = Inference())
    xs = device(DataLoaders.collate([copy(encodeinput(method, context, input)) for input in inputs]))
    ŷs = undevice(model(xs))
    targets = [decodeŷ(method, context, ŷ) for ŷ in DataLoaders.obsslices(ŷs)]
    return targets
end

function _predictx(method, model, x, device = identity)
    if shouldbatch(method)
        x = DataLoaders.collate([x])
    end
    ŷs = device(model)(device(x))
    if shouldbatch(method)
        ŷ = ŷs[((:) for _ in 1:ndims(ŷs)-1)..., 1]
    end
    return ŷ
end


text:
"Types that can be queried."
typealias Queryable @compat(Union{Symbol, Expr, AbstractString})

"""
Holds the parsed user query.

**Fields:**

* `objects`: The objects that will be searched for in metadata.
* `mods`:    Modules to be searched through for documentation.
* `index`:   1-based, for picking an individual entry to display out of a list.
"""
type Query
    objects :: Vector
    modules :: Set{Module}
    index   :: Int

    Query(objects, modname, index) = new(objects, Set([modname]), index)
    Query(objects, index)          = new(objects, documented(), index)
end

function (==)(a::Query, b::Query)
    res = length(a.objects) == length(b.objects)
    for (x, y) in zip(a.objects, b.objects)
        res &= x == y
    end
    res &= isempty(setdiff(a.modules, b.modules))
    res &= a.index == b.index
end

"An entry and the set of all objects that are linked to it."
type Match
    entry   :: Entry
    objects :: Set

    Match(entry) = new(entry, Set())
end

Base.push!(match::Match, object) = push!(match.objects, object)

"Stores the matching entries resulting from running a query."
type QueryResults
    query   :: Query
    matches :: Dict{AbstractEntry, Match}
    scores  :: Dict{Float64, Set{AbstractEntry}}

    QueryResults(query) = new(query, Dict(), Dict())
end

function Base.push!(res::QueryResults, entry, object, score)
    score ≡ 0 && return
    if !haskey(res.matches, entry)
        res.matches[entry] = Match(entry)
        if haskey(res.scores, score)
            push!(res.scores[score], entry)
        else
            res.scores[score] = Set{AbstractEntry}([entry])
        end
    end
    push!(res.matches[entry], object)
end

Base.push!(res::QueryResults, entry::Entry{:comment}, object, score) = res

"""
Create a `Query` object from the provided `args`.
The resulting query can then be `run` to retrieve matching results from currently loaded
documentation.

This is a low-level interface. For everyday usage in the REPL rather use the
built-in `?` mode, which Lexicon hooks into automatically.

The first argument must be the expression to search for. Supported expressions
include method calls, macros, constants, types, functions, and strings. An
optional integer argument may be used to only show documentation from one of
several results.

**Example:**

```julia
q = @query Lexicon.@query
run(q)
```

```julia
q = @query Lexicon.save 2
run(q)
```

**Note:** When searching documentation for an operator (`+`, `-`, etc.) it should
be enclosed in parentheses:

```julia
q = @query (+) 4
run(q)
```
"""
macro query(args...) esc(query(args...)) end

query(ex::Queryable, index = 0) = build(objects(ex), modname(ex), index)
query(other...) = throw(ArgumentError("Invalid arguments: query($(join(other, ", "))."))

build(objects, mod, index)       = :(Lexicon.Query([$(objects...)], $(mod), $(index)))
build(objects, ::@compat(Void), index) = :(Lexicon.Query([$(objects...)], $(index)))

immutable Head{S} end

if VERSION < v"0.4.0-dev"
    macro H_str(text)
        heads = [Head{symbol(t)} for t in split(text, ", ")]
        Expr(:(::), Expr(:call, :Union, heads...))
    end
else
    macro H_str(text)
        heads = [Head{symbol(t)} for t in split(text, ", ")]
        Expr(:(::), Expr(:curly, :Union, heads...))
    end
end

modname(ex::Expr) = modname(Head{ex.head}(), ex)
modname(other)    = nothing

modname(H"call, macrocall", ex) = modname(ex.args[1])
modname(H".", ex)               = ex.args[1]

objects(ex::Expr) = objects(Head{ex.head}(), ex)

objects(s::AbstractString) = [s]
objects(s::Symbol)         = [s, quot(s)]
objects(H"quote", ex)      = [ex.args[1], ex]

objects(H".", ex) = [ex, ex.args[2]]

objects(H"call", ex) = [:(@which($(ex)))]

objects(H"macrocall", ex) =
    isexpr(ex.args[1], :(.)) ?
    [:(getfield($(modname(ex)), $(ex.args[1].args[2])))] :
    [ex.args[1]]

# Tuple overhaul.
if VERSION ≥ v"0.4-4319"
    tuple_collect(sig) = collect(sig.parameters)
else
    tuple_collect(sig) = collect(sig)
end

function partial_signature_matching(f::Function, sig)
    ms = Set{Method}()
    for m in methods(f)
        msig = tuple(tuple_collect(m.sig)...)
        if length(msig) > 0 && msig[1] == typeof(f)
            startof = 2
        else
            startof = 1
        end
        for n = 1:length(msig)
            isequal(typeintersect(msig[startof:n], sig), @compat(Union{})) || push!(ms, m)
        end
    end
    ms
end

"""
Search loaded documentation for methods of generic function `f` that match `sig`.
Optionally, provide an index (1-based) to view an individual entry if several different ones are
found.
"""
function query(f::Function, sig, index = 0)
    ms = collect(partial_signature_matching(f, sig))
    run(Query(ms, index))
end

function run(query::Query)
    res = QueryResults(query)
    for modname in query.modules, object in query.objects
        isdocumented(modname) && append_result!(res, object, metadata(modname))
    end
    res
end

function append_result!(res, query::AbstractString, meta)
    for (object, entry) in entries(meta)
        score = calculate_score(query, data(docs(entry)), writeobj(object, entry))
        push!(res, entry, object, score)
    end
end

"Basic text importance scoring."
calculate_score(query, text, object) = length(split(string(object, text), query)) - 1

function append_result!(res, func::Function, meta)
    ms = _isgeneric(func) ? Set(methods(func)) : Set{Method}()
    for (object, entry) in entries(meta)
        if object ≡ func
            push!(res, entry, object, 2)
        elseif object ∈ ms
            push!(res, entry, object, 1)
        end
    end
end

function append_result!(res, dt::DataType, meta)
    ms = Set(wrap_non_iterables(methods(mostgeneral(dt))))
    for (object, entry) in entries(meta)
        if object ≡ dt
            push!(res, entry, object, 2)
        elseif object ∈ ms
            push!(res, entry, object, 1)
        end
    end
end

append_result!(res, other, meta) = nothing

function append_result!(res, lsd :: LambdaStaticData, meta)
    for (object, entry) in entries(meta)
        if object == lsd
            push!(res, entry, object, 1)
        end
    end
end

mostgeneral(T::DataType) = T{[tvar.ub for tvar in T.parameters]...}

wrap_non_iterables(obj) = applicable(start, obj) ? obj : [obj]

typealias SimpleObject @compat(Union{Symbol, Method, Module})

function append_result!(res, object::SimpleObject, meta)
    if haskey(entries(meta), object)
        push!(res, entries(meta)[object], object, 1)
    end
end

const INDEXED = r"\s(\d+)$"

if VERSION < v"0.4-"
    function help(line)
        ex  = ismatch(INDEXED, line) ? nothing : parse("Base.Help.@help $(line)", raise = false)
        lex = parse("Lexicon.@query $(line)", raise = false)

        quote
            $(ex)
            println()
            run($(lex))
        end
    end
else
    function help(line)
        sline = symbol(line)
        iskey = isdefined(Base.Docs, :keywords) && haskey(Base.Docs.keywords, sline)

        ex, lex =
            # keywords can't be searched for in Lexicon.
            if iskey
                (:(Base.Docs.@repl $(sline)), nothing)
            else
                lex = :(run($(parse("Lexicon.@query $(line)", raise = false))))
                # Base REPL doesn't support indexing query results.
                if ismatch(INDEXED, line)
                    (nothing, lex)
                else
                    (parse("Base.Docs.@repl $(line)", raise = false), lex)
                end
            end

        # Check for string argument, not supported by base REPL. Call apropos.
        if isexpr(ex, :macrocall)
            ex =
                if length(ex.args) ≡ 2 && isa(ex.args[2], AbstractString)
                    :(Base.apropos($(ex.args[2])); println())
                else
                    result = gensym()
                    quote
                        $(result) = $(ex)
                        $(result) ≡ nothing || display($(result))
                    end
                end
        end

        quote
            try $(ex) catch end
            $(lex)
        end
    end
end

function setup_help()
    # Some environments, such as IJulia & Juno don't have an active repl.
    if isdefined(Base, :active_repl) && isa(Base.active_repl, Base.REPL.LineEditREPL)
        repl = Base.active_repl

        julia_mode = repl.interface.modes[1]
        help_mode  = repl.interface.modes[3]

        help_mode.on_done = Base.REPL.respond(repl, julia_mode) do line
            Lexicon.help(line)
        end
    end
end


text:
function _tdvp_compute_nsweeps(t; kwargs...)
  time_step::Number = get(kwargs, :time_step, t)
  nsweeps::Union{Int,Nothing} = get(kwargs, :nsweeps, nothing)
  if !isnothing(nsweeps) && time_step != t
    error("Cannot specify both time_step and nsweeps in tdvp")
  elseif isfinite(time_step) && abs(time_step) > 0.0 && isnothing(nsweeps)
    nsweeps = convert(Int, ceil(abs(t / time_step)))
    if !(nsweeps * time_step ≈ t)
      error("Time step $time_step not commensurate with total time t=$t")
    end
  end

  return nsweeps
end

function _extend_sweeps_param(param, nsweeps)
  if param isa Number
    eparam = fill(param, nsweeps)
  else
    length(param) == nsweeps && return param
    eparam = Vector(undef, nsweeps)
    eparam[1:length(param)] = param
    eparam[(length(param) + 1):end] .= param[end]
  end
  return eparam
end

function process_sweeps(; kwargs...)
  nsweeps = get(kwargs, :nsweeps, 1)
  maxdim = get(kwargs, :maxdim, fill(typemax(Int), nsweeps))
  mindim = get(kwargs, :mindim, fill(1, nsweeps))
  cutoff = get(kwargs, :cutoff, fill(1E-16, nsweeps))
  noise = get(kwargs, :noise, fill(0.0, nsweeps))

  maxdim = _extend_sweeps_param(maxdim, nsweeps)
  mindim = _extend_sweeps_param(mindim, nsweeps)
  cutoff = _extend_sweeps_param(cutoff, nsweeps)
  noise = _extend_sweeps_param(noise, nsweeps)

  return (; maxdim, mindim, cutoff, noise)
end

function tdvp(solver, PH, t::Number, psi0::MPS; kwargs...)
  reverse_step = get(kwargs, :reverse_step, true)

  nsweeps = _tdvp_compute_nsweeps(t; kwargs...)
  maxdim, mindim, cutoff, noise = process_sweeps(; nsweeps, kwargs...)

  time_start::Number = get(kwargs, :time_start, 0.0)
  time_step::Number = get(kwargs, :time_step, t)
  order = get(kwargs, :order, 2)
  tdvp_order = TDVPOrder(order, Base.Forward)

  checkdone = get(kwargs, :checkdone, nothing)
  write_when_maxdim_exceeds::Union{Int,Nothing} = get(
    kwargs, :write_when_maxdim_exceeds, nothing
  )
  observer = get(kwargs, :observer!, NoObserver())
  step_observer = get(kwargs, :step_observer!, NoObserver())
  outputlevel::Int = get(kwargs, :outputlevel, 0)

  psi = copy(psi0)

  # Keep track of the start of the current time step.
  # Helpful for tracking the total time, for example
  # when using time-dependent solvers.
  # This will be passed as a keyword argument to the
  # `solver`.
  current_time = time_start

  for sw in 1:nsweeps
    if !isnothing(write_when_maxdim_exceeds) && maxdim[sw] > write_when_maxdim_exceeds
      if outputlevel >= 2
        println(
          "write_when_maxdim_exceeds = $write_when_maxdim_exceeds and maxdim(sweeps, sw) = $(maxdim(sweeps, sw)), writing environment tensors to disk",
        )
      end
      PH = disk(PH)
    end

    sw_time = @elapsed begin
      psi, PH, info = tdvp(
        tdvp_order,
        solver,
        PH,
        time_step,
        psi;
        kwargs...,
        current_time,
        reverse_step,
        sweep=sw,
        maxdim=maxdim[sw],
        mindim=mindim[sw],
        cutoff=cutoff[sw],
        noise=noise[sw],
      )
    end

    current_time += time_step

    update!(step_observer; psi, sweep=sw, outputlevel, current_time)

    if outputlevel >= 1
      print("After sweep ", sw, ":")
      print(" maxlinkdim=", maxlinkdim(psi))
      @printf(" maxerr=%.2E", info.maxtruncerr)
      print(" current_time=", round(current_time; digits=3))
      print(" time=", round(sw_time; digits=3))
      println()
      flush(stdout)
    end

    isdone = false
    if !isnothing(checkdone)
      isdone = checkdone(; psi, sweep=sw, outputlevel, kwargs...)
    elseif observer isa ITensors.AbstractObserver
      isdone = checkdone!(observer; psi, sweep=sw, outputlevel)
    end
    isdone && break
  end
  return psi
end

"""
    tdvp(H::MPO,psi0::MPS,t::Number; kwargs...)
    tdvp(H::MPO,psi0::MPS,t::Number; kwargs...)

Use the time dependent variational principle (TDVP) algorithm
to compute `exp(t*H)*psi0` using an efficient algorithm based
on alternating optimization of the MPS tensors and local Krylov
exponentiation of H.

Returns:
* `psi::MPS` - time-evolved MPS

Optional keyword arguments:
* `outputlevel::Int = 1` - larger outputlevel values resulting in printing more information and 0 means no output
* `observer` - object implementing the [Observer](@ref observer) interface which can perform measurements and stop early
* `write_when_maxdim_exceeds::Int` - when the allowed maxdim exceeds this value, begin saving tensors to disk to free memory in large calculations
"""
function tdvp(solver, H::MPO, t::Number, psi0::MPS; kwargs...)
  check_hascommoninds(siteinds, H, psi0)
  check_hascommoninds(siteinds, H, psi0')
  # Permute the indices to have a better memory layout
  # and minimize permutations
  H = ITensors.permute(H, (linkind, siteinds, linkind))
  PH = ProjMPO(H)
  return tdvp(solver, PH, t, psi0; kwargs...)
end

function tdvp(solver, t::Number, H, psi0::MPS; kwargs...)
  return tdvp(solver, H, t, psi0; kwargs...)
end

function tdvp(solver, H, psi0::MPS, t::Number; kwargs...)
  return tdvp(solver, H, t, psi0; kwargs...)
end

"""
    tdvp(Hs::Vector{MPO},psi0::MPS,t::Number; kwargs...)
    tdvp(Hs::Vector{MPO},psi0::MPS,t::Number, sweeps::Sweeps; kwargs...)

Use the time dependent variational principle (TDVP) algorithm
to compute `exp(t*H)*psi0` using an efficient algorithm based
on alternating optimization of the MPS tensors and local Krylov
exponentiation of H.

This version of `tdvp` accepts a representation of H as a
Vector of MPOs, Hs = [H1,H2,H3,...] such that H is defined
as H = H1+H2+H3+...
Note that this sum of MPOs is not actually computed; rather
the set of MPOs [H1,H2,H3,..] is efficiently looped over at
each step of the algorithm when optimizing the MPS.

Returns:
* `psi::MPS` - time-evolved MPS
"""
function tdvp(solver, Hs::Vector{MPO}, t::Number, psi0::MPS; kwargs...)
  for H in Hs
    check_hascommoninds(siteinds, H, psi0)
    check_hascommoninds(siteinds, H, psi0')
  end
  Hs .= ITensors.permute.(Hs, Ref((linkind, siteinds, linkind)))
  PHs = ProjMPOSum(Hs)
  return tdvp(solver, PHs, t, psi0; kwargs...)
end


text:
#=
two stage stochastic farming problem

sets:
i ∈ 1..n  crops

parameters:
m  ∈ ℝ₊  number of available plots
aᵢ ∈ ℝ₊  fixed cost per farm plot for i
bᵢ ∈ ℝ₊  purchase cost for i
cᵢ ∈ ℝ₊  selling price for i
dᵢ ∈ ℝ₊  contractual demand for i

uncertain parameters:
ξᵢ ∈ ℝ₊  crop yield per farm plot for i
where ξ ~ Ξ

variables:
xᵢ ∈ ℤ₊  number of farm plots for i (1st stage)
yᵢ ∈ P₊  amount of crop i to purchase (2nd stage)
zᵢ ∈ P₊  amount of crop i to sell (2nd stage)
where P₊ are nonnegative polynomials in ξ

objective:
min ∑ᵢ aᵢ xᵢ + 𝔼(ξ ~ Ξ)[∑ᵢ(bᵢ yᵢ - cᵢ zᵢ)]  minimize total expected cost

constraints:
∑ᵢ xᵢ ≤ m  available plots
xᵢ ξᵢ + yᵢ - zᵢ - dᵢ = 0, ∀i  balance inputs and outputs for i

optional linking constraints:
f - ∑ᵢ (yᵢ + zᵢ) ∈ P₊  capacity limit for all crops (f ∈ ℝ₊ is a parameter)

TODO
- CVaR objective or other
- if unlinked, variables can be partitioned when x is fixed, so solve separate subproblems
=#

struct TwoStageStochastic <: ExampleInstance
    n::Int
    halfdeg::Int
    linked::Bool # whether stage two decisions are linked or independent
    use_nat::Bool # use WSOS cone formulation, else SDP formulation
end

function build(inst::TwoStageStochastic)
    n = inst.n
    m = 2n

    # generate parameter values
    a = rand(n)
    b = 1 .+ rand(n)
    c = b - rand(n)
    d = 1 .+ rand(n)
    f = sum(d) + 0.2 * n * rand()

    # setup polynomial interpolation
    n0 = (inst.linked ? n : 1)
    dom = PolyUtils.BoxDomain{Float64}(zeros(n0), ones(n0))
    (U, pts, Ps, _, w) = PolyUtils.interpolate(dom, inst.halfdeg, get_quadr = true)
    w .*= (inst.linked ? 1 : 2^(n - 1))

    # build model
    model = JuMP.Model()
    JuMP.@variable(model, x[1:n], Int)
    JuMP.@variable(model, y[1:n, 1:U])
    JuMP.@variable(model, z[1:n, 1:U])

    JuMP.@objective(
        model,
        Min,
        JuMP.dot(a, x) + JuMP.dot(w, sum(b[i] * y[i, :] - c[i] * z[i, :] for i in 1:n))
    )

    JuMP.@constraint(model, x .>= 0)
    JuMP.@constraint(model, sum(x) <= m)

    add_wsos(aff) = (inst.use_nat ? add_wsos_nat : add_wsos_ext)(Ps, aff, model)

    for i in 1:n
        add_wsos(y[i, :])
        add_wsos(z[i, :])
        pts_i = (inst.linked ? pts[:, i] : vec(pts))
        JuMP.@constraint(model, pts_i * x[i] + y[i, :] - z[i, :] .- d[i] .== 0)
    end

    if inst.linked
        fyz = JuMP.@expression(model, f .- sum(y[i, :] + z[i, :] for i in 1:n))
        add_wsos(fyz)
    end

    # save for use in tests
    model.ext[:x] = x
    model.ext[:y] = y
    model.ext[:z] = z
    if inst.linked
        model.ext[:fyz] = fyz
    end
    model.ext[:Ps] = Ps

    return model
end

function test_extra(inst::TwoStageStochastic, model::JuMP.Model)
    stat = JuMP.termination_status(model)
    @test stat in OPT_OR_LIMIT
    (stat == MOI.OPTIMAL) || return

    tol = eps()^0.2
    x = JuMP.value.(model.ext[:x])
    @test x ≈ round.(Int, x) atol = tol rtol = tol
    # check WSOS feasibility
    Ps = model.ext[:Ps]
    y = JuMP.value.(model.ext[:y])
    z = JuMP.value.(model.ext[:z])
    @test all(check_nonneg(y[i, :] .+ tol, Ps) for i in 1:(inst.n))
    @test all(check_nonneg(z[i, :] .+ tol, Ps) for i in 1:(inst.n))
    if inst.linked
        fyz = JuMP.value.(model.ext[:fyz])
        @test check_nonneg(fyz .+ tol, Ps)
    end
    return
end


text:
export ft_readee

function ft_readee(ft_handle::Culong, wordoffset::Integer)
  @assert wordoffset >= 0
  value = Ref{Cushort}()
  ft_status = ccall((:FT_ReadEE, d2xx),
                     Cuint,
                     (Culong, Cuint, Ref{Cushort}),
                     ft_handle, wordoffset, value)
  checkstatus(ft_status)
  return value[]
end


text:
using Clang
using Clang.LibClang
using Test

@testset "nested_union" begin
    trans_unit = parse_header(joinpath(@__DIR__, "c", "union.h"))
    ctx = DefaultContext()
    push!(ctx.trans_units, trans_unit)

    # get root cursor
    root_cursor = getcursor(trans_unit)
    cursors = children(root_cursor)
    # case 1
    wrap!(ctx, cursors[1])
    expr = :(struct ANONYMOUS1_bar
                str::Cstring
                x::Cint
                y::Cfloat
                z::Cdouble
            end)
    Base.remove_linenums!(expr)
    @test ctx.common_buffer[:ANONYMOUS1_bar].items[1] == expr
    expr = :(struct nested_union
                bar::ANONYMOUS1_bar
            end)
    Base.remove_linenums!(expr)
    @test ctx.common_buffer[:nested_union].items[1] == expr
end


text:
import Gaius

import BenchmarkTools
import InteractiveUtils
import LinearAlgebra
import Random
import StructArrays
import Test
import VectorizationBase

using Random: shuffle
using StructArrays: StructArray
using Test: @testset, @test, @test_logs, @test_throws

include("test_suite_preamble.jl")

@info("VectorizationBase.num_cores() is $(VectorizationBase.num_cores())")

include("block_operations.jl")
include("public_mul_coverage.jl")
include("init.jl")
include("kernels.jl")
include("matmul_coverage.jl")

if !coverage
    include("public_mul_main.jl")
    include("matmul_main.jl")
end


text:
module CompEconTests

using CompEcon

using Test

include("types.jl")

# correctness is checked in BasisMatrices. Here we just check that all
# functions run without error
b1 = fundef((:spli, range(0, stop = 1, length = 10), 0, 3))
b2 = fundef((:lin, range(0, stop = 1, length = 10), 0))
b3 = fundef((:cheb, 10, 0.0, 3.0))
b4 = fundefn(:spli, [10, 10], [-2.0, -1.0], [1.0, 2.0], 3)
b5 = fundefn(:lin, [10, 10], [-2.0, -1.0], [1.0, 2.0])
b6 = fundefn(:cheb, [10, 10], [-2.0, -1.0], [1.0, 2.0])
f1(X) = exp.(X)
f2(X) = sin.(X[:, 1] + X[:, 2])

i = 0
for b in (b1, b2, b3, b4, b5, b6)
    global i
    @show i += 1
    X, x = funnode(b)
    funbase(b)
    funbasex(b)

    f = b[:d] == 1 ? f1 : f2
    funfitf(b, f)
    y = f(X)
    c, B = funfitxy(b, X, y)
    funeval(c, b, x)
    funeval(c, b, X)
    funeval(c, b, B)
    funbconv(B, B[:order], :expanded)
end

end  # module


text:
save_results = false

@testset "Quadrotor" begin
# load expected results
res = load(joinpath(@__DIR__,"quadrotor.jld2"))

# Set up problem and solver
prob, opts = Problems.Quadrotor()
times = TO.gettimes(prob)
u0 = zeros(prob.model[1])[2]
U = [u0 + 0.1*SA[sin(t), sin(t), -sin(t), -sin(t)] for t in times]
initial_controls!(prob, U)
rollout!(prob)
states(prob)[end]

solver = ALTROSolver(prob, opts, save_S=true)
N = RD.dims(solver)[3]
n,m = RD.dims(prob.model[1])
ilqr = Altro.get_ilqr(solver)
Altro.initialize!(ilqr)
X = states(ilqr)
U = controls(ilqr)
@test X ≈ res["X"] atol=1e-6
@test U ≈ res["U"] atol=1e-6

# State diff Jacobian
Altro.errstate_jacobians!(ilqr.model, ilqr.G, ilqr.Z)
G = ilqr.G
@test G[1] != zeros(n,n)
@test G[N] != zeros(n,n)
@test G[1:N] ≈ res["G"][1:N] atol=1e-6

# Dynamics Jacobians
Altro.dynamics_expansion!(ilqr)
Altro.error_expansion!(ilqr.model, ilqr.D, ilqr.G)
A_ = [Matrix(D.A) for D in ilqr.D]
B_ = [Matrix(D.B) for D in ilqr.D]
A = [Matrix(D.fx) for D in ilqr.D]
B = [Matrix(D.fu) for D in ilqr.D]
@test A_ ≈ res["A_"] atol=1e-6
@test B_ ≈ res["B_"] atol=1e-6
@test A ≈ res["A"] atol=1e-6
@test B ≈ res["B"] atol=1e-6

# Unconstrained Cost Expansion
Altro.cost_expansion!(ilqr.obj.obj, ilqr.Efull, ilqr.Z)
Altro.error_expansion!(ilqr.model, ilqr.Eerr, ilqr.Efull, ilqr.G, ilqr.Z)
hess0 = [Array(E.hess) for E in ilqr.Eerr]
grad0 = [Array(E.grad) for E in ilqr.Eerr]
@test hess0 ≈ res["hess0"] atol=1e-6
@test grad0 ≈ res["grad0"] atol=1e-6

# AL Cost Expansion
cost(ilqr)  # evaluates the constraints
Altro.cost_expansion!(ilqr.obj, ilqr.Efull, ilqr.Z)
Altro.error_expansion!(ilqr.model, ilqr.Eerr, ilqr.Efull, ilqr.G, ilqr.Z)
hess = [Array(E.hess) for E in ilqr.Eerr]
grad = [Array(E.grad) for E in ilqr.Eerr]
@test hess ≈ res["hess"] atol=1e-6
@test grad ≈ res["grad"] atol=1e-6

# Backward Pass
ilqr.reg.ρ = ilqr.opts.bp_reg_min*0
ΔV = Altro.backwardpass!(ilqr)
K = Matrix.(ilqr.K)
d = Vector.(ilqr.d)
S = [Matrix(S.xx) for S in ilqr.S]
s = [Vector(S.x) for S in ilqr.S]
Qzz = [E.hess for E in ilqr.Q]
Qz = [E.grad for E in ilqr.Q]
@test norm(K - res["K"]) < 1e-6
@test norm(d - res["d"]) < 1e-5
@test S ≈ res["S"] atol=1e-6
@test s ≈ res["s"] atol=1e-6
@test Qzz ≈ res["Qzz"] atol=1e-6
@test Qz  ≈ res["Qz"] atol=1e-6
@test ΔV ≈ res["ΔV"] atol=1e-6

if save_results
    @save joinpath(@__DIR__,"quadrotor.jld2") X U G A B A_ B_ hess0 grad0 hess grad K d S s Qzz Qz ΔV
end

end


text:
using CUSPARSE
using CUDArt
using Base.Test

m = 25
n = 35
k = 10
blockdim = 5

@testset "util" begin
    x = sprand(m,n,0.2)
    d_x = CudaSparseMatrixCSC(x)
    @test device(x)   == -1
    @test length(d_x) == m*n
    @test size(d_x)   == (m,n)
    @test size(d_x,1) == m
    @test size(d_x,2) == n
    y = sprand(k,n,0.2)
    d_y = CudaSparseMatrixCSC(y)
    @test_throws ArgumentError copy!(d_y,d_x)
    d_y = CUSPARSE.switch2csr(d_y)
    d_x = CUSPARSE.switch2csr(d_x)
    @test_throws ArgumentError copy!(d_y,d_x)
    d_y = CUSPARSE.switch2bsr(d_y,convert(Cint,blockdim))
    d_x = CUSPARSE.switch2bsr(d_x,convert(Cint,blockdim))
    @test_throws ArgumentError copy!(d_y,d_x)
end

@testset "char" begin
    @test_throws ArgumentError CUSPARSE.cusparseop('Z')
    @test_throws ArgumentError CUSPARSE.cusparsetype('Z')
    @test_throws ArgumentError CUSPARSE.cusparsefill('Z')
    @test_throws ArgumentError CUSPARSE.cusparsediag('Z')
    @test_throws ArgumentError CUSPARSE.cusparsedir('Z')
    @test_throws ArgumentError CUSPARSE.cusparseindex('A')

    @test CUSPARSE.cusparseop('N') == CUSPARSE.CUSPARSE_OPERATION_NON_TRANSPOSE
    @test CUSPARSE.cusparseop('C') == CUSPARSE.CUSPARSE_OPERATION_CONJUGATE_TRANSPOSE
    @test CUSPARSE.cusparseop('T') == CUSPARSE.CUSPARSE_OPERATION_TRANSPOSE
    @test CUSPARSE.cusparsefill('U') == CUSPARSE.CUSPARSE_FILL_MODE_UPPER
    @test CUSPARSE.cusparsefill('L') == CUSPARSE.CUSPARSE_FILL_MODE_LOWER
    @test CUSPARSE.cusparsediag('U') == CUSPARSE.CUSPARSE_DIAG_TYPE_UNIT
    @test CUSPARSE.cusparsediag('N') == CUSPARSE.CUSPARSE_DIAG_TYPE_NON_UNIT
    @test CUSPARSE.cusparseindex('Z') == CUSPARSE.CUSPARSE_INDEX_BASE_ZERO
    @test CUSPARSE.cusparseindex('O') == CUSPARSE.CUSPARSE_INDEX_BASE_ONE
end

@testset "conversion" begin
    @testset for elty in [Float32, Float64, Complex64, Complex128]
        @testset "make_csc" begin
            x = sparse(rand(elty,m,n))
            d_x = CudaSparseMatrixCSC(x)
            h_x = to_host(d_x)
            @test h_x == x
            @test eltype(d_x) == elty
        end

        @testset "make_csr" begin
            x = sparse(rand(elty,m,n))
            d_x = CudaSparseMatrixCSR(x)
            h_x = to_host(d_x)
            @test h_x == x
        end

        @testset "convert_r2c" begin
            x = sparse(rand(elty,m,n))
            d_x = CudaSparseMatrixCSR(x)
            d_x = CUSPARSE.switch2csc(d_x)
            h_x = to_host(d_x)
            @test h_x.rowval == x.rowval
            @test h_x.nzval ≈ x.nzval
        end

        @testset "convert_r2b" begin
            x = sparse(rand(elty,m,n))
            d_x = CudaSparseMatrixCSR(x)
            d_x = CUSPARSE.switch2bsr(d_x,convert(Cint,blockdim))
            d_x = CUSPARSE.switch2csr(d_x)
            h_x = to_host(d_x)
            @test h_x ≈ x
        end

        @testset "convert_c2b" begin
            x = sparse(rand(elty,m,n))
            d_x = CudaSparseMatrixCSC(x)
            d_x = CUSPARSE.switch2bsr(d_x,convert(Cint,blockdim))
            d_x = CUSPARSE.switch2csc(d_x)
            h_x = to_host(d_x)
            @test h_x ≈ x
        end

        @testset "convert_c2h" begin
            x = sparse(rand(elty,m,n))
            d_x = CudaSparseMatrixCSC(x)
            d_x = CUSPARSE.switch2hyb(d_x)
            d_y = CUSPARSE.switch2csc(d_x)
            CUSPARSE.cusparseDestroyHybMat(d_x.Mat)
            h_x = to_host(d_y)
            @test h_x.rowval == x.rowval
            @test h_x.nzval ≈ x.nzval
        end

        @testset "convert_r2h" begin
            x = sparse(rand(elty,m,n))
            d_x = CudaSparseMatrixCSR(x)
            d_x = CUSPARSE.switch2hyb(d_x)
            d_y = CUSPARSE.switch2csr(d_x)
            CUSPARSE.cusparseDestroyHybMat(d_x.Mat)
            h_x = to_host(d_y)
            @test h_x.rowval == x.rowval
            @test h_x.nzval ≈ x.nzval
        end

        @testset "convert_d2h" begin
            x = rand(elty,m,n)
            d_x = CudaArray(x)
            d_x = CUSPARSE.sparse(d_x,'H')
            d_y = CUSPARSE.full(d_x)
            CUSPARSE.cusparseDestroyHybMat(d_x.Mat)
            h_x = to_host(d_y)
            @test h_x ≈ x
        end

        @testset "convert_d2b" begin
            x = rand(elty,m,n)
            d_x = CudaArray(x)
            d_x = CUSPARSE.sparse(d_x,'B')
            d_y = CUSPARSE.full(d_x)
            h_x = to_host(d_y)
            @test h_x ≈ x
        end

        @testset "convert_c2r" begin
            x = sparse(rand(elty,m,n))
            d_x = CudaSparseMatrixCSC(x)
            d_x = CUSPARSE.switch2csr(d_x)
            h_x = to_host(d_x)
            @test h_x.rowval == x.rowval
            @test h_x.nzval ≈ x.nzval
        end

        @testset "convert_r2d" begin
            x = sparse(rand(elty,m,n))
            d_x = CudaSparseMatrixCSR(x)
            d_x = CUSPARSE.full(d_x)
            h_x = to_host(d_x)
            @test h_x ≈ full(x)
        end

        @testset "convert_c2d" begin
            x = sparse(rand(elty,m,n))
            d_x = CudaSparseMatrixCSC(x)
            d_x = CUSPARSE.full(d_x)
            h_x = to_host(d_x)
            @test h_x ≈ full(x)
        end

        @testset "convert_d2c" begin
            x = rand(elty,m,n)
            d_x = CudaArray(x)
            d_x = CUSPARSE.sparse(d_x,'C')
            h_x = to_host(d_x)
            @test h_x ≈ sparse(x)
        end

        @testset "convert_d2r" begin
            x = rand(elty,m,n)
            d_x = CudaArray(x)
            d_x = CUSPARSE.sparse(d_x)
            h_x = to_host(d_x)
            @test h_x ≈ sparse(x)
        end
    end
end

testnames = ["axpyi","dot","gthr","roti",
             "sctr","bsrsv","hybsv",
             "mv","mm","cssm",
             "bsrsm","gemm","geam","csic",
             "cssv","csilu","bsric",
             "bsrilu","gtsv"]
chosentests = testnames
if( !isempty(ARGS) )
    chosentests = ARGS
end

for test in chosentests
    include("$test.jl")
end


text:
# This file contains functions for the Additive DEA model
"""
    AdditivelDEAModel
An data structure representing an additive DEA model.
"""
struct AdditiveDEAModel <: AbstractTechnicalDEAModel
    n::Int64
    m::Int64
    s::Int64
    rts::Symbol
    eff::Vector
    slackX::Matrix
    slackY::Matrix
    lambda::SparseMatrixCSC{Float64, Int64}
    weights::Symbol
end

"""
    deaadd(X, Y, model)
Compute related data envelopment analysis weighted additive models for inputs `X` and outputs `Y`.

Model specification:
- `:Ones`: standard additive DEA model.
- `:MIP`: Measure of Inefficiency Proportions. (Charnes et al., 1987; Cooper et al., 1999)
- `:Normalized`: Normalized weighted additive DEA model. (Lovell and Pastor, 1995)
- `:RAM`: Range Adjusted Measure. (Cooper et al., 1999)
- `:BAM`: Bounded Adjusted Measure. (Cooper et al, 2011)
- `:Custom`: User supplied weights.

# Optional Arguments
- `rts=:VRS`: chosse between constant returns to scale `:CRS` or variable returns to scale `:VRS`.
- `wX`: matrix of weights of inputs. Only if `model=:Custom`.
- `WY`: matrix of weights of outputs. Only if `model=:Custom`.
- `Xref=X`: Identifies the reference set of inputs against which the units are evaluated.
- `Yref=Y`: Identifies the reference set of outputs against which the units are evaluated.

# Examples
```jldoctest
julia> X = [5 13; 16 12; 16 26; 17 15; 18 14; 23 6; 25 10; 27 22; 37 14; 42 25; 5 17];

julia> Y = [12; 14; 25; 26; 8; 9; 27; 30; 31; 26; 12];

julia> deaadd(X, Y, :MIP)
Weighted Additive DEA Model
DMUs = 11; Inputs = 2; Outputs = 1
Weights = MIP; Returns to Scale = VRS
─────────────────────────────────────────────────────
      efficiency       slackX1  slackX2       slackY1
─────────────────────────────────────────────────────
1    0.0           0.0              0.0   0.0
2    0.507519      0.0              0.0   7.10526
3    0.0           0.0              0.0   0.0
4   -4.72586e-17  -8.03397e-16      0.0   0.0
5    2.20395       0.0              0.0  17.6316
6    1.31279e-16   8.10382e-16      0.0   8.64407e-16
7    0.0           0.0              0.0   0.0
8    0.0           0.0              0.0   0.0
9    0.0           0.0              0.0   0.0
10   1.04322      17.0             15.0   1.0
11   0.235294      0.0              4.0   0.0
─────────────────────────────────────────────────────
```
"""
function deaadd(X::Matrix, Y::Matrix, model::Symbol = :Ones; rts::Symbol = :VRS,
    wX::Matrix = Array{Float64}(undef, 0, 0), wY::Matrix = Array{Float64}(undef, 0, 0),
    Xref::Matrix = X, Yref::Matrix = Y)::AdditiveDEAModel

    # Check parameters
    nx, m = size(X)
    ny, s = size(Y)

    nrefx, mref = size(Xref)
    nrefy, sref = size(Yref)

    if nx != ny
        error("number of observations is different in inputs and outputs")
    end
    if nrefx != nrefy
        error("number of observations is different in inputs reference set and ouputs reference set")
    end
    if m != mref
        error("number of inputs in evaluation set and reference set is different")
    end
    if s != sref
        error("number of outputs in evaluation set and reference set is different")
    end

    # Get weights based on the selected model
    if model != :Custom
        wX, wY = deaaddweights(X, Y, model)
    end

    if size(wX) != size(X)
        error("size of weights matrix for inputs should be equal to size of inputs")
    end
    if size(wY) != size(Y)
        error("size of weights matrix for outputs should be qual to size of outputs")
    end

    # Parameters for additional condition in BAM model
    minXref = minimum(X, dims = 1)
    maxYref = maximum(Y, dims = 1)

    # Compute efficiency for each DMU
    n = nx
    nref = nrefx

    effi = zeros(n)
    slackX = zeros(n, m)
    slackY = zeros(n, s)
    lambdaeff = spzeros(n, nref)

    for i=1:n
        # Value of inputs and outputs to evaluate
        x0 = X[i,:]
        y0 = Y[i,:]

        # Value of weights to evaluate
        wX0 = wX[i,:]
        wY0 = wY[i,:]

        # Create the optimization model
        deamodel = Model(with_optimizer(GLPK.Optimizer))
        @variable(deamodel, sX[1:m] >= 0)
        @variable(deamodel, sY[1:s] >= 0)
        @variable(deamodel, lambda[1:nref] >= 0)

        @objective(deamodel, Max, sum(wX0[j] * sX[j] for j in 1:m) + sum(wY0[j] * sY[j] for j in 1:s) )
        @constraint(deamodel, [j in 1:m], sum(Xref[t,j] * lambda[t] for t in 1:nref) == x0[j] - sX[j])
        @constraint(deamodel, [j in 1:s], sum(Yref[t,j] * lambda[t] for t in 1:nref) == y0[j] + sY[j])

        # Add return to scale constraints
        if rts == :CRS
            # Add constraints for BAM CRS model
            if model == :BAM
                @constraint(deamodel, [j in 1:m], sum(Xref[t,j] * lambda[t] for t in 1:nref) >= minXref[j])
                @constraint(deamodel, [j in 1:s], sum(Yref[t,j] * lambda[t] for t in 1:nref) <= maxYref[j])
            end
        elseif rts == :VRS
            @constraint(deamodel, sum(lambda) == 1)
        else
            error("Invalid returns to scale $rts. Returns to scale should be :CRS or :VRS")
        end

        # Optimize and return results
        JuMP.optimize!(deamodel)

        effi[i]  = JuMP.objective_value(deamodel)
        lambdaeff[i,:] = JuMP.value.(lambda)
        slackX[i,:] = JuMP.value.(sX)
        slackY[i,:] = JuMP.value.(sY)

    end

    return AdditiveDEAModel(n, m, s, rts, effi, slackX, slackY, lambdaeff, model)

end

function deaadd(X::Vector, Y::Matrix, model::Symbol = :Ones; rts::Symbol = :VRS,
    wX::Vector = Array{Float64}(undef, 0), wY::Matrix = Array{Float64}(undef, 0, 0),
    Xref::Vector = X, Yref::Matrix = Y)::AdditiveDEAModel

    X = X[:,:]
    wX = wX[:,:]
    Xref = Xref[:,:]
    return deaadd(X, Y, model, rts = rts, wX = wX, wY = wY, Xref = Xref, Yref = Yref)
end

function deaadd(X::Matrix, Y::Vector, model::Symbol = :Ones; rts::Symbol = :VRS,
    wX::Matrix = Array{Float64}(undef, 0, 0), wY::Vector = Array{Float64}(undef, 0),
    Xref::Matrix = X, Yref::Vector = Y)::AdditiveDEAModel

    Y = Y[:,:]
    wY = wY[:,:]
    Yref = Yref[:,:]
    return deaadd(X, Y, model, rts = rts, wX = wX, wY = wY, Xref = Xref, Yref = Yref)
end

function deaadd(X::Vector, Y::Vector, model::Symbol = :Ones; rts::Symbol = :VRS,
    wX::Vector = Array{Float64}(undef, 0), wY::Vector = Array{Float64}(undef, 0),
    Xref::Vector = X, Yref::Vector = Y)::AdditiveDEAModel

    X = X[:,:]
    wX = wX[:,:]
    Xref = Xref[:,:]
    Y = Y[:,:]
    wY = wY[:,:]
    Yref = Yref[:,:]
    return deaadd(X, Y, model, rts = rts, wX = wX, wY = wY, Xref = Xref, Yref = Yref)
end

function Base.show(io::IO, x::AdditiveDEAModel)
    compact = get(io, :compact, false)

    n = nobs(x)
    m = ninputs(x)
    s = noutputs(x)
    eff = efficiency(x)
    slackX = slacks(x, :X)
    slackY = slacks(x, :Y)

    if !compact
        print(io, "Weighted Additive DEA Model \n")
        print(io, "DMUs = ", n)
        print(io, "; Inputs = ", m)
        print(io, "; Outputs = ", s)
        print(io, "\n")
        print(io, "Weights = ", string(x.weights))
        print(io, "; Returns to Scale = ", string(x.rts))
        print(io, "\n")
        show(io, CoefTable(hcat(eff, slackX, slackY), ["efficiency"; ["slackX$i" for i in 1:m ]; ; ["slackY$i" for i in 1:s ]], ["$i" for i in 1:n]))
    end

end

"""
    deaaddweights(X, Y, model)
Compute corresponding weights for related data envelopment analysis weighted additive models for inputs `X` and outputs `Y`.

Model specification:
- `:Ones`: standard additive DEA model.
- `:MIP`: Measure of Inefficiency Proportions. (Charnes et al., 1987; Cooper et al., 1999)
- `:Normalized`: Normalized weighted additive DEA model. (Lovell and Pastor, 1995)
- `:RAM`: Range Adjusted Measure. (Cooper et al., 1999)
- `:BAM`: Bounded Adjusted Measure. (Cooper et al, 2011)
"""
function deaaddweights(X::Matrix, Y::Matrix, model::Symbol)

    # Compute specific weights based on the model
    if model == :Ones
        # Standard Additive DEA model
        wX = ones(size(X))
        wY = ones(size(Y))
    elseif model == :MIP
        # Measure of Inefficiency Proportions
        wX = 1 ./ X
        wY = 1 ./ Y
    elseif model == :Normalized
        # Normalized weighted additive DEA model
        wX = zeros(size(X))
        wY = zeros(size(Y))

        m = size(X, 2)
        s = size(Y, 2)

        for i=1:m
            wX[:,i] .= 1 ./ std(X[:,i])
        end
        for i=1:s
            wY[:,i] .= 1 ./ std(Y[:,i])
        end

        wX[isinf.(wX)] .= 0
        wY[isinf.(wY)] .= 0

    elseif model == :RAM
        # Range Adjusted Measure
        m = size(X, 2)
        s = size(Y, 2)

        wX = zeros(size(X))
        wY = zeros(size(Y))

        for i=1:m
            wX[:,i] .= 1 ./ ((m + s) * (maximum(X[:,i])  - minimum(X[:,i])))
        end
        for i=1:s
            wY[:,i] .= 1 ./ ((m + s) * (maximum(Y[:,i])  - minimum(Y[:,i])))
        end

        wX[isinf.(wX)] .= 0
        wY[isinf.(wY)] .= 0

    elseif model == :BAM
        # Bounded Adjusted Measure
        m = size(X, 2)
        s = size(Y, 2)

        minX = zeros(m)
        maxY = zeros(s)

        wX = zeros(size(X))
        wY = zeros(size(Y))

        for i=1:m
            minX[i] = minimum(X[:,i])
            wX[:,i] = 1 ./ ((m  + s) .* (X[:,i] .- minX[i] ))
        end
        for i=1:s
            maxY[i] = maximum(Y[:,i])
            wY[:,i] = 1 ./ ((m + s) .* (maxY[i] .- Y[:,i]))
        end

        wX[isinf.(wX)] .= 0
        wY[isinf.(wY)] .= 0

    else
        error("Invalid model ", model)
    end

    return wX, wY

end


text:
# ---
# title: 863. All Nodes Distance K in Binary Tree
# id: problem863
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories: Tree, Depth-first Search, Breadth-first Search
# link: <https://leetcode.com/problems/all-nodes-distance-k-in-binary-tree/description/>
# hidden: true
# ---
#
# We are given a binary tree (with root node `root`), a `target` node, and an
# integer value `K`.
#
# Return a list of the values of all nodes that have a distance `K` from the
# `target` node.  The answer can be returned in any order.
#
#
#
# **Example 1:**
#
#
#
#     Input: root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, K = 2
#
#     Output: [7,4,1]
#
#     Explanation:
#     The nodes that are a distance 2 from the target node (with value 5)
#     have values 7, 4, and 1.
#
#     ![](https://s3-lc-upload.s3.amazonaws.com/uploads/2018/06/28/sketch0.png)
#
#     Note that the inputs "root" and "target" are actually TreeNodes.
#     The descriptions of the inputs above are just serializations of these objects.
#
#
#
#
# **Note:**
#
#   1. The given tree is non-empty.
#   2. Each node in the tree has unique values `0 <= node.val <= 500`.
#   3. The `target` node is a node in the tree.
#   4. `0 <= K <= 1000`.
#
#
## @lc code=start
using LeetCode

## add your code here:
## @lc code=end


text:
"""
    TestUtils

Some commom test utilities for portfolio optimization formulations.

Mainly:
    - `get_test_data` that returns a TimeArray of prices for 6 assets.
"""
module TestUtils
    using Distributions
    using JuMP
    using ..PortfolioOpt: MarketHistory, market_template, past_prices, clear_market!, current_prices, total_profit
    using LinearAlgebra
    import Reexport
    using MarketData
    import Base.keys

    include("auxilary_functions.jl")

    export get_test_data,
        mean_variance,
        keys

    Reexport.@reexport using MarketData
end

max_stars_repo_name: andrewrosemberg/PortfolioOpt

max_issues_repo_name: andrewrosemberg/PortfolioOpt

max_forks_repo_name: andrewrosemberg/PortfolioOpt